diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp index 80cbca3ba..30c7d378c 100644 --- a/include/libtorrent/config.hpp +++ b/include/libtorrent/config.hpp @@ -205,6 +205,7 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_HAVE_MMAP 1 #define TORRENT_USE_NETLINK 1 +#define TORRENT_USE_IFADDRS 0 #define TORRENT_USE_IFCONF 1 #define TORRENT_HAS_SALEN 0 #define TORRENT_USE_FDATASYNC 1 @@ -214,10 +215,8 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_ANDROID #define TORRENT_HAS_FALLOCATE 0 #define TORRENT_USE_ICONV 0 -#define TORRENT_USE_IFADDRS 0 #define TORRENT_USE_MEMALIGN 1 #else // ANDROID -#define TORRENT_USE_IFADDRS 1 #define TORRENT_USE_POSIX_MEMALIGN 1 // posix_fallocate() is available under this condition diff --git a/src/enum_net.cpp b/src/enum_net.cpp index 01c994f0c..92f6c301b 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -122,6 +122,7 @@ namespace libtorrent {namespace { } #endif +#if !TORRENT_USE_NETLINK int sockaddr_len(sockaddr const* sin) { #if TORRENT_HAS_SALEN @@ -148,6 +149,7 @@ namespace libtorrent {namespace { #endif return address(); } +#endif bool valid_addr_family(int family) { @@ -160,7 +162,7 @@ namespace libtorrent {namespace { #if TORRENT_USE_NETLINK - int read_nl_sock(int sock, char *buf, int bufsize, std::uint32_t const seq, std::uint32_t const pid) + int read_nl_sock(int sock, span buf, std::uint32_t const seq, std::uint32_t const pid) { nlmsghdr* nl_hdr; @@ -168,10 +170,11 @@ namespace libtorrent {namespace { for (;;) { - int read_len = int(recv(sock, buf, std::size_t(bufsize - msg_len), 0)); + auto next_msg = buf.subspan(msg_len); + int read_len = int(recv(sock, next_msg.data(), next_msg.size(), 0)); if (read_len < 0) return -1; - nl_hdr = reinterpret_cast(buf); + nl_hdr = reinterpret_cast(next_msg.data()); #ifdef __clang__ #pragma clang diagnostic push @@ -191,7 +194,6 @@ namespace libtorrent {namespace { 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; @@ -199,6 +201,38 @@ namespace libtorrent {namespace { return msg_len; } + enum { NL_BUFSIZE = 8192 }; + + int nl_dump_request(int sock, int type, std::uint32_t seq, char family, span msg, std::size_t msg_len) + { + nlmsghdr* nl_msg = reinterpret_cast(msg.data()); + nl_msg->nlmsg_len = NLMSG_LENGTH(msg_len); + nl_msg->nlmsg_type = type; + nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + nl_msg->nlmsg_seq = seq; + // in theory nlmsg_pid should be set to the netlink port ID (NOT the process ID) + // of the sender, but the kernel ignores this field so it is typically set to + // zero + nl_msg->nlmsg_pid = 0; + // first byte of routing messages is always the family + msg[sizeof(nlmsghdr)] = family; + + if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) + { + return -1; + } + + // get the socket's port ID so that we can verify it in the repsonse + sockaddr_nl sock_addr; + socklen_t sock_addr_len = sizeof(sock_addr); + if (getsockname(sock, (sockaddr*)&sock_addr, &sock_addr_len) < 0) + { + return -1; + } + + return read_nl_sock(sock, msg, seq, sock_addr.nl_pid); + } + bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info) { rtmsg* rt_msg = reinterpret_cast(NLMSG_DATA(nl_hdr)); @@ -262,6 +296,129 @@ namespace libtorrent {namespace { // } return true; } + + int parse_nl_link(nlmsghdr* nl_hdr, ip_interface* link_info) + { + ifinfomsg* link_msg = reinterpret_cast(NLMSG_DATA(nl_hdr)); + + link_info->name[0] = 0; + link_info->mtu = 0; + + int rt_len = int(IFLA_PAYLOAD(nl_hdr)); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for (rtattr* rt_attr = reinterpret_cast(IFLA_RTA(link_msg)); + RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr, rt_len)) + { + switch(rt_attr->rta_type) + { + case IFLA_MTU: + link_info->mtu = int(*reinterpret_cast(RTA_DATA(rt_attr))); + break; + case IFLA_IFNAME: + strncpy(link_info->name, reinterpret_cast(RTA_DATA(rt_attr)), sizeof(link_info->name)); + break; + } + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + return link_msg->ifi_index; + } + + bool parse_nl_address(std::map const& link_info, nlmsghdr* nl_hdr, ip_interface* ip_info) + { + ifaddrmsg* addr_msg = reinterpret_cast(NLMSG_DATA(nl_hdr)); + + if (!valid_addr_family(addr_msg->ifa_family)) + return false; + + ip_info->preferred = (addr_msg->ifa_flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED | IFA_F_TENTATIVE)) == 0; + +#if TORRENT_USE_IPV6 + if (addr_msg->ifa_family == AF_INET6) + { + TORRENT_ASSERT(addr_msg->ifa_prefixlen <= 128); + if (addr_msg->ifa_prefixlen > 0) + { + address_v6::bytes_type mask = {}; + auto it = mask.begin(); + if (addr_msg->ifa_prefixlen > 64) + { + detail::write_uint64(0xffffffffffffffffULL, it); + addr_msg->ifa_prefixlen -= 64; + } + if (addr_msg->ifa_prefixlen > 0) + { + std::uint64_t const m = ~((1ULL << (64 - addr_msg->ifa_prefixlen)) - 1); + detail::write_uint64(m, it); + } + ip_info->netmask = address_v6(mask); + } + } + else +#endif + { + TORRENT_ASSERT(addr_msg->ifa_prefixlen <= 32); + if (addr_msg->ifa_prefixlen != 0) + { + std::uint32_t const m = ~((1U << (32 - addr_msg->ifa_prefixlen)) - 1); + ip_info->netmask = address_v4(m); + } + } + + // intiialize name to be empty + ip_info->name[0] = '\0'; + + int rt_len = int(IFA_PAYLOAD(nl_hdr)); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for (rtattr* rt_attr = reinterpret_cast(IFA_RTA(addr_msg)); + RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr, rt_len)) + { + switch(rt_attr->rta_type) + { + case IFA_ADDRESS: +#if TORRENT_USE_IPV6 + if (addr_msg->ifa_family == AF_INET6) + { + address_v6 addr = inaddr6_to_address(reinterpret_cast(RTA_DATA(rt_attr))); + if (addr_msg->ifa_scope == RT_SCOPE_LINK) + addr.scope_id(addr_msg->ifa_index); + ip_info->interface_address = addr; + } + else +#endif + { + ip_info->interface_address = inaddr_to_address(reinterpret_cast(RTA_DATA(rt_attr))); + } + break; + case IFA_LABEL: + strncpy(ip_info->name, reinterpret_cast(RTA_DATA(rt_attr)), sizeof(ip_info->name)); + break; + } + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + auto ifi_info = link_info.find(addr_msg->ifa_index); + if (ifi_info != link_info.end()) + { + ip_info->mtu = ifi_info->second.mtu; + // for some reason IPv6 entries don't include an IFA_LABEL attribute + // so get it from the link in that case + if (ip_info->name[0] == '\0') + strncpy(ip_info->name, ifi_info->second.name, sizeof(ip_info->name)); + } + + return true; + } #endif // TORRENT_USE_NETLINK #endif // !BUILD_SIMULATOR @@ -451,7 +608,73 @@ namespace libtorrent { wan.mtu = ios.sim().config().path_mtu(ip, ip); ret.push_back(wan); } +#elif TORRENT_USE_NETLINK + int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) + { + ec = error_code(errno, system_category()); + return ret; + } + std::uint32_t seq = 0; + std::map link_info; + + { + char msg[NL_BUFSIZE] = {}; + nlmsghdr* nl_msg = reinterpret_cast(msg); + int len = nl_dump_request(sock, RTM_GETLINK, seq++, AF_UNSPEC, msg, sizeof(ifinfomsg)); + if (len < 0) + { + ec = error_code(errno, system_category()); + close(sock); + return ret; + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" + // NLMSG_OK uses signed/unsigned compare in the same expression +#pragma clang diagnostic ignored "-Wsign-compare" +#endif + for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) + { + ip_interface iface; + int ifi_index = parse_nl_link(nl_msg, &iface); + if (ifi_index >= 0) link_info.emplace(ifi_index, iface); + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + { + char msg[NL_BUFSIZE] = {}; + nlmsghdr* nl_msg = reinterpret_cast(msg); + int len = nl_dump_request(sock, RTM_GETADDR, seq++, AF_PACKET, msg, sizeof(ifaddrmsg)); + if (len < 0) + { + ec = error_code(errno, system_category()); + close(sock); + return ret; + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" + // NLMSG_OK uses signed/unsigned compare in the same expression +#pragma clang diagnostic ignored "-Wsign-compare" +#endif + for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) + { + ip_interface iface; + if (parse_nl_address(link_info, nl_msg, &iface)) ret.push_back(iface); + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + close(sock); #elif TORRENT_USE_IFADDRS int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) @@ -1039,8 +1262,6 @@ namespace libtorrent { // Free memory free(routes); #elif TORRENT_USE_NETLINK - enum { BUFSIZE = 8192 }; - int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); if (sock < 0) { @@ -1050,36 +1271,9 @@ namespace libtorrent { std::uint32_t seq = 0; - char msg[BUFSIZE] = {}; + char msg[NL_BUFSIZE] = {}; nlmsghdr* nl_msg = reinterpret_cast(msg); - - nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); - nl_msg->nlmsg_type = RTM_GETROUTE; - nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; - nl_msg->nlmsg_seq = seq; - // in theory nlmsg_pid should be set to the netlink port ID (NOT the process ID) - // of the sender, but the kernel ignores this field so it is typically set to - // zero - nl_msg->nlmsg_pid = 0; - - if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) - { - ec = error_code(errno, system_category()); - close(sock); - return std::vector(); - } - - // get the socket's port ID so that we can verify it in the repsonse - sockaddr_nl sock_addr; - socklen_t sock_addr_len = sizeof(sock_addr); - if (getsockname(sock, (sockaddr*)&sock_addr, &sock_addr_len) < 0) - { - ec = error_code(errno, system_category()); - close(sock); - return std::vector(); - } - - int len = read_nl_sock(sock, msg, BUFSIZE, seq, sock_addr.nl_pid); + int len = nl_dump_request(sock, RTM_GETROUTE, seq++, AF_UNSPEC, msg, sizeof(rtmsg)); if (len < 0) { ec = error_code(errno, system_category()); @@ -1087,10 +1281,6 @@ namespace libtorrent { return std::vector(); } - // seq should be incremented between requests so do it here - // just in case someone adds another send below - ++seq; - int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) {