simplify natpmp gateway and local address discovery

This commit is contained in:
Arvid Norberg 2018-07-29 09:05:50 +02:00 committed by Arvid Norberg
parent d7a60442bd
commit a477a53b80
4 changed files with 65 additions and 43 deletions

View File

@ -41,6 +41,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <sys/socket.h> // for SO_BINDTODEVICE
#endif
#include <boost/optional.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/io_service_fwd.hpp"
@ -94,6 +96,9 @@ namespace libtorrent {
TORRENT_EXTRA_EXPORT bool in_local_network(std::vector<ip_interface> const& net
, address const& addr);
TORRENT_EXTRA_EXPORT boost::optional<ip_route> get_default_route(io_service& ios
, string_view device, bool v6, error_code& ec);
// returns the first default gateway found if device is empty
TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios
, string_view device, bool v6, error_code& ec);

View File

@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/asio/ip/host_name.hpp>
#include <boost/optional.hpp>
#if TORRENT_USE_IFCONF
#include <sys/ioctl.h>
@ -844,10 +845,10 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
return ret;
}
address get_default_gateway(io_service& ios
, string_view device, bool v6, error_code& ec)
boost::optional<ip_route> get_default_route(io_service& ios
, string_view const device, bool const v6, error_code& ec)
{
std::vector<ip_route> ret = enum_routes(ios, ec);
std::vector<ip_route> const ret = enum_routes(ios, ec);
auto const i = std::find_if(ret.begin(), ret.end()
, [device,v6](ip_route const& r)
{
@ -855,8 +856,15 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
&& r.destination.is_v6() == v6
&& (device.empty() || r.name == device);
});
if (i == ret.end()) return address();
return i->gateway;
if (i == ret.end()) return boost::none;
return *i;
}
address get_default_gateway(io_service& ios
, string_view const device, bool const v6, error_code& ec)
{
auto const default_route = get_default_route(ios, device, v6, ec);
return default_route ? default_route->gateway : address();
}
std::vector<ip_route> enum_routes(io_service& ios, error_code& ec)

View File

@ -156,24 +156,58 @@ void natpmp::start(address local_address, std::string device)
// if necessary
m_version = version_pcp;
// we really want a device name to get the right default gateway
// try to find one even if the listen socket isn't bound to a device
if (device.empty())
{
device = device_for_address(local_address, m_socket.get_io_service(), ec);
// if this fails fall back to using the first default gateway in the
// routing table
ec.clear();
}
auto const route = get_default_route(m_socket.get_io_service()
, device, local_address.is_v6(), ec);
if (!route)
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
log("failed to find default route for \"%s\" %s: %s"
, device.c_str(), local_address.to_string().c_str()
, convert_from_native(ec.message()).c_str());
}
#endif
disable(ec);
return;
}
if (device.empty()) device = route->name;
TORRENT_ASSERT(!device.empty());
// PCP requires reporting the source address at the application
// layer so the socket MUST be bound to a specific address
// if the caller didn't specify one then get the first suitable
// address from the OS
if (local_address.is_unspecified())
{
for (auto const& a : enum_net_interfaces(m_socket.get_io_service(), ec))
{
if (a.interface_address.is_loopback()) continue;
if (a.interface_address.is_v4() != local_address.is_v4()) continue;
if (a.interface_address.is_v6() && is_local(a.interface_address)) continue;
if (!device.empty() && a.name != device) continue;
local_address = a.interface_address;
device = a.name;
break;
}
std::vector<ip_interface> const net = enum_net_interfaces(
m_socket.get_io_service(), ec);
if (local_address.is_unspecified())
auto const it = std::find_if(net.begin(), net.end(), [&](ip_interface const& i)
{
return i.interface_address.is_v4() == local_address.is_v4()
&& (i.interface_address.is_v4() || !is_local(i.interface_address))
&& i.name == device;
});
if (it != net.end())
{
local_address = it->interface_address;
}
else
{
// if we can't get a specific address to bind to we'll have
// to fall back to NAT-PMP
@ -198,34 +232,9 @@ void natpmp::start(address local_address, std::string device)
}
}
// we really want a device name to get the right default gateway
// try to find one even if the listen socket isn't bound to a device
if (device.empty())
{
device = device_for_address(local_address, m_socket.get_io_service(), ec);
// if this fails fall back to using the first default gateway in the
// routing table
ec.clear();
}
address const gateway = get_default_gateway(m_socket.get_io_service()
, device, local_address.is_v6(), ec);
if (ec || gateway.is_unspecified())
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
log("failed to find default route for %s: %s"
, local_address.to_string().c_str(), convert_from_native(ec.message()).c_str());
}
#endif
disable(ec);
return;
}
m_disabled = false;
udp::endpoint nat_endpoint(gateway, 5351);
udp::endpoint const nat_endpoint(route->gateway, 5351);
if (nat_endpoint == m_nat_endpoint) return;
m_nat_endpoint = nat_endpoint;

View File

@ -84,7 +84,7 @@ int main()
for (auto const& i : net)
{
address iface_def_gw = get_default_gateway(ios, i.name, i.interface_address.is_v6(), ec);
address const iface_def_gw = get_default_gateway(ios, i.name, i.interface_address.is_v6(), ec);
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()