Date: 2026-04-20
Environment: WSL2 (Ubuntu, kernel 6.6.87.2-microsoft-standard-WSL2), Cisco AnyConnect VPN, networkingMode=mirrored
Symptom
Any attempt to connect to SQL Server from WSL2 via pyodbc fails with:
[HYT00] Login timeout expired (SQLDriverConnect)
after several seconds (multiple connection attempts × ODBC login timeout + retries).
For example:
import pyodbc
conn = pyodbc.connect(
"DRIVER=ODBC Driver 17 for SQL Server;"
"SERVER=myserver.example.local;"
"DATABASE=mydb;"
"Trusted_Connection=yes;"
)
The same connection from a native Windows process (e.g. sqlcmd) completes in ~1 second.
Root Cause
The Microsoft ODBC Driver 17 and 18 for SQL Server (Linux) call bind(sock, 0.0.0.0:0) on
a freshly created TCP socket before calling connect(). This is a redundant but
standard pre-connection step — binding to the wildcard address on port 0 asks the
kernel to assign an ephemeral source port, which connect() would do automatically
anyway if bind() is skipped.
On this system, that bind() call returns ENOSYS ("Function not implemented"):
bind(3, {sa_family=AF_INET, sin_port=0, sin_addr=0.0.0.0}) = -1 ENOSYS
Confirmed via strace. Also reproducible directly:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(False)
s.bind(('0.0.0.0', 0))
# → OSError: [Errno 38] Function not implemented
When bind() fails, the ODBC driver closes the socket and retries with exponential
backoff (100 ms → 200 ms → 400 ms → 800 ms → 1 s intervals) until the login timeout
expires. TCP connectivity to the server is otherwise fine — connect() without a
prior bind() reaches the SQL Server almost immediately.
Why does bind() fail?
In WSL2 networkingMode=mirrored, WSL processes share the Windows host's network
stack directly. Cisco AnyConnect installs a kernel-level socket filter that,
in this configuration, blocks bind() on AF_INET sockets.
The failure is consistent whenever AnyConnect is active in mirrored mode,
regardless of the underlying physical network (WiFi or ethernet). The observed
pattern — connections succeeding 1–2 times after a fresh WSL session before all
subsequent attempts fail — is consistent with
AnyConnect's socket filter initializing fully a few seconds after VPN connection
is established. Once the filter is active, bind() fails for every subsequent call
and accumulated socket errors eventually degrade WSL's networking state.
Switching WSL2 to networkingMode=nat restores bind() but breaks internet
access entirely under AnyConnect, so that is not a viable workaround.
Fix
An LD_PRELOAD shim that intercepts bind(AF_INET, 0.0.0.0:0) and returns
success without calling the kernel. All other bind() calls (any non-zero
address or port) are passed through to the real implementation unchanged.
Source (fix_bind.c)
#define _GNU_SOURCE
#include <dlfcn.h>
#include <sys/socket.h>
#include <netinet/in.h>
int bind(int fd, const struct sockaddr *addr, socklen_t len) {
if (addr && addr->sa_family == AF_INET) {
const struct sockaddr_in *a = (const struct sockaddr_in *)addr;
if (a->sin_addr.s_addr == 0 && a->sin_port == 0)
return 0; /* redundant bind — connect() auto-assigns source port */
}
static int (*real_bind)(int, const struct sockaddr *, socklen_t) = NULL;
if (!real_bind) real_bind = dlsym(RTLD_NEXT, "bind");
return real_bind(fd, addr, len);
}
Build
gcc -shared -fPIC -o /usr/local/lib/fix_bind.so fix_bind.c -ldl
Install (system-wide, survives WSL restart)
sudo bash -c 'echo "/usr/local/lib/fix_bind.so" >> /etc/ld.so.preload'
Result
With the shim in place, pyodbc.connect() completes almost immediately instead of
timing out after several seconds.
Safety
The shim intercepts only bind(AF_INET, 0.0.0.0, port=0). This call is
semantically a no-op — skipping it has exactly the same observable effect as
letting it succeed, because connect() auto-assigns a source address and port
in both cases. No other bind() call is affected.
Notes
If others hit this issue in WSL2 with AnyConnect and networkingMode=mirrored,
the shim above is the practical workaround. A cleaner fix would be for the
Microsoft ODBC Driver to make the pre-connection bind() call conditional on
the platform — skipping it on Linux when the address is wildcard, since
connect() handles source port assignment automatically.
Root cause diagnosed with assistance from Claude Code (Anthropic).
Date: 2026-04-20
Environment: WSL2 (Ubuntu, kernel 6.6.87.2-microsoft-standard-WSL2), Cisco AnyConnect VPN,
networkingMode=mirroredSymptom
Any attempt to connect to SQL Server from WSL2 via pyodbc fails with:
after several seconds (multiple connection attempts × ODBC login timeout + retries).
For example:
The same connection from a native Windows process (e.g.
sqlcmd) completes in ~1 second.Root Cause
The Microsoft ODBC Driver 17 and 18 for SQL Server (Linux) call
bind(sock, 0.0.0.0:0)ona freshly created TCP socket before calling
connect(). This is a redundant butstandard pre-connection step — binding to the wildcard address on port 0 asks the
kernel to assign an ephemeral source port, which
connect()would do automaticallyanyway if
bind()is skipped.On this system, that
bind()call returns ENOSYS ("Function not implemented"):Confirmed via
strace. Also reproducible directly:When
bind()fails, the ODBC driver closes the socket and retries with exponentialbackoff (100 ms → 200 ms → 400 ms → 800 ms → 1 s intervals) until the login timeout
expires. TCP connectivity to the server is otherwise fine —
connect()without aprior
bind()reaches the SQL Server almost immediately.Why does bind() fail?
In WSL2
networkingMode=mirrored, WSL processes share the Windows host's networkstack directly. Cisco AnyConnect installs a kernel-level socket filter that,
in this configuration, blocks
bind()on AF_INET sockets.The failure is consistent whenever AnyConnect is active in mirrored mode,
regardless of the underlying physical network (WiFi or ethernet). The observed
pattern — connections succeeding 1–2 times after a fresh WSL session before all
subsequent attempts fail — is consistent with
AnyConnect's socket filter initializing fully a few seconds after VPN connection
is established. Once the filter is active, bind() fails for every subsequent call
and accumulated socket errors eventually degrade WSL's networking state.
Switching WSL2 to
networkingMode=natrestoresbind()but breaks internetaccess entirely under AnyConnect, so that is not a viable workaround.
Fix
An LD_PRELOAD shim that intercepts
bind(AF_INET, 0.0.0.0:0)and returnssuccess without calling the kernel. All other
bind()calls (any non-zeroaddress or port) are passed through to the real implementation unchanged.
Source (
fix_bind.c)Build
Install (system-wide, survives WSL restart)
sudo bash -c 'echo "/usr/local/lib/fix_bind.so" >> /etc/ld.so.preload'Result
With the shim in place,
pyodbc.connect()completes almost immediately instead oftiming out after several seconds.
Safety
The shim intercepts only
bind(AF_INET, 0.0.0.0, port=0). This call issemantically a no-op — skipping it has exactly the same observable effect as
letting it succeed, because
connect()auto-assigns a source address and portin both cases. No other
bind()call is affected.Notes
If others hit this issue in WSL2 with AnyConnect and
networkingMode=mirrored,the shim above is the practical workaround. A cleaner fix would be for the
Microsoft ODBC Driver to make the pre-connection
bind()call conditional onthe platform — skipping it on Linux when the address is wildcard, since
connect()handles source port assignment automatically.Root cause diagnosed with assistance from Claude Code (Anthropic).