net: use -bind address for outgoing connections #35027

pull 8144225309 wants to merge 3 commits into bitcoin:master from 8144225309:net-bind-outgoing changing 10 files +175 −4
  1. 8144225309 commented at 9:48 PM on April 7, 2026: none

    Closes #6476

    -bind only affected the listening socket. Outgoing connections used whatever source address the OS chose, so on multi-homed nodes traffic could originate from an unintended interface.

    Store one -bind address per address family and bind() the socket before connect() for direct connections. Proxied and CJDNS connections are unaffected. Loopback bind addresses are skipped.

    A functional test verifies the source IP seen by the receiving node matches the -bind address.

    Based on vasild's approach in #6476 (comment).

  2. DrahtBot added the label P2P on Apr 7, 2026
  3. DrahtBot commented at 9:49 PM on April 7, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Approach ACK frankomosh

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34839 (doc: remove unshipped doc references from help and warning text by AgusR7)
    • #34538 (net: advertise -externalip addresses by willcl-ark)
    • #31974 (Drop testnet3 by Sjors)
    • #17783 (common: Disallow calling IsArgSet() on ALLOW_LIST options by ryanofsky)
    • #17581 (refactor: Remove settings merge reverse precedence code by ryanofsky)
    • #17580 (refactor: Add ALLOW_LIST flags and enforce usage in CheckArgFlags by ryanofsky)
    • #17493 (util: Forbid ambiguous multiple assignments in config file by ryanofsky)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. 8144225309 force-pushed on Apr 7, 2026
  5. 8144225309 force-pushed on Apr 7, 2026
  6. luke-jr commented at 5:12 PM on April 9, 2026: member

    This might break a scenario where someone wants to listen on one IP, but load balance outgoing connections. A new option might be better?

  7. 8144225309 commented at 6:38 PM on April 9, 2026: none

    The original issue asks for -bind to control outgoing connections, and users who want listen-only can omit -bind. Outbound is capped at 11 (8 full relay + 2 block-only + 1 feeler) so there isn't much to load balance. A separate option could work but users already expect -bind to cover both directions.

  8. DrahtBot added the label Needs rebase on Apr 9, 2026
  9. net: use -bind address for outgoing connections
    Previously -bind only affected the listening socket. Outgoing
    connections used whatever source address the OS selected, which on
    multi-homed nodes meant traffic could originate from an unintended
    interface.
    
    Only clearnet (IPv4/IPv6) direct connections are bound. Proxied
    connections (Tor, I2P, SOCKS5) are unaffected since the proxy
    determines the source address. CJDNS connections without a proxy
    are also excluded to avoid binding a regular address to a socket
    routed through the CJDNS tun device. Local (loopback) bind addresses
    are skipped so that -bind=127.0.0.1 is not affected.
    
    Closes #6476
    ae9dc431de
  10. test: add tests for outbound bind address selection
    Unit test verifying that CConnman::Init() stores the first non-local
    -bind address per address family, skips loopback addresses, and
    confirms that CJDNS addresses are neither IPv4 nor IPv6 (ensuring
    the outbound bind selection in ConnectNode correctly skips them).
    
    Functional test (feature_bind_outgoing.py) verifying that outbound
    connections originate from the -bind address. Requires two routable
    IPs on the machine (skipped otherwise).
    f91b44711d
  11. doc: add release notes for outbound bind 13879e1dd0
  12. 8144225309 force-pushed on Apr 10, 2026
  13. DrahtBot removed the label Needs rebase on Apr 10, 2026
  14. in src/net.h:1136 in 13879e1dd0
    1130 | @@ -1131,6 +1131,15 @@ class CConnman
    1131 |              }
    1132 |          }
    1133 |          m_onion_binds = connOptions.onion_binds;
    1134 | +        // Use -bind addresses for outgoing connections (one per address family).
    1135 | +        for (const auto& bind_addr : connOptions.vBinds) {
    1136 | +            if (bind_addr.IsLocal()) continue;
    


    frankomosh commented at 8:54 AM on April 14, 2026:

    IsLocal() also matches 0.0.0.0/8, not just loopback. The PR description says "loopback addresses are skipped", but the actual skip is broader. Worth a comment somewhere, clarifying that?

  15. in src/net.h:1137 in 13879e1dd0
    1130 | @@ -1131,6 +1131,15 @@ class CConnman
    1131 |              }
    1132 |          }
    1133 |          m_onion_binds = connOptions.onion_binds;
    1134 | +        // Use -bind addresses for outgoing connections (one per address family).
    1135 | +        for (const auto& bind_addr : connOptions.vBinds) {
    1136 | +            if (bind_addr.IsLocal()) continue;
    1137 | +            if (bind_addr.IsIPv4() && !m_outbound_bind_v4) {
    


    frankomosh commented at 9:09 AM on April 14, 2026:

    The listen path validates the address early through BindListenPort and surfaces errors at startup. Here we store it without checking if it's actually assigned to a local interface. Would it make sense to do a trial bind() or similar check here so a misconfigured address fails loudly at init rather than silently killing outbound for that family.

  16. frankomosh commented at 9:16 AM on April 14, 2026: contributor

    Approach ACK.

    Left some inline comments

  17. in src/test/net_tests.cpp:1610 in 13879e1dd0
    1605 | +
    1606 | +    // First non-local address per family is used; second is ignored.
    1607 | +    options.vBinds = {addr1, addr2};
    1608 | +    connman->Init(options);
    1609 | +    BOOST_REQUIRE(connman->GetOutboundBindV4());
    1610 | +    BOOST_CHECK_EQUAL(connman->GetOutboundBindV4()->ToStringAddr(), "203.0.113.1");
    


    frankomosh commented at 1:01 PM on April 14, 2026:

    The test verifies that the first IPv4 address is stored, but doesn't check that m_outbound_bind_v6 stays empty. If the else if condition were weakened, an IPv4 address could leak into the IPv6 slot undetected. You can avoid this by including BOOST_CHECK(!connman->GetOutboundBindV6()); around here.

  18. in src/netbase.cpp:662 in 13879e1dd0
     658 | +        socklen_t bind_len = sizeof(bind_sa);
     659 | +        if (!bind_service.GetSockAddr(reinterpret_cast<struct sockaddr*>(&bind_sa), &bind_len)) {
     660 | +            LogError("Cannot get sockaddr for bind address %s\n", bind_addr->ToStringAddr());
     661 | +            return {};
     662 | +        }
     663 | +        if (sock->Bind(reinterpret_cast<struct sockaddr*>(&bind_sa), bind_len) == SOCKET_ERROR) {
    


    frankomosh commented at 1:13 PM on April 14, 2026:

    I believe it would it be great to add a unit test that calls ConnectDirectly with a bind address, even if just to verify the syscall is attempted. Right now the unit test covers Init-time storage but this Bind() path has no automated coverage.

  19. gmaxwell commented at 2:38 AM on April 25, 2026: contributor

    The original issue asks for -bind to control outgoing connections, and users who want listen-only can omit -bind.

    Just because someone asked that way doesn't mean it's the best solution. I believe that as proposed here -bind=mypublicinterface would suddenly make my node unable to connect to stuff on my local lan (or, in VM on bridge interfaces)-- pretty surprising.

    I don't see any particular harm in making a separate obind to control output interfaces-- otherwise the OS's routing table makes binding decisions.

  20. 8144225309 commented at 3:29 PM on April 25, 2026: none

    On the LAN/bridge case, that's a real regression for multi-homed setups (VM bridges especially). A separate outbound-only option avoids it without forcing existing -bind users to revisit their configs.

    I'd lean towards -bindoutgoing for clarity and alphabetic proximity to -bind in docs/tab-completion. That said, -outboundbind (or -obind) is arguably the better fit given the existing -whitebind/-rpcbind convention. Any preference?


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-02 12:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me