diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 2be68889a..1d97a9d12 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -201,6 +201,8 @@ namespace aux { // time on handler allocation every time we read again. aux::handler_storage udp_handler_storage; + std::shared_ptr natpmp_mapper; + // the key is an id that is used to identify the // client with the tracker only. std::uint32_t tracker_key = 0; @@ -636,7 +638,7 @@ namespace aux { void start_ip_notifier(); void start_lsd(); - natpmp* start_natpmp(); + void start_natpmp(); upnp* start_upnp(); void stop_ip_notifier(); @@ -777,6 +779,8 @@ namespace aux { void on_lsd_peer(tcp::endpoint const& peer, sha1_hash const& ih) override; + void start_natpmp(aux::listen_socket_t& s); + void set_external_address(std::shared_ptr const& sock, address const& ip , ip_source_t source_type, address const& source); @@ -1136,7 +1140,6 @@ namespace aux { // this is deducted from the connect speed int m_boost_connections = 0; - std::shared_ptr m_natpmp; std::shared_ptr m_upnp; std::shared_ptr m_lsd; diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index 9d6a490ef..9dbad1813 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -94,7 +94,9 @@ namespace libtorrent { TORRENT_EXTRA_EXPORT bool in_local_network(std::vector const& net , address const& addr); - TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios, 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); // attempt to bind socket to the device with the specified name. For systems // that don't support SO_BINDTODEVICE the socket will be bound to one of the diff --git a/include/libtorrent/natpmp.hpp b/include/libtorrent/natpmp.hpp index e78e87fec..9c6a65888 100644 --- a/include/libtorrent/natpmp.hpp +++ b/include/libtorrent/natpmp.hpp @@ -51,7 +51,7 @@ struct TORRENT_EXTRA_EXPORT natpmp { natpmp(io_service& ios, aux::portmap_callback& cb); - void start(); + void start(address const& local_address, std::string device); // maps the ports, if a port is set to 0 // it will not be mapped diff --git a/src/enum_net.cpp b/src/enum_net.cpp index ef9c384db..a8fccbb8b 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -846,11 +846,17 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl return ret; } - address get_default_gateway(io_service& ios, error_code& ec) + address get_default_gateway(io_service& ios + , string_view device, bool v6, error_code& ec) { std::vector ret = enum_routes(ios, ec); auto const i = std::find_if(ret.begin(), ret.end() - , [](ip_route const& r) { return r.destination == address(); }); + , [device,v6](ip_route const& r) + { + return r.destination.is_unspecified() + && r.destination.is_v6() == v6 + && (device.empty() || r.name == device); + }); if (i == ret.end()) return address(); return i->gateway; } diff --git a/src/natpmp.cpp b/src/natpmp.cpp index 39cf384da..2f0750004 100644 --- a/src/natpmp.cpp +++ b/src/natpmp.cpp @@ -76,19 +76,31 @@ natpmp::natpmp(io_service& ios m_mappings.reserve(10); } -void natpmp::start() +void natpmp::start(address const& local_address, std::string device) { TORRENT_ASSERT(is_single_thread()); error_code ec; - address const gateway = get_default_gateway(m_socket.get_io_service(), ec); - if (ec) + + // 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 failes 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: %s" - , convert_from_native(ec.message()).c_str()); + 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); @@ -109,13 +121,13 @@ void natpmp::start() } #endif - m_socket.open(udp::v4(), ec); + m_socket.open(local_address.is_v4() ? udp::v4() : udp::v6(), ec); if (ec) { disable(ec); return; } - m_socket.bind({address_v4::any(), 0}, ec); + m_socket.bind({local_address, 0}, ec); if (ec) { disable(ec); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index d85c776ad..b30590728 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1859,6 +1859,7 @@ namespace aux { #endif if ((*remove_iter)->sock) (*remove_iter)->sock->close(ec); if ((*remove_iter)->udp_sock) (*remove_iter)->udp_sock->sock.close(); + if ((*remove_iter)->natpmp_mapper) (*remove_iter)->natpmp_mapper->close(); remove_iter = m_listen_sockets.erase(remove_iter); } @@ -1879,9 +1880,11 @@ namespace aux { TORRENT_ASSERT((s->incoming == duplex::accept_incoming) == bool(s->sock)); if (s->sock) async_accept(s->sock, s->ssl); + if (m_settings.get_bool(settings_pack::enable_natpmp)) + start_natpmp(*s); // since this is a new socket it needs to map ports // even if the caller did not request re-mapping - if (!map_ports) remap_ports(remap_natpmp_and_upnp, *s); + if (!map_ports) remap_ports(remap_upnp, *s); } } @@ -2126,10 +2129,10 @@ namespace aux { tcp::endpoint const tcp_ep = s.sock ? s.sock->local_endpoint() : tcp::endpoint(); udp::endpoint const udp_ep = s.udp_sock ? s.udp_sock->sock.local_endpoint() : udp::endpoint(); - if ((mask & remap_natpmp) && m_natpmp) + if ((mask & remap_natpmp) && s.natpmp_mapper) { - map_port(*m_natpmp, portmap_protocol::tcp, tcp_ep, s.tcp_port_mapping[0]); - map_port(*m_natpmp, portmap_protocol::udp, to_tcp(udp_ep), s.udp_port_mapping[0]); + map_port(*s.natpmp_mapper, portmap_protocol::tcp, tcp_ep, s.tcp_port_mapping[0]); + map_port(*s.natpmp_mapper, portmap_protocol::udp, to_tcp(udp_ep), s.udp_port_mapping[0]); } if ((mask & remap_upnp) && m_upnp) { @@ -5450,6 +5453,23 @@ namespace aux { m_alerts.emplace_alert(t->get_handle(), peer); } + void session_impl::start_natpmp(aux::listen_socket_t& s) + { + // don't create mappings for local IPv6 addresses + // they can't be reached from outside of the local network anyways + if (is_v6(s.local_endpoint) && is_local(s.local_endpoint.address())) + return; + + if (!s.natpmp_mapper) + { + // the natpmp constructor may fail and call the callbacks + // into the session_impl. + s.natpmp_mapper = std::make_shared(m_io_service, *this); + s.natpmp_mapper->start(s.local_endpoint.address(), s.device); + } + remap_ports(remap_natpmp, s); + } + namespace { bool find_tcp_port_mapping(portmap_transport const transport , port_mapping_t mapping, std::shared_ptr const& ls) @@ -6646,22 +6666,11 @@ namespace aux { m_alerts.emplace_alert(ec); } - natpmp* session_impl::start_natpmp() + void session_impl::start_natpmp() { INVARIANT_CHECK; - - if (m_natpmp) return m_natpmp.get(); - - // the natpmp constructor may fail and call the callbacks - // into the session_impl. - m_natpmp = std::make_shared(m_io_service, *this); - m_natpmp->start(); - for (auto& s : m_listen_sockets) - { - remap_ports(remap_natpmp, *s); - } - return m_natpmp.get(); + start_natpmp(*s); } upnp* session_impl::start_upnp() @@ -6694,15 +6703,21 @@ namespace aux { port_mapping_t ret{-1}; if (m_upnp) ret = m_upnp->add_mapping(t, external_port , tcp::endpoint({}, static_cast(local_port))); - if (m_natpmp) ret = m_natpmp->add_mapping(t, external_port - , tcp::endpoint({}, static_cast(local_port))); + for (auto& s : m_listen_sockets) + { + if (s->natpmp_mapper) ret = s->natpmp_mapper->add_mapping(t, external_port + , tcp::endpoint({}, static_cast(local_port))); + } return ret; } void session_impl::delete_port_mapping(port_mapping_t handle) { if (m_upnp) m_upnp->delete_mapping(handle); - if (m_natpmp) m_natpmp->delete_mapping(handle); + for (auto& s : m_listen_sockets) + { + if (s->natpmp_mapper) s->natpmp_mapper->delete_mapping(handle); + } } void session_impl::stop_ip_notifier() @@ -6722,16 +6737,14 @@ namespace aux { void session_impl::stop_natpmp() { - if (!m_natpmp) return; - - m_natpmp->close(); for (auto& s : m_listen_sockets) { s->tcp_port_mapping[0] = port_mapping_t{-1}; s->udp_port_mapping[0] = port_mapping_t{-1}; + if (!s->natpmp_mapper) continue; + s->natpmp_mapper->close(); + s->natpmp_mapper.reset(); } - - m_natpmp.reset(); } void session_impl::stop_upnp() diff --git a/test/enum_if.cpp b/test/enum_if.cpp index a8a5dbf62..701d1975e 100644 --- a/test/enum_if.cpp +++ b/test/enum_if.cpp @@ -42,7 +42,7 @@ int main() io_service ios; error_code ec; - address def_gw = get_default_gateway(ios, ec); + address def_gw = get_default_gateway(ios, "", false, ec); if (ec) { std::printf("%s\n", ec.message().c_str());