diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index fd92c0d71783..7e31bd9c5e3a 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -52,23 +52,15 @@ jobs: uv run ./configure --disable-valgrind --disable-compat uv run gmake uv run gmake check-gen-updated + sudo gmake install - - name: Start bitcoind in regtest mode + - name: Run pytest + env: + SLOW_MACHINE: 1 + PYTEST_OPTS: "-vvv --timeout=1800 --durations=10" + PYTEST_TESTS: | + tests/test_misc.py::test_ipv4_and_ipv6 + tests/test_connection.py::test_websocket + tests/test_connection.py::test_wss_proxy run: | - bitcoind -regtest -daemon - sleep 5 - - - name: Generate initial block - run: | - bitcoin-cli -regtest createwallet default_wallet - bitcoin-cli -regtest generatetoaddress 1 $(bitcoin-cli -regtest getnewaddress) - sleep 2 - - - name: Start CLN in regtest mode - run: | - lightningd/lightningd --network=regtest --log-file=/tmp/l1.log --daemon - sleep 5 - - - name: Verify CLN is running - run: | - cli/lightning-cli --regtest getinfo + VALGRIND=0 uv run pytest $PYTEST_TESTS -n $(sysctl -n hw.ncpu) ${PYTEST_OPTS} diff --git a/connectd/connectd.c b/connectd/connectd.c index 61759f7a61f2..cb95ef9b0cac 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1301,6 +1301,21 @@ static struct listen_fd *make_listen_fd(const tal_t *ctx, status_unusual("Failed setting socket reuse: %s", strerror(errno)); +#ifdef IPV6_V6ONLY + /* Most Linux distros (Debian, Ubuntu) ship net.ipv6.bindv6only=1 in + * sysctl, making IPv6 sockets IPv6-only by default, so a separate IPv4 + * wildcard socket can also bind. macOS, Fedora, Arch and vanilla + * kernels default to 0 (dual-stack): binding '::' also covers + * '0.0.0.0', and the subsequent IPv4 bind fails with EADDRINUSE. + * Explicitly set IPV6_V6ONLY=1 so both sockets always bind + * independently, regardless of the system sysctl. */ + if (domain == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) + status_unusual("Failed setting IPV6_V6ONLY: %s", + strerror(errno)); + } +#endif + if (bind(fd, addr, len) != 0) { const char *es = strerror(errno); *errstr = tal_fmt(ctx, "Failed to bind socket for %s%s: %s", @@ -1535,6 +1550,9 @@ setup_listeners(const tal_t *ctx, } else if (!ipv6_ok) { /* Both failed, return now, errstr set. */ return NULL; + } else { + /* IPv4 failed, but IPv6 (dual-stack) succeeded: discard errstr. */ + *errstr = tal_free(*errstr); } continue; } diff --git a/tests/test_misc.py b/tests/test_misc.py index 91af3d7d7076..b33704536b4d 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1842,10 +1842,10 @@ def test_ipv4_and_ipv6(node_factory): assert bind[1]['address'] == '0.0.0.0' assert int(bind[1]['port']) == port else: - # Assume we're IPv4 only... + # Either IPv4-only, or IPv6 dual-stack (covers IPv4 too, so no separate IPv4 socket) assert len(bind) == 1 - assert bind[0]['type'] == 'ipv4' - assert bind[0]['address'] == '0.0.0.0' + assert bind[0]['type'] in ('ipv4', 'ipv6') + assert bind[0]['address'] in ('0.0.0.0', '::') assert int(bind[0]['port']) == port