proper default gateway discovery

This commit is contained in:
Arvid Norberg 2008-01-11 09:38:05 +00:00
parent 9ade71a5fb
commit f36e6a6c4f
4 changed files with 146 additions and 57 deletions

View File

@ -21,8 +21,6 @@ int main()
if (is_local(i->interface_address)) std::cout << "local "; if (is_local(i->interface_address)) std::cout << "local ";
if (is_loopback(i->interface_address)) std::cout << "loopback "; if (is_loopback(i->interface_address)) std::cout << "loopback ";
std::cout << std::endl; std::cout << std::endl;
std::cout << " router: " << router_for_interface(i->interface_address, ec);
std::cout << std::endl;
} }
address local = guess_local_address(ios); address local = guess_local_address(ios);

View File

@ -55,9 +55,11 @@ namespace libtorrent
// returns true if the specified address is on the same // returns true if the specified address is on the same
// local network as us // local network as us
bool in_local_network(asio::io_service& ios, address const& addr, asio::error_code& ec); bool in_local_network(asio::io_service& ios, address const& addr
, asio::error_code& ec);
address router_for_interface(address const interface, asio::error_code& ec); address get_default_gateway(asio::io_service& ios, address const& addr
, asio::error_code& ec);
} }
#endif #endif

View File

@ -30,10 +30,12 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
#if defined __linux__ || defined BSD #if defined __linux__ || (defined __APPLE__ && __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \
|| defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <net/if.h> #include <net/if.h>
#include <net/route.h>
#elif defined WIN32 #elif defined WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
@ -67,6 +69,14 @@ namespace libtorrent
} }
return address(); return address();
} }
bool verify_sockaddr(sockaddr_in* sin)
{
return (sin->sin_len == sizeof(sockaddr_in)
&& sin->sin_family == AF_INET)
|| (sin->sin_len == sizeof(sockaddr_in6)
&& sin->sin_family == AF_INET6);
}
} }
bool in_subnet(address const& addr, ip_interface const& iface) bool in_subnet(address const& addr, ip_interface const& iface)
@ -213,15 +223,117 @@ namespace libtorrent
return ret; return ret;
} }
address router_for_interface(address const interface, asio::error_code& ec) address get_default_gateway(asio::io_service& ios, address const& interface, asio::error_code& ec)
{ {
#ifdef WIN32
#if defined __linux__ || (defined __APPLE__ && __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \
|| defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__
struct rt_msg
{
rt_msghdr m_rtm;
char buf[512];
};
rt_msg m;
int len = sizeof(rt_msg);
bzero(&m, len);
m.m_rtm.rtm_type = RTM_GET;
m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
m.m_rtm.rtm_version = RTM_VERSION;
m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
m.m_rtm.rtm_seq = 0;
m.m_rtm.rtm_msglen = len;
int s = socket(PF_ROUTE, SOCK_RAW, AF_INET);
if (s == -1)
{
ec = asio::error_code(errno, asio::error::system_category);
return address_v4::any();
}
int n = write(s, &m, len);
if (n == -1)
{
ec = asio::error_code(errno, asio::error::system_category);
close(s);
return address_v4::any();
}
else if (n != len)
{
ec = asio::error::operation_not_supported;
close(s);
return address_v4::any();
}
bzero(&m, len);
n = read(s, &m, len);
if (n == -1)
{
ec = asio::error_code(errno, asio::error::system_category);
close(s);
return address_v4::any();
}
close(s);
TORRENT_ASSERT(m.m_rtm.rtm_seq == 0);
TORRENT_ASSERT(m.m_rtm.rtm_pid == getpid());
if (m.m_rtm.rtm_errno)
{
ec = asio::error_code(m.m_rtm.rtm_errno, asio::error::system_category);
return address_v4::any();
}
if (m.m_rtm.rtm_flags & RTF_UP == 0
|| m.m_rtm.rtm_flags & RTF_GATEWAY == 0)
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
if (m.m_rtm.rtm_addrs & RTA_DST == 0
|| m.m_rtm.rtm_addrs & RTA_GATEWAY == 0)
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
if (m.m_rtm.rtm_msglen > len)
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
int min_len = sizeof(rt_msghdr) + 2 * sizeof(struct sockaddr_in);
if (m.m_rtm.rtm_msglen < min_len)
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
// default route
char* p = m.buf;
sockaddr_in* sin = (sockaddr_in*)p;
if (!verify_sockaddr(sin))
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
// default gateway
p += sin->sin_len;
sin = (sockaddr_in*)p;
if (!verify_sockaddr(sin))
{
ec = asio::error::operation_not_supported;
return address_v4::any();
}
return sockaddr_to_address((sockaddr*)sin);
#elif defined WIN32
// Load Iphlpapi library // Load Iphlpapi library
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
if (!iphlp) if (!iphlp)
{ {
ec = asio::error::fault; ec = asio::error::operation_not_supported;
return address_v4::any(); return address_v4::any();
} }
@ -231,7 +343,7 @@ namespace libtorrent
if (!GetAdaptersInfo) if (!GetAdaptersInfo)
{ {
FreeLibrary(iphlp); FreeLibrary(iphlp);
ec = asio::error::fault; ec = asio::error::operation_not_supported;
return address_v4::any(); return address_v4::any();
} }
@ -240,7 +352,7 @@ namespace libtorrent
if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW)
{ {
FreeLibrary(iphlp); FreeLibrary(iphlp);
ec = asio::error::fault; ec = asio::error::operation_not_supported;
return address_v4::any(); return address_v4::any();
} }
@ -248,22 +360,29 @@ namespace libtorrent
if (!adapter_info) if (!adapter_info)
{ {
FreeLibrary(iphlp); FreeLibrary(iphlp);
ec = asio::error::fault; ec = asio::error::no_memory;
return address_v4::any(); return address_v4::any();
} }
address ret; address ret;
if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR)
{ {
PIP_ADAPTER_INFO adapter = adapter_info;
while (adapter != 0) for (PIP_ADAPTER_INFO adapter = adapter_info;
adapter != 0; adapter = adapter->Next)
{ {
if (interface == address::from_string(adapter->IpAddressList.IpAddress.String, ec)) address iface = address::from_string(adapter->IpAddressList.IpAddress.String, ec);
if (ec)
{
ec = asio::error_code();
continue;
}
if (is_loopback(iface) || is_any(iface)) continue;
if (interface == address() || interface == iface)
{ {
ret = address::from_string(adapter->GatewayList.IpAddress.String, ec); ret = address::from_string(adapter->GatewayList.IpAddress.String, ec);
break; break;
} }
adapter = adapter->Next;
} }
} }
@ -274,10 +393,11 @@ namespace libtorrent
return ret; return ret;
#else #else
// TODO: temporary implementation address interface = guess_local_address(ios);
if (!interface.is_v4()) if (!interface.is_v4())
{ {
ec = asio::error::fault; ec = asio::error::operation_not_supported;
return address_v4::any(); return address_v4::any();
} }
return address_v4((interface.to_v4().to_ulong() & 0xffffff00) | 1); return address_v4((interface.to_v4().to_ulong() & 0xffffff00) | 1);

View File

@ -45,13 +45,6 @@ using namespace libtorrent;
enum { num_mappings = 2 }; enum { num_mappings = 2 };
namespace libtorrent
{
// defined in upnp.cpp
bool is_local(address const& a);
address guess_local_address(asio::io_service&);
}
natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb) natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb)
: m_callback(cb) : m_callback(cb)
, m_currently_mapping(-1) , m_currently_mapping(-1)
@ -72,51 +65,27 @@ natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callbac
void natpmp::rebind(address const& listen_interface) void natpmp::rebind(address const& listen_interface)
{ {
address local = address_v4::any(); asio::error_code ec;
if (listen_interface != address_v4::any()) address gateway = get_default_gateway(m_socket.get_io_service(), listen_interface, ec);
if (ec)
{ {
local = listen_interface;
}
else
{
local = guess_local_address(m_socket.io_service());
if (local == address_v4::any())
{
disable("local host is probably not on a NATed "
"network. disabling NAT-PMP");
return;
}
}
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string() m_log << time_now_string() << " failed to find default router: "
<< " local ip: " << local.to_string() << std::endl; << ec.message() << std::endl;
#endif #endif
disable("failed to find default router");
if (!is_local(local))
{
// the local address seems to be an external
// internet address. Assume it is not behind a NAT
disable("local IP is not on a local network");
return; return;
} }
m_disabled = false; m_disabled = false;
asio::error_code ec; udp::endpoint nat_endpoint(gateway, 5351);
udp::endpoint nat_endpoint(router_for_interface(local, ec), 5351);
if (ec)
{
disable("cannot retrieve router address");
return;
}
if (nat_endpoint == m_nat_endpoint) return; if (nat_endpoint == m_nat_endpoint) return;
m_nat_endpoint = nat_endpoint; m_nat_endpoint = nat_endpoint;
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl; m_log << time_now_string() << " found router at: "
<< m_nat_endpoint.address() << std::endl;
#endif #endif
m_socket.open(udp::v4(), ec); m_socket.open(udp::v4(), ec);