diff --git a/examples/enum_if.cpp b/examples/enum_if.cpp index 905f12835..0c60bc9ab 100644 --- a/examples/enum_if.cpp +++ b/examples/enum_if.cpp @@ -11,22 +11,23 @@ int main() asio::error_code ec; std::vector const& net = enum_net_interfaces(ios, ec); + address local = guess_local_address(ios); + std::cout << "Local address: " << local << std::endl; + + address gateway = get_default_gateway(ios, local, ec); + std::cout << "Default gateway: " << gateway << std::endl; + std::cout << "================\n"; + + std::cout << "interface\tnetmask \tflags\n"; for (std::vector::const_iterator i = net.begin() , end(net.end()); i != end; ++i) { - std::cout << "address: " << i->interface_address << std::endl - << " mask: " << i->netmask << std::endl - << " flags: "; + std::cout << i->interface_address << "\t" << i->netmask << "\t"; if (is_multicast(i->interface_address)) std::cout << "multicast "; if (is_local(i->interface_address)) std::cout << "local "; if (is_loopback(i->interface_address)) std::cout << "loopback "; std::cout << std::endl; } - address local = guess_local_address(ios); - std::cout << "Local address: " << local << std::endl; - - address gateway = get_default_gateway(ios, local, ec); - std::cout << "Default gateway: " << gateway << std::endl; } diff --git a/src/enum_net.cpp b/src/enum_net.cpp index ff3c108c3..9b0652809 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -31,8 +31,8 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "libtorrent/config.hpp" - -#if defined TORRENT_BSD || defined TORRENT_LINUX + +#if defined TORRENT_BSD #include #include #include @@ -44,6 +44,87 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include #include +#elif defined TORRENT_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct route_info +{ + u_int dst_addr; + u_int src_addr; + u_int gateway; + char if_name[IF_NAMESIZE]; +}; + +int read_nl_sock(int sock, char *buf, int bufsize, int seq, int pid) +{ + nlmsghdr* nl_hdr; + + int msg_len = 0; + + do + { + int read_len = recv(sock, buf, bufsize - msg_len, 0); + if (read_len < 0) return -1; + + nl_hdr = (nlmsghdr*)buf; + + if ((NLMSG_OK(nl_hdr, read_len) == 0) || (nl_hdr->nlmsg_type == NLMSG_ERROR)) + return -1; + + if (nl_hdr->nlmsg_type == NLMSG_DONE) break; + + buf += read_len; + msg_len += read_len; + + if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) break; + + } while((nl_hdr->nlmsg_seq != seq) || (nl_hdr->nlmsg_pid != pid)); + return msg_len; +} + +// For parsing the route info returned +void parse_route(nlmsghdr *nl_hdr, route_info *rt_info) +{ + rtmsg* rt_msg = (rtmsg*)NLMSG_DATA(nl_hdr); + + if((rt_msg->rtm_family != AF_INET) || (rt_msg->rtm_table != RT_TABLE_MAIN)) + return; + + int rt_len = RTM_PAYLOAD(nl_hdr); + for (rtattr* rt_attr = (rtattr*)RTM_RTA(rt_msg); + RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) + { + switch(rt_attr->rta_type) + { + case RTA_OIF: + if_indextoname(*(int*)RTA_DATA(rt_attr), rt_info->if_name); + break; + case RTA_GATEWAY: + rt_info->gateway = *(u_int*)RTA_DATA(rt_attr); + break; + case RTA_PREFSRC: + rt_info->src_addr = *(u_int*)RTA_DATA(rt_attr); + break; + case RTA_DST: + rt_info->dst_addr = *(u_int*)RTA_DATA(rt_attr); + break; + } + } + return; +} #endif #include "libtorrent/enum_net.hpp" @@ -54,22 +135,28 @@ namespace libtorrent { namespace { + address inaddr_to_address(in_addr const* ina) + { + typedef asio::ip::address_v4::bytes_type bytes_t; + bytes_t b; + memcpy(&b[0], ina, b.size()); + return address_v4(b); + } + + address inaddr6_to_address(in6_addr const* ina6) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t b; + memcpy(&b[0], ina6, b.size()); + return address_v6(b); + } + address sockaddr_to_address(sockaddr const* sin) { if (sin->sa_family == AF_INET) - { - typedef asio::ip::address_v4::bytes_type bytes_t; - bytes_t b; - memcpy(&b[0], &((sockaddr_in const*)sin)->sin_addr, b.size()); - return address_v4(b); - } + return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr); else if (sin->sa_family == AF_INET6) - { - typedef asio::ip::address_v6::bytes_type bytes_t; - bytes_t b; - memcpy(&b[0], &((sockaddr_in6 const*)sin)->sin6_addr, b.size()); - return address_v6(b); - } + return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr); return address(); } @@ -394,8 +481,61 @@ namespace libtorrent return ret; -//#elif defined TORRENT_LINUX -// No linux implementation yet +#elif defined TORRENT_LINUX + + enum { BUFSIZE = 8192 }; + + int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) + { + ec = asio::error_code(errno, asio::error::system_category); + return address_v4::any(); + } + + int seq = 0; + + char msg[BUFSIZE]; + memset(msg, 0, BUFSIZE); + nlmsghdr* nl_msg = (nlmsghdr*)msg; + + nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nl_msg->nlmsg_type = RTM_GETROUTE; + nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + nl_msg->nlmsg_seq = seq++; + nl_msg->nlmsg_pid = getpid(); + + if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) + { + ec = asio::error_code(errno, asio::error::system_category); + close(sock); + return address_v4::any(); + } + + int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid()); + if (len < 0) + { + ec = asio::error_code(errno, asio::error::system_category); + close(sock); + return address_v4::any(); + } + + route_info rt_info; + for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) + { + memset(&rt_info, 0, sizeof(route_info)); + parse_route(nl_msg, &rt_info); + + if (rt_info.dst_addr == 0) + { + in_addr addr; + addr.s_addr = rt_info.gateway; + close(sock); + return inaddr_to_address(&addr); + } + } + close(sock); + return address_v4::any(); + #else if (!interface.is_v4()) {