<div dir="auto">Hi all,<div dir="auto"><br></div><div dir="auto">My guess is that it could depend on socket type. I.e. nonblocking tcp socket in Linux might always return EINPROGRESS, but for unix socket other situations might be possible.</div><div dir="auto"><br></div><div dir="auto">Regards,</div><div dir="auto">Alexander</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Fri, Oct 17, 2025, 22:02 Ze Xia <<a href="mailto:billxia135@gmail.com">billxia135@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Fri, Oct 17, 2025 at 10:57 PM Maria Matejka <<a href="mailto:maria.matejka@nic.cz" target="_blank" rel="noreferrer">maria.matejka@nic.cz</a>> wrote:<br>
><br>
> Hello Ze Xia,<br>
><br>
> this looks like a real bug, yet I'm not sure whether we happen to observe it in real world often. Please, do you have any instructions how to trigger it reliably so that we can add it to our CI?<br>
><br>
> Thanks,<br>
> Maria<br>
><br>
<br>
I tried to trigger it "naturally" by creating 2 bird daemons connected<br>
through veth-pair, this fails to reproduce the bug. According to<br>
strace, connect() always returns -1 with errno = EINPROGRESS.<br>
<br>
However, I figured out that I can wait a little while for connect() to<br>
success by preloading a custom dynamically-linked library. My current<br>
implementation:<br>
<br>
#include <dlfcn.h><br>
#include <errno.h><br>
#include <poll.h><br>
#include <sys/socket.h><br>
<br>
// milliseconds<br>
#define MAX_CONNECT_BLOCKTIME 10<br>
<br>
typedef int (*connect_t)(int, const struct sockaddr *, socklen_t);<br>
<br>
__attribute__((visibility("default"))) int connect(int sock, const<br>
struct sockaddr *addr, socklen_t len)<br>
{<br>
    int orig_errno = errno;<br>
<br>
    connect_t true_connect = dlsym(RTLD_NEXT, "connect");<br>
    int r = true_connect(sock, addr, len);<br>
    if (!(addr->sa_family == AF_INET && r == -1 && errno == EINPROGRESS))<br>
        return r;<br>
<br>
    struct pollfd fds[1] = {{.fd = sock,<br>
                             .events = POLLOUT | POLLERR | POLLHUP}};<br>
    int poll_res = poll(fds, 1, MAX_CONNECT_BLOCKTIME);<br>
    if (poll_res == 0)<br>
    {<br>
        errno = EINPROGRESS;<br>
        return -1;<br>
    }<br>
    int err;<br>
    socklen_t errlen = sizeof(err);<br>
    getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);<br>
    if (err == 0)<br>
    {<br>
        errno = orig_errno;<br>
        return 0;<br>
    }<br>
    else<br>
    {<br>
        errno = err;<br>
        return -1;<br>
    }<br>
}<br>
<br>
Compile it with:<br>
<br>
gcc bird-preload.c -fPIC -fvisibility=hidden -shared -o libpreload.so<br>
<br>
Then write the absolute path of libpreload.so to /etc/ld.so.preload<br>
(man ld.so for more information about LD_PRELOAD). I started 2 bird<br>
daemons inside a docker container with config file as in attachment of<br>
this mail, and connected them with veth-pair. When this libpreload.so<br>
is preloaded, the connect retry timer (2s) should fire every time and<br>
tears down the connection, causing a reconnection, which can be<br>
checked in the debug log.<br>
<br>
With the libpreload.so, bird should behave just like the thread does<br>
not get scheduled for a while (<10ms) when calling connect(), it seems<br>
to have no other side-effect to me. I'm not sure does this fits in<br>
your CI workflow though. Hope this helps!<br>
<br>
Regards,<br>
Ze Xia<br>
</blockquote></div>