factor out get_gateway function and add unit tests. IPv6 gateways are not addressed in the same network, so we can't use match_addr_mask(). Assume all local IPv6 addresses do not have a gateway
This commit is contained in:
parent
571952fd19
commit
a53d3a8746
|
@ -50,6 +50,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/error_code.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/aux_/bind_to_device.hpp"
|
||||
#include "libtorrent/span.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -101,6 +102,11 @@ namespace libtorrent {
|
|||
TORRENT_EXTRA_EXPORT bool in_local_network(std::vector<ip_interface> const& net
|
||||
, address const& addr);
|
||||
|
||||
// return the gateway for the given ip_interface, if there is one. Otherwise
|
||||
// return nullopt.
|
||||
TORRENT_EXTRA_EXPORT boost::optional<address> get_gateway(
|
||||
ip_interface const& iface, span<ip_route const> routes);
|
||||
|
||||
TORRENT_EXTRA_EXPORT boost::optional<ip_route> get_default_route(io_service& ios
|
||||
, string_view device, bool v6, error_code& ec);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
#include "libtorrent/aux_/socket_type.hpp"
|
||||
#include "libtorrent/span.hpp"
|
||||
#ifdef TORRENT_WINDOWS
|
||||
#include "libtorrent/aux_/win_util.hpp"
|
||||
#endif
|
||||
|
@ -793,6 +794,29 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
|
|||
return ret;
|
||||
}
|
||||
|
||||
boost::optional<address> get_gateway(ip_interface const& iface, span<ip_route const> routes)
|
||||
{
|
||||
bool const v4 = iface.interface_address.is_v4();
|
||||
|
||||
// local IPv6 addresses can never be used to reach the internet
|
||||
if (!v4 && is_local(iface.interface_address)) return {};
|
||||
|
||||
auto const it = std::find_if(routes.begin(), routes.end()
|
||||
, [&](ip_route const& r) -> bool
|
||||
{
|
||||
return r.destination.is_unspecified()
|
||||
&& r.destination.is_v4() == iface.interface_address.is_v4()
|
||||
&& !r.gateway.is_unspecified()
|
||||
// IPv6 gateways aren't addressed in the same network as the
|
||||
// interface, but they are addressed by the local network address
|
||||
// space. So this check only works for IPv4.
|
||||
&& (!v4 || match_addr_mask(r.gateway, iface.interface_address, iface.netmask))
|
||||
&& strcmp(r.name, iface.name) == 0;
|
||||
});
|
||||
if (it != routes.end()) return it->gateway;
|
||||
return {};
|
||||
}
|
||||
|
||||
boost::optional<ip_route> get_default_route(io_service& ios
|
||||
, string_view const device, bool const v6, error_code& ec)
|
||||
{
|
||||
|
|
|
@ -305,21 +305,10 @@ namespace aux {
|
|||
|
||||
ep.netmask = iface->netmask;
|
||||
|
||||
bool const v4 = iface->interface_address.is_v4();
|
||||
|
||||
// also record whether the device has a gateway associated with it
|
||||
// (which indicates it can be used to reach the internet)
|
||||
// only gateways inside the interface's network count
|
||||
bool const has_gateway = std::find_if(routes.begin(), routes.end(), [&](ip_route const& r)
|
||||
{
|
||||
return r.destination.is_unspecified()
|
||||
&& r.destination.is_v4() == v4
|
||||
&& !r.gateway.is_unspecified()
|
||||
&& match_addr_mask(r.gateway, iface->interface_address, iface->netmask)
|
||||
&& strcmp(r.name, iface->name) == 0;
|
||||
}) != routes.end();
|
||||
|
||||
if (has_gateway)
|
||||
if(get_gateway(*iface, routes))
|
||||
ep.flags |= listen_socket_t::has_gateway;
|
||||
|
||||
ep.device = iface->name;
|
||||
|
|
|
@ -66,7 +66,7 @@ int main()
|
|||
std::printf("%-18s%-18s%-35s%-7d%s\n"
|
||||
, r.destination.to_string(ec).c_str()
|
||||
, r.netmask.to_string(ec).c_str()
|
||||
, r.gateway.to_string(ec).c_str()
|
||||
, r.gateway.is_unspecified() ? "-" : r.gateway.to_string(ec).c_str()
|
||||
, r.mtu
|
||||
, r.name);
|
||||
}
|
||||
|
@ -80,11 +80,11 @@ int main()
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::printf("%-34s%-45s%-20s%-20s%-34sdescription\n", "address", "netmask", "name", "flags", "default gateway");
|
||||
std::printf("%-34s%-45s%-20s%-20s%-34sdescription\n", "address", "netmask", "name", "flags", "gateway");
|
||||
|
||||
for (auto const& i : net)
|
||||
{
|
||||
address const iface_def_gw = get_default_gateway(ios, i.name, i.interface_address.is_v6(), ec);
|
||||
boost::optional<address> const gateway = get_gateway(i, routes);
|
||||
std::printf("%-34s%-45s%-20s%s%s%-20s%-34s%s %s\n"
|
||||
, i.interface_address.to_string(ec).c_str()
|
||||
, i.netmask.to_string(ec).c_str()
|
||||
|
@ -92,7 +92,7 @@ int main()
|
|||
, (i.interface_address.is_multicast()?"multicast ":"")
|
||||
, (is_local(i.interface_address)?"local ":"")
|
||||
, (is_loopback(i.interface_address)?"loopback ":"")
|
||||
, iface_def_gw.to_string(ec).c_str()
|
||||
, gateway ? gateway->to_string(ec).c_str() : "-"
|
||||
, i.friendly_name, i.description);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include "libtorrent/address.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
#include <cstring>
|
||||
|
||||
using namespace lt;
|
||||
using boost::none;
|
||||
|
||||
TORRENT_TEST(is_local)
|
||||
{
|
||||
|
@ -176,3 +178,95 @@ TORRENT_TEST(build_netmask_unknown)
|
|||
{
|
||||
TEST_CHECK(build_netmask(0, -1) == address{});
|
||||
}
|
||||
|
||||
namespace {
|
||||
ip_route rt(char const* ip, char const* device, char const* gateway)
|
||||
{
|
||||
ip_route ret;
|
||||
ret.destination = address::from_string(ip);
|
||||
ret.gateway = address::from_string(gateway);
|
||||
std::strncpy(ret.name, device, sizeof(ret.name));
|
||||
ret.name[sizeof(ret.name) - 1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
ip_interface ip(char const* addr, char const* mask, char const* name)
|
||||
{
|
||||
ip_interface ret;
|
||||
ret.interface_address = address::from_string(addr);
|
||||
ret.netmask = address::from_string(mask);
|
||||
std::strncpy(ret.name, name, sizeof(ret.name));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(get_gateway_basic)
|
||||
{
|
||||
std::vector<ip_route> const routes = {
|
||||
rt("0.0.0.0", "eth0", "192.168.0.1"),
|
||||
rt("::", "eth0", "2a02::1234")
|
||||
};
|
||||
|
||||
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("2a02::4567", "ffff::", "eth0"), routes) == address::from_string("2a02::1234"));
|
||||
|
||||
// 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("2a02::4567", "ffff::", "eth1"), routes) == none);
|
||||
|
||||
// the gateway for the route is outside of this local network, it cannot be
|
||||
// used for this network
|
||||
TEST_CHECK(get_gateway(ip("192.168.1.130", "255.255.255.0", "eth0"), routes) == none);
|
||||
|
||||
// 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"));
|
||||
}
|
||||
|
||||
TORRENT_TEST(get_gateway_no_default_route)
|
||||
{
|
||||
std::vector<ip_route> const routes = {
|
||||
rt("192.168.0.0", "eth0", "0.0.0.0"),
|
||||
rt("2a02::", "eth0", "::")
|
||||
};
|
||||
|
||||
// no default route
|
||||
TEST_CHECK(get_gateway(ip("192.168.1.130", "255.255.0.0", "eth0"), routes) == none);
|
||||
TEST_CHECK(get_gateway(ip("2a02::1234", "ffff::", "eth0"), routes) == none);
|
||||
}
|
||||
|
||||
TORRENT_TEST(get_gateway_local_v6)
|
||||
{
|
||||
std::vector<ip_route> const routes = {
|
||||
rt("2a02::", "eth0", "::")
|
||||
};
|
||||
|
||||
// local IPv6 addresses never have a gateway
|
||||
TEST_CHECK(get_gateway(ip("fe80::1234", "ffff::", "eth0"), routes) == none);
|
||||
}
|
||||
|
||||
// an odd, imaginary setup, where the loopback network has a gateway
|
||||
TORRENT_TEST(get_gateway_loopback)
|
||||
{
|
||||
std::vector<ip_route> const routes = {
|
||||
rt("0.0.0.0", "eth0", "192.168.0.1"),
|
||||
rt("0.0.0.0", "lo", "127.1.1.1"),
|
||||
rt("::", "eth0", "fec0::1234"),
|
||||
rt("::", "lo", "::2")
|
||||
};
|
||||
|
||||
TEST_CHECK(get_gateway(ip("127.0.0.1", "255.0.0.0", "lo"), routes) == address::from_string("127.1.1.1"));
|
||||
|
||||
// with IPv6, there are no gateways for local or loopback addresses
|
||||
TEST_CHECK(get_gateway(ip("::1", "ffff:ffff:ffff:ffff::", "lo"), routes) == none);
|
||||
}
|
||||
|
||||
TORRENT_TEST(get_gateway_multi_homed)
|
||||
{
|
||||
std::vector<ip_route> const routes = {
|
||||
rt("0.0.0.0", "eth0", "192.168.0.1"),
|
||||
rt("0.0.0.0", "eth0", "10.0.0.1")
|
||||
};
|
||||
|
||||
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("10.0.1.130", "255.0.0.0", "eth0"), routes) == address::from_string("10.0.0.1"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue