Skip to content
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

qBittorrent uTP bypasses routing table, sends packets to the internet on the interface with no default route #15325

Open
WGH- opened this issue Aug 14, 2021 · 9 comments
Labels
Libtorrent Proxy/VPN Issues related to the use of proxy/VPN

Comments

@WGH-
Copy link

WGH- commented Aug 14, 2021

Description

qBittorrent sends UDP traffic to the internet through network interfaces that have no default route configured.

qBittorrent info and operating system(s)

  • qBittorrent version: v4.3.5
  • Operating system(s) where the issue occurs: Gentoo Linux

If on Linux, libtorrent-rasterbar and Qt versions

  • Qt: 5.15.2
  • libtorrent-rasterbar: 1.2.12.0

What is the problem

I have a WireGuard interface set up without default route set.

$ ip addr show dev foobar-wg
4: foobar-wg: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.2.3/24 scope global foobar-wg
       valid_lft forever preferred_lft forever
    inet6 2a01:4f8:XXXX:XXXX:1::3/128 scope global
       valid_lft forever preferred_lft forever

$ { for x in -4 -6; do ip $x route show; done } | grep foobar-wg
192.168.2.0/24 dev foobar-wg proto kernel scope link src 192.168.2.3
2a01:4f8:XXXX:XXXX:1::3 dev foobar-wg proto kernel metric 256 pref medium

However, qBittorrent still sends DHT/LSD/uTP traffic through it. Which is pretty bad, since I didn't expect any public internet traffic to go through it, especially BitTorrent one, since that could get me into trouble with the VPS hoster.

Detailed steps to reproduce the problem

  1. ip link add name wg-foobar type wireguard
  2. ip addr add 192.168.3.2/24 dev wg-foobar
  3. ip link set dev wg-foobar up
  4. tcpdump -i wg-foobar -n
  5. Start qBittorrent

What is the expected behavior

qBittorrent should never bypass system routing table.

Extra info (if any)

This looks somewhat similar to #13094 and arvidn/libtorrent#4850, but what I see is that the source IP address is selected correctly. In fact, the remote WireGuard peer actually routes the traffic to the public internet, and qBittorrent gets response.

/cc @arvidn

@thalieht thalieht added the Proxy/VPN Issues related to the use of proxy/VPN label Aug 14, 2021
@cwmcd
Copy link

cwmcd commented Aug 15, 2021

so did you set qbittorrent to use the wireguard interface via tools>options>advanced>network interfaces?

@WGH-
Copy link
Author

WGH- commented Aug 15, 2021

@cwmcd nope, it's set to "Any interface".

@ghost
Copy link

ghost commented Aug 15, 2021

If you do not select a specific interface/IP to listen to, qBt will listen on all interfaces and leak your IP.

@WGH-
Copy link
Author

WGH- commented Aug 15, 2021

I still think qBittorrent does the wrong thing that accidentally happens to work on Linux. Like dude, there's no route to the public internet on this network interface, why are you sending stuff to it?

@arvidn
Copy link
Contributor

arvidn commented Aug 16, 2021

I'm interested in improving this. Have you looked at the logic that decides which interfaces to use, and identified the issue there. The routing table is taken into account, but I recall wireguard setting up its network interface in a different way than other VPNs (with a point-to-point connection) that warranted some special rules in libtorrent.

@WGH-
Copy link
Author

WGH- commented Aug 16, 2021

It seems libtorrent chooses random socket (with_gateways[random(std::uint32_t(with_gateways.size() - 1))]) from all sockets that it doesn't consider "local" (!(ls->flags & listen_socket_t::local_network))? And in my scenario the socket corresponding to the WireGuard interface is considered non-local because it's indeed point-to-point:

bool const local
        = ipface.interface_address.is_loopback()
        || is_link_local(ipface.interface_address)
        || (ipface.flags & if_flags::loopback)
        || (!is_global(ipface.interface_address)
                && !(ipface.flags & if_flags::pointopoint)
                && has_any_internet_route(routes)
                && !has_internet_route(ipface.name, family(ipface.interface_address), routes));

This condition looks weird. I'm not sure what exactly is special with p2p links wrt routing. They don't have a concept of gateways, sure, but otherwise they work the same.

However, WireGuard in particular uses policy-based routing in many configurations. My example is not one, you'd need to use wg-quick on WireGuard config with AllowedIPs=0.0.0.0/0 to trigger that. The default routing table remains unchanged, but:

# ip route
default via 10.0.0.254 dev enp0s25 proto dhcp src 10.0.0.71 metric 1024
10.0.0.0/24 dev enp0s25 proto kernel scope link src 10.0.0.71
192.168.2.0/24 dev wg-foobar proto kernel scope link src 192.168.2.3

# ip rule
0:	from all lookup local
32764:	from all lookup main suppress_prefixlength 0 # <- added by wg-quick
32765:	not from all fwmark 0xca6c lookup 51820 # <- added by wg-quick
32766:	from all lookup main
32767:	from all lookup default

# ip route show table 51820
default dev wg-foobar scope link

# ip route get 1.1.1.1
1.1.1.1 dev wg-foobar table 51820 src 192.168.2.3 uid 0
    cache

This trick is described in detail here: https://www.wireguard.com/netns/#routing-all-your-traffic

But this whole thing looks very strange to me. Perhaps, there was a reason why the code was written the way it is, but I don't understand why libtorrent bothers to enumerate interfaces to send a packet somewhere. Just bind the socket to the any address (0.0.0.0 or, even better, ::), and let the OS route as it wants. However, enumerating interfaces would be still necessary to run LSD, I think.

@superclarkk

This comment has been minimized.

@arvidn
Copy link
Contributor

arvidn commented Oct 26, 2021

@WGH- Fundamentally the reason libtorrent does this is to support multi-homed systems. i.e. systems that might have multiple paths to the internet, for example via IPv4 and IPv6. This support has been generalized to also support specifying which interfaces/IPs to use (and not use any others).

When announcing to trackers, each local IP that can reach the tracker needs to announce, so that all of our external IP addresses are recorded by the tracker.

Additionally, the way the DHT handles multiple external IPs is by effectively running separate nodes, with separate routing tables, on each.

It's been a while since I was looking at that code, but iirc, looking for a gateway and default route was a heuristic to determine whether this interface/source IP has a possibility to reach the internet at all.

@Neustradamus
Copy link

It will be nice to have a good µTP (uTP) support for I2P:

cc: @zzzi2p.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Libtorrent Proxy/VPN Issues related to the use of proxy/VPN
Projects
None yet
Development

No branches or pull requests

7 participants