apparently on VPNs it's common for the gateway for a route to be outside of the netmask of the network it's a router for. fix that in get_gateway

This commit is contained in:
arvidn 2020-02-08 21:11:39 +01:00 committed by Arvid Norberg
parent 6d5bdbdb1a
commit 0a4a06ce74
2 changed files with 40 additions and 28 deletions

View File

@ -801,7 +801,7 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
// IPv6 gateways aren't addressed in the same network as the // IPv6 gateways aren't addressed in the same network as the
// interface, but they are addressed by the local network address // interface, but they are addressed by the local network address
// space. So this check only works for IPv4. // space. So this check only works for IPv4.
&& (!v4 || match_addr_mask(r.gateway, iface.interface_address, iface.netmask)) && (!v4 || match_addr_mask(r.gateway, iface.interface_address, r.netmask))
// in case there are multiple networks on the same networking // in case there are multiple networks on the same networking
// device, the source hint may be the only thing telling them // device, the source hint may be the only thing telling them
// apart // apart

View File

@ -180,21 +180,22 @@ TORRENT_TEST(build_netmask_unknown)
} }
namespace { namespace {
ip_route rt(char const* ip, char const* device, char const* gateway) ip_route rt(char const* ip, char const* device, char const* gateway, char const* mask)
{ {
ip_route ret; ip_route ret;
ret.destination = address::from_string(ip); ret.destination = address::from_string(ip);
ret.gateway = address::from_string(gateway); ret.gateway = address::from_string(gateway);
ret.netmask = address::from_string(mask);
std::strncpy(ret.name, device, sizeof(ret.name)); std::strncpy(ret.name, device, sizeof(ret.name));
ret.name[sizeof(ret.name) - 1] = '\0'; ret.name[sizeof(ret.name) - 1] = '\0';
return ret; return ret;
} }
ip_interface ip(char const* addr, char const* mask, char const* name) ip_interface ip(char const* addr, char const* name)
{ {
ip_interface ret; ip_interface ret;
ret.interface_address = address::from_string(addr); ret.interface_address = address::from_string(addr);
ret.netmask = address::from_string(mask); ret.netmask = address::from_string("255.255.255.255");
std::strncpy(ret.name, name, sizeof(ret.name)); std::strncpy(ret.name, name, sizeof(ret.name));
return ret; return ret;
} }
@ -203,70 +204,81 @@ namespace {
TORRENT_TEST(get_gateway_basic) TORRENT_TEST(get_gateway_basic)
{ {
std::vector<ip_route> const routes = { std::vector<ip_route> const routes = {
rt("0.0.0.0", "eth0", "192.168.0.1"), rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.255.0"),
rt("::", "eth0", "2a02::1234") rt("::", "eth0", "2a02::1234", "ffff::")
}; };
TEST_CHECK(get_gateway(ip("192.168.0.130", "255.255.0.0", "eth0"), routes) == address::from_string("192.168.0.1")); TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.0.1"));
TEST_CHECK(get_gateway(ip("2a02::4567", "ffff::", "eth0"), routes) == address::from_string("2a02::1234")); TEST_CHECK(get_gateway(ip("2a02::4567", "eth0"), routes) == address::from_string("2a02::1234"));
// the device name does not match the route // the device name does not match the route
TEST_CHECK(get_gateway(ip("192.168.0.130", "255.255.0.0", "eth1"), routes) == none); TEST_CHECK(get_gateway(ip("192.168.0.130", "eth1"), routes) == none);
TEST_CHECK(get_gateway(ip("2a02::4567", "ffff::", "eth1"), routes) == none); TEST_CHECK(get_gateway(ip("2a02::4567", "eth1"), routes) == none);
// the gateway for the route is outside of this local network, it cannot be // the gateway for the route is outside of this local network, it cannot be
// used for this network // used for this network
TEST_CHECK(get_gateway(ip("192.168.1.130", "255.255.255.0", "eth0"), routes) == none); TEST_CHECK(get_gateway(ip("192.168.1.130", "eth0"), routes) == none);
// for IPv6, the address family and device name matches, so it's a match // for IPv6, the address family and device name matches, so it's a match
TEST_CHECK(get_gateway(ip("2a02:8000::0123:4567", "ffff:ffff::", "eth0"), routes) == address::from_string("2a02::1234")); TEST_CHECK(get_gateway(ip("2a02:8000::0123:4567", "eth0"), routes) == address::from_string("2a02::1234"));
} }
TORRENT_TEST(get_gateway_no_default_route) TORRENT_TEST(get_gateway_no_default_route)
{ {
std::vector<ip_route> const routes = { std::vector<ip_route> const routes = {
rt("192.168.0.0", "eth0", "0.0.0.0"), rt("192.168.0.0", "eth0", "0.0.0.0", "0.0.0.0"),
rt("2a02::", "eth0", "::") rt("2a02::", "eth0", "::", "ffff::")
}; };
// no default route // no default route
TEST_CHECK(get_gateway(ip("192.168.1.130", "255.255.0.0", "eth0"), routes) == none); TEST_CHECK(get_gateway(ip("192.168.1.130", "eth0"), routes) == none);
TEST_CHECK(get_gateway(ip("2a02::1234", "ffff::", "eth0"), routes) == none); TEST_CHECK(get_gateway(ip("2a02::1234", "eth0"), routes) == none);
} }
TORRENT_TEST(get_gateway_local_v6) TORRENT_TEST(get_gateway_local_v6)
{ {
std::vector<ip_route> const routes = { std::vector<ip_route> const routes = {
rt("2a02::", "eth0", "::") rt("2a02::", "eth0", "::", "ffff::")
}; };
// local IPv6 addresses never have a gateway // local IPv6 addresses never have a gateway
TEST_CHECK(get_gateway(ip("fe80::1234", "ffff::", "eth0"), routes) == none); TEST_CHECK(get_gateway(ip("fe80::1234", "eth0"), routes) == none);
} }
// an odd, imaginary setup, where the loopback network has a gateway // an odd, imaginary setup, where the loopback network has a gateway
TORRENT_TEST(get_gateway_loopback) TORRENT_TEST(get_gateway_loopback)
{ {
std::vector<ip_route> const routes = { std::vector<ip_route> const routes = {
rt("0.0.0.0", "eth0", "192.168.0.1"), rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.0.0"),
rt("0.0.0.0", "lo", "127.1.1.1"), rt("0.0.0.0", "lo", "127.1.1.1", "255.0.0.0"),
rt("::", "eth0", "fec0::1234"), rt("::", "eth0", "fec0::1234", "ffff::"),
rt("::", "lo", "::2") rt("::", "lo", "::2", "ffff:ffff:ffff:ffff::")
}; };
TEST_CHECK(get_gateway(ip("127.0.0.1", "255.0.0.0", "lo"), routes) == address::from_string("127.1.1.1")); TEST_CHECK(get_gateway(ip("127.0.0.1", "lo"), routes) == address::from_string("127.1.1.1"));
// with IPv6, there are no gateways for local or loopback addresses // with IPv6, there are no gateways for local or loopback addresses
TEST_CHECK(get_gateway(ip("::1", "ffff:ffff:ffff:ffff::", "lo"), routes) == none); TEST_CHECK(get_gateway(ip("::1", "lo"), routes) == none);
}
TORRENT_TEST(get_gateway_netmask)
{
std::vector<ip_route> const routes = {
rt("0.0.0.0", "eth0", "192.168.1.1", "255.255.255.0"),
rt("0.0.0.0", "eth0", "192.168.2.1", "0.0.0.0")
};
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.2.1"));
TEST_CHECK(get_gateway(ip("192.168.1.130", "eth0"), routes) == address::from_string("192.168.1.1"));
} }
TORRENT_TEST(get_gateway_multi_homed) TORRENT_TEST(get_gateway_multi_homed)
{ {
std::vector<ip_route> const routes = { std::vector<ip_route> const routes = {
rt("0.0.0.0", "eth0", "192.168.0.1"), rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.0.0"),
rt("0.0.0.0", "eth0", "10.0.0.1") rt("0.0.0.0", "eth0", "10.0.0.1", "255.0.0.0")
}; };
TEST_CHECK(get_gateway(ip("192.168.0.130", "255.255.0.0", "eth0"), routes) == address::from_string("192.168.0.1")); TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.0.1"));
TEST_CHECK(get_gateway(ip("10.0.1.130", "255.0.0.0", "eth0"), routes) == address::from_string("10.0.0.1")); TEST_CHECK(get_gateway(ip("10.0.1.130", "eth0"), routes) == address::from_string("10.0.0.1"));
} }