-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Linux: SO_BINDTODEVICE not used for http tracker announce ("leaks" real IP) #6772
Comments
This is, unfortunately, the way the Linux networking stack works. In the normal case, Linux routes packets based only on destination IP. You have multiple default routes, and your wifi interface route has a lower metric, so that will be used. Your source ip / bind address is disregarded. Routing based on information other than the destination IP is called "policy routing". This is managed with I'm actually surprised Personally, I do bittorrent-over-VPN on Linux by using docker. In one container I run my vpn client, configured to route all traffic. It doesn't route all traffic on the host, just the traffic in its container. I then run my torrent client using All that said ... @arvidn It might be nice to have libtorrent do |
@AllSeeingEyeTolledEweSew thanks for the response. The docker idea is a good one, can force traffic while still having my "normal" internet as well, will play around with it + port forwarding! Regarding the local interface, it seems that Linux indeed treats this differently - I was able to make it work on Windows (kinda). Very interestingly, ndoe.js also does not set that option it seems - when I was debugging this issue on windows, I wrote a node script to make a TCP connection from a local address: import net from 'net';
const s = net.createConnection({
localAddress: '10.16.0.5',
host: 'a_server_i_own',
port: 58384
}); On linux, when my route via 10.6.0.1 has a higher metric, it tries to be sent out from ethernet
On linux, when my route via 10.6.0.1 has the lowest metric (first choice), it goes via tun0 correctly
This behavior aligns with what you mentioned, as when I do a quick search of node.js source for Interesting: On **WINDOWS** when there is no route, the node script gives an error that there is no route
I think, if it would not be to hard to add the option so that it does not affect windows users, it would be nice! |
Look like libtorrent actually does do I must have mistyped a grep to look for it before. So IIUC, you should get desired behavior if you change the libtorrent setting |
From the qbittorrent source itself, it seems it does create a setting for outgoing interfaces - https://github.com/qbittorrent/qBittorrent/blob/master/src/base/bittorrent/session.cpp#L1501 Though as you say, I do not know if it is being controlled by the GUI (nor is my C++ good enough to find out). Can you confirm if libtorrent should always use |
FYI: Here is an strace the moment I add the tracker to the torrent.
I chose a random IP / PORT combo (23.23.23.23:7777) just to track the TCP handshake (well the SYN). tcp dump again shows that it is being sent out via wrong interface (ethernet) strace of all socket calls when I launch qbit``` $ strace -f -e trace=network qbittorrent 2>&1 | grep "socket(" socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0) = 3 [pid 241503] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0) = 7 [pid 241503] socket(AF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_ROUTE) = 9 [pid 241505] socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) = 12 [pid 241505] socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE) = 13 [pid 241505] socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE) = 13 [pid 241505] socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 14 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 18 [pid 241505] socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 13 [pid 241505] socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 14 [pid 241510] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0) = 25 [pid 241503] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 31 [pid 241503] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 31 [pid 241505] socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 32 ```I do not see the option for However, the source IP is set in the bind call following socket creation (though I understand, in linux this does not control routing without EDIT: Just to reiterate - I am talking about HTTP Tracker Announces here, in case it is just handled differently in libtorrent (ignoring network interface options or something) |
On latest Deluge
with interface, IP set as: I manual add a fake tracker strace:
(note - no tcpdump shows packet sent out from ethernet (not
|
What happens if you select only the interface in qbit and not optional ip. |
Just tried, this is what I get:
On adding a fake tracker SYN gets sent out via ethernet again:
Again, with "correct" source IP, wrong interface. |
This comment was marked as off-topic.
This comment was marked as off-topic.
I should have done this before, but if I add a peer manually , then it does do so:
Note the tcpdump shows SYN from correct interface. So it seems the issue is that HTTP announce does not respect the interface setting. @arvidn I wonder if this is a conscious decision? I remember reading an issue where you'd mentioned that you didn't see a problem with the announce going from an IP which was connectable (#4803 (comment)) But in my case, I would not be able to accept incoming connections via ethernet, since qbit is bound (listening) on tun0. (So when the tracker gives my gateway IP of ethernet to other peers they wont be able to connect) |
the intention is that the tracker announce is made from all the interfaces listed in the |
In my (admittedly unique) case, I would argue it is made from the IP , since for it to be made from the interface it would explicitly provide the Would you consider adding that option ( |
I think what's going on is that libtorrent (may) only use Could you try to specify |
Althought I am not using libtorrent directly, I do think the following settings in the torrent clients should be equivalent. qBittorrentstrace for manual add HTTP tracker
(tcp dump shows syn sent out via incorrect wifi interface) strace for manual add UDP tracker
tcpdump shows packet sent from "CORRECT" tun0 interface strace for adding manual peer:
The initial UDP packet (I'm guessing for uTP connection) was sent out via Delugestrace for adding HTTP tracker;
tcp dump shows SYN sent via wifi interface (both 192.x source and 10.x source) strace for adding UDP tracker:
tcpdump shows the 192.x source went out via wifi, the 10.x source went out via strace for adding peer:
Again, the first seems to be a uTP attempt, tcpdump shows src was Again, I do not know exactly the internals of qbit/deluge with libtorrent, but I would think that when I change settings they would update it in libtorrent, an |
If anyone comes across this, I am using the following workaround on linux as a workaround:
i.e. Policy Based routing. Since it just routes all based on source address, it is a bit simpler than application level policy routing (tagging packets and whatnot). I used https://casualhacking.io/blog/2010/12/22/route-based-on-source-ip-address-linux-bsd.html as a reference |
From your screenshot, your "Incoming address" is blank. Assuming this corresponds to the libtorrent The outgoing interface is meant to load balance between multiple interfaces your outgoing peer connections The purpose of the outgoing tracker connection is to make other peers know how to connect to you. That's why the tracker connections needs to be made from the same interface that your accepting incoming connections on. i.e. it's not taking Have you tried setting your "Incoming Address" to |
I have tried that as well -> #6772 (comment) The |
That doesn't sound right. I was assuming "Incoming Address" controlled the Digging a little bit in deluge, it looks like they always configure two listen interfaces, always the same device/IP but with different ports. Sometimes even with the same port. The internal setting in deluge is called this is where it duplicates the listen interfaces. It looks like a bug. Anyway, regarding you setting an interface name as your listen address, the string appears to be checked to be a valid interface, here by this function. is it possible your this seems to suggest it is the "incoming address" that's controlling |
I think he mentioned of having same issue with qbit as well on the OP. |
If I set "Incoming Address" to If I set it to But when I add a peer , it is ok (first UDP must be a uTP attempt): this is because for adding a peer,
However, since you mention it is a fringe setting, it is quite possible deluge/qbit just dont behave as I expect them to, which is unfortunate as it results in the tracker registering the wrong IP. Regardless, I would not ask of you to debug clients' implementation of libtorrent, perhaps I should open an issue in there saying they are not calling the underlying function correctly? |
I believe this is an oversight. UDP tracker announces use a socket that has been |
Just my two cents, but on (qbittorrent 4.4.1) a tracker I happen to announce w/ several layers of internet connectivity layers. If I ran a vpn on the docker bridge it would, I expect, add another level of attenuation. The tracker in question would accept my announcement, but not pass on the internal IP's to peers but would log them. I am not sure how to IP inspect a announcement, however. |
I believe this fixes it: #6860 I made a clean-up commit right before that one on |
Thanks a lot arvidn. I will try compiling it and then compile qbit / deluge against it! |
Awesome, I can confirm it works! Adding an http tracker now. strace of socket call:
Correct
For those curious, this is how I managed to compile it (was a pain tbh...):Clone libtorrent, checkout
Build libtorrent with python (had to install cmake, ninja)
(it could not find my python3 otherwise) Then, clone and install deluge
The latest deluge with LT 1.2 has some other errors in logs which I ignored for this test |
thanks for testing! |
libtorrent version (or branch): 2.0.5-1
platform/architecture: Arch Linux
Linux arch 5.16.10-arch1-1
, x64 Intelcompiler and compiler version: Sorry dunno, installed qbit from pacman today (2022-03-09)
TL;DR SO_BINDTODEVICE is only used on the peer BT connection, so it is possible for the TCP packets for HTTP announce to be sent from wrong interface (and result in tracker registering the wrong external IP)
My qBittorrent settings:
However the announce request is being sent from the
wlp0s20f3
interface (though source IP is indeed10.16.0.5
). More details below...I have two gateways on my machine, with my secondary gateway having a higher metric. (Note: Technically the 2nd gateway is an OpenVPN connection - but I ave configured routes manually and I am not trying a killswitch or anything like that, so I will try and refer to it as gateway #2)
My main gateway has Public IP 183.x.x.x , local is 192.x.x.131 on
wlp0s20f3
My secondary gateway has Public IP 152.x.x.x, local is 10.x.x.5 on
tun0
.Routing table:
I verify that the routing to internet happens correctly (default via interface) by:
When I add an http tracker in qbittorrent, the packets are sent out on the
wlp0s20f3
interface instead oftun0
. I verified this by running the following tcpdumps at the same time:interface of gateway 1
interface of gateway 2
The packet you see is the SYN to establish TCP connection. Weirdly the source IP is
10.16.0.5
which belongs totun0
interface. Since I explicitly set that in qbittorrent, I would assume it would go through there! It isn't routed however, and just getsHowever, when I launch qbittorrent, I see a ton of UDP traffic on the
tun0
interface - the DHT stuffs. Clearly this means qbittorrent can access it, but for the announce it isnt!Additionally, the tracker does receive the SYN, and even responses with a SYN/ACK, though this is lost - my guess is Gateway 1 (my ISP router) , tries to route the SYN/ACK back to
10.16.0.5
, realizes it has no idea who that IP belongs to. I confirm this because partly, since I run that tracker:Here is TCP dump from server if it is of interest
This means the tracker will see my real IP, though since I don't complete the TCP handshake (because of broken routing), I dont actually make the HTTP announce. To confirm - I have configured the interface in qBit, and that interface passes traffic over gateway 2 (verified via the previous cURL)
(If I do a manual curl to the tracker, and source IP is 192.x.x.x, then I can complete the handshake + HTTP req).
I tried to poke around libtorrent source, but C/C++ programming is not my strong suit. From a networking / TCP/IP point of view, I do believe this is a bug however.
Lastly, I was actually trying to replicate a bug I faced in windows qbit (different version), where similar behavior would occur, and the handshake would complete, resulting in the tracker registering my real IP!!!. Will make a separate issue for it.
The text was updated successfully, but these errors were encountered: