<div dir="ltr"><div>Hi David</div><div><br></div><div>IP_BIND_ADDRESS_NO_PORT did not fix your somewhat similar problem in your Haproxy setup, because all the connections are to the same dst tuple <ip, port> (i.e 127.0.0.1:ExtORPort).</div><div>The connect() system call is looking for a unique 5-tuple <protocol, srcip, srcport, dstip, dstport>. In the Haproxy setup, the only free variable is srcport <tcp, 127.0.0.1, srcport, 127.0.0.1, ExtORPort>, so toggling IP_BIND_ADDRESS_NO_PORT makes no difference.</div><div><br></div><div>The following should help (unless found a bug in Linux):<br><ol><li>Let tor listen on a bunch of different ExtORPort</li><li>Let tor listen on a bunch of ips for the ExtORPort (so we have #ExtORPort * #ExtOrPortListenIPs unique combinations)<br></li><li>Connect from different src ips (what you already implemented)</li><li>sysctl -w net.ipv4.ip_local_port_range="1024 65535"</li></ol></div><div>For 1 and 2 to make a difference, if you do a 3 (i.e bind before connect), you need IP_BIND_ADDRESS_NO_PORT enabled on the socket.</div><div><br></div><div>Tor relays already connect to many different dstip:dstport pairs, so enabling IP_BIND_ADDRESS_NO_PORT should solve our problem.</div><div><br></div><div>I rest my case ;)<br></div><div><br></div><div>Best regards</div><div>Anders Trier Olesen<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Dec 10, 2022 at 5:41 AM David Fifield <<a href="mailto:david@bamsoftware.com">david@bamsoftware.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, Dec 09, 2022 at 09:47:07AM +0000, Alexander Færøy wrote:<br>
> On 2022/12/01 20:35, Christopher Sheats wrote:<br>
> > Does anyone have experience troubleshooting and/or fixing this problem?<br>
> <br>
> Like I wrote in [1], I think it would be interesting to hear if the<br>
> patch from pseudonymisaTor in ticket #26646[2] would be of any help in<br>
> the given situation. The patch allows an exit operator to specify a<br>
> range of IP addresses for binding purposes for outbound connections. I<br>
> would think this could split the load wasted on trying to resolve port<br>
> conflicts in the kernel amongst the set of IP's you have available for<br>
> outbound connections.<br>
<br>
This sounds similar to a problem we faced with the main Snowflake<br>
bridge. After usage passed a certain threshold, we started getting<br>
constant EADDRNOTAVAIL, not on the outgoing connections to middle nodes,<br>
but on the many localhost TCP connections used by the pluggable<br>
transports model.<br>
<br>
<a href="https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40198" rel="noreferrer" target="_blank">https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40198</a><br>
<a href="https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40201" rel="noreferrer" target="_blank">https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40201</a><br>
<br>
Long story short, the only mitigation that worked for us was to bind<br>
sockets to an address (with port number unspecified, and with<br>
IP_BIND_ADDRESS_NO_PORT *unset*) before connecting them, and use<br>
different <a href="http://127.0.0.0/8" rel="noreferrer" target="_blank">127.0.0.0/8</a> addresses or ranges of addresses in different<br>
segments of the communication chain.<br>
<br>
<a href="https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/120" rel="noreferrer" target="_blank">https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/120</a><br>
<a href="https://gitlab.torproject.org/dcf/extor-static-cookie/-/commit/a5c7a038a71aec1ff78d1b15888f1c75b66639cd" rel="noreferrer" target="_blank">https://gitlab.torproject.org/dcf/extor-static-cookie/-/commit/a5c7a038a71aec1ff78d1b15888f1c75b66639cd</a><br>
<br>
IP_BIND_ADDRESS_NO_PORT was mentioned in another part of the thread<br>
(<a href="https://lists.torproject.org/pipermail/tor-relays/2022-December/020895.html" rel="noreferrer" target="_blank">https://lists.torproject.org/pipermail/tor-relays/2022-December/020895.html</a>).<br>
For us, this bind option *did not help* and in fact we had to apply a<br>
workaround for Haproxy, which has IP_BIND_ADDRESS_NO_PORT hardcoded.<br>
*Why* that should be the case is a mystery to me, as is why it is true<br>
that bind-before-connect avoids EADDRNOTAVAIL even when the address<br>
manually bound to is the very same address the kernel would have<br>
automatically assigned. I even spent some time reading the Linux 5.10<br>
source code trying to make sense of it. In the source code I found, or<br>
at least think I found, code paths for the behvior I observed; but the<br>
behavior seems to go against how bind and IP_BIND_ADDRESS_NO_PORT are<br>
documented to work.<br>
<br>
<a href="https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40201#note_2839472" rel="noreferrer" target="_blank">https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/40201#note_2839472</a><br>
<br>
> Although my understanding of what Linux is doing is very imperfect, my<br>
> understanding is that both of these questions have the same answer:<br>
> port number assignment in `connect` when called on a socket not yet<br>
> bound to a port works differently than in `bind` when called with a<br>
> port number of 0. In case (1), the socket is not bound to a port<br>
> because you haven't even called `bind`. In case (2), the socket is not<br>
> bound to a port because haproxy sets the `IP_BIND_ADDRESS_NO_PORT`<br>
> sockopt before calling `bind`. When you call `bind` *without*<br>
> `IP_BIND_ADDRESS_NO_PORT`, it causes the port number to be bound<br>
> before calling `connect`, which avoids the code path in `connect` that<br>
> results in `EADDRNOTAVAIL`.<br>
><br>
> I am confused by these results, which are contrary to my understanding<br>
> of what `IP_BIND_ADDRESS_NO_PORT` is supposed to do, which is<br>
> precisely to avoid the problem of source address port exhaustion by<br>
> deferring the port number assignment until the time of `connect`, when<br>
> additional information about the destination address is available. But<br>
> it's demonstrable that binding to a source port before calling<br>
> `connect` avoids `EADDRNOTAVAIL` errors in our use cases, whatever the<br>
> cause may be.<br>
_______________________________________________<br>
tor-relays mailing list<br>
<a href="mailto:tor-relays@lists.torproject.org" target="_blank">tor-relays@lists.torproject.org</a><br>
<a href="https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-relays" rel="noreferrer" target="_blank">https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-relays</a><br>
</blockquote></div>