qbittorrent / qBittorrent

qBittorrent BitTorrent client
https://www.qbittorrent.org
Other
28.36k stars 3.99k forks source link

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

Open WGH- opened 3 years ago

WGH- commented 3 years ago

Description

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

qBittorrent info and operating system(s)

If on Linux, libtorrent-rasterbar and Qt versions

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

cwmcd commented 3 years ago

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

WGH- commented 3 years ago

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

ghost commented 3 years ago

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

WGH- commented 3 years ago

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 commented 3 years ago

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- commented 3 years ago

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 commented 3 years ago

+1

arvidn commented 3 years ago

@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.