From 35e65d018cb8e3aead6d1a21494611368bb981c8 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sun, 30 Apr 2017 12:32:16 -0700 Subject: [PATCH] Move routing related functions into their own header file --- include/tins/utils.h | 103 +------ include/tins/utils/routing_utils.h | 148 ++++++++++ src/CMakeLists.txt | 1 + src/network_interface.cpp | 2 +- src/utils.cpp | 370 +------------------------ src/utils/routing_utils.cpp | 429 +++++++++++++++++++++++++++++ 6 files changed, 581 insertions(+), 472 deletions(-) create mode 100644 include/tins/utils/routing_utils.h create mode 100644 src/utils/routing_utils.cpp diff --git a/include/tins/utils.h b/include/tins/utils.h index f445af3..27d2128 100644 --- a/include/tins/utils.h +++ b/include/tins/utils.h @@ -32,8 +32,6 @@ #include "macros.h" #include -#include -#include #include #include "ip_address.h" #include "ipv6_address.h" @@ -41,6 +39,7 @@ #include "detail/type_traits.h" #include "utils/checksum_utils.h" #include "utils/frequency_utils.h" +#include "utils/routing_utils.h" // Fix for Windows interface define on combaseapi.h #undef interface @@ -63,66 +62,6 @@ class HWAddress; */ namespace Utils { -/** - * Struct that represents an entry the routing table - */ -struct RouteEntry { - /** - * This interface's name. - */ - std::string interface; - - /** - * This route entry's destination. - */ - IPv4Address destination; - - /** - * This route entry's gateway. - */ - IPv4Address gateway; - - /** - * This route entry's subnet mask. - */ - IPv4Address mask; - - /** - * This route entry's metric. - */ - int metric; -}; - -/** - * Struct that represents an entry the IPv6 routing table - */ -struct Route6Entry { - /** - * This interface's name. - */ - std::string interface; - - /** - * This route entry's destination. - */ - IPv6Address destination; - - /** - * This route entry's subnet mask. - */ - IPv6Address mask; - - /** - * This route entry's next hop. - */ - IPv6Address gateway; - - /** - * This route entry's metric. - */ - int metric; -}; - /** * \brief Resolves a domain name and returns its corresponding ip address. * @@ -173,14 +112,6 @@ TINS_API HWAddress<6> resolve_hwaddr(const NetworkInterface& iface, */ TINS_API HWAddress<6> resolve_hwaddr(IPv4Address ip, PacketSender& sender); -/** \brief List all network interfaces. - * - * Returns a set of strings, each of them representing the name - * of a network interface. These names can be used as the input - * interface for Utils::interface_ip, Utils::interface_hwaddr, etc. - */ -TINS_API std::set network_interfaces(); - /** * \brief Finds the gateway's IP address for the given IP * address. @@ -194,29 +125,6 @@ TINS_API std::set network_interfaces(); */ TINS_API bool gateway_from_ip(IPv4Address ip, IPv4Address& gw_addr); - -/** - * \brief Retrieves entries in the routing table. - * - * \brief output ForwardIterator in which entries will be stored. - */ -template -void route_entries(ForwardIterator output); - -/** - * \brief Retrieves entries in the routing table. - * - * \return a vector which contains all of the route entries. - */ -TINS_API std::vector route_entries(); - -/** - * \brief Retrieves entries in the routing table. - * - * \return a vector which contains all of the route entries. - */ -TINS_API std::vector route6_entries(); - /** * \brief Converts a PDUType to a string. * \param pduType The PDUType to be converted. @@ -259,13 +167,4 @@ dereference_until_pdu(T& value) { } // Utils } // Tins -template -void Tins::Utils::route_entries(ForwardIterator output) { - std::vector entries = route_entries(); - for (size_t i = 0; i < entries.size(); ++i) { - *output = entries[i]; - ++output; - } -} - #endif // TINS_UTILS_H diff --git a/include/tins/utils/routing_utils.h b/include/tins/utils/routing_utils.h new file mode 100644 index 0000000..71a5863 --- /dev/null +++ b/include/tins/utils/routing_utils.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TINS_ROUTING_UTILS_H +#define TINS_ROUTING_UTILS_H + +#include +#include +#include "../macros.h" +#include "../ip_address.h" +#include "../ipv6_address.h" + +// Fix for Windows interface define on combaseapi.h +#undef interface + +namespace Tins { +namespace Utils { + +/** + * Struct that represents an entry the routing table + */ +struct RouteEntry { + /** + * This interface's name. + */ + std::string interface; + + /** + * This route entry's destination. + */ + IPv4Address destination; + + /** + * This route entry's gateway. + */ + IPv4Address gateway; + + /** + * This route entry's subnet mask. + */ + IPv4Address mask; + + /** + * This route entry's metric. + */ + int metric; +}; + +/** + * Struct that represents an entry the IPv6 routing table + */ +struct Route6Entry { + /** + * This interface's name. + */ + std::string interface; + + /** + * This route entry's destination. + */ + IPv6Address destination; + + /** + * This route entry's subnet mask. + */ + IPv6Address mask; + + /** + * This route entry's next hop. + */ + IPv6Address gateway; + + /** + * This route entry's metric. + */ + int metric; +}; + +/** + * \brief Retrieves entries in the routing table. + * + * \brief output ForwardIterator in which entries will be stored. + */ +template +void route_entries(ForwardIterator output); + +/** + * \brief Retrieves entries in the routing table. + * + * \return a vector which contains all of the route entries. + */ +TINS_API std::vector route_entries(); + +/** + * \brief Retrieves entries in the routing table. + * + * \return a vector which contains all of the route entries. + */ +TINS_API std::vector route6_entries(); + +/** + * \brief List all network interfaces. + * + * Returns a set of strings, each of them representing the name + * of a network interface. These names can be used as the input + * interface for Utils::interface_ip, Utils::interface_hwaddr, etc. + */ +TINS_API std::set network_interfaces(); + +} // Utils +} // Tins + +template +void Tins::Utils::route_entries(ForwardIterator output) { + std::vector entries = route_entries(); + for (size_t i = 0; i < entries.size(); ++i) { + *output = entries[i]; + ++output; + } +} + +#endif // TINS_ROUTING_UTILS_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ba788f..9e709e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,6 +74,7 @@ set(SOURCES dot11/dot11_control.cpp utils/checksum_utils.cpp utils/frequency_utils.cpp + utils/routing_utils.cpp ) SET(PCAP_DEPENDENT_SOURCES diff --git a/src/network_interface.cpp b/src/network_interface.cpp index 47c75cc..cd191be 100644 --- a/src/network_interface.cpp +++ b/src/network_interface.cpp @@ -31,7 +31,6 @@ #include #include #include "macros.h" -#include "utils.h" #ifndef _WIN32 #include #if defined(BSD) || defined(__FreeBSD_kernel__) @@ -52,6 +51,7 @@ #include "network_interface.h" #include "endianness.h" #include "exceptions.h" +#include "utils/routing_utils.h" using std::string; using std::wstring; diff --git a/src/utils.cpp b/src/utils.cpp index 413d713..9940c91 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -28,10 +28,7 @@ */ #include -#include -#include #include -#include #include "macros.h" #ifndef _WIN32 #if defined(BSD) || defined(__FreeBSD_kernel__) @@ -59,22 +56,19 @@ #undef interface #endif #include "utils.h" -#include "pdu.h" #include "arp.h" #include "ethernetII.h" -#include "endianness.h" #include "network_interface.h" #include "packet_sender.h" #include "cxxstd.h" #include "hw_address.h" #include "memory_helpers.h" -#include "internals.h" +#include "detail/smart_ptr.h" #include "detail/smart_ptr.h" using std::string; using std::istream; using std::set; -using std::ifstream; using std::vector; using std::back_inserter; using std::runtime_error; @@ -84,72 +78,6 @@ using Tins::Memory::OutputMemoryStream; /** \cond */ -bool from_hex(const string& str, uint32_t& result) { - size_t i = 0; - result = 0; - while (i < str.size()) { - uint8_t tmp; - if (str[i] >= 'A' && str[i] <= 'F') { - tmp = (str[i] - 'A' + 10); - } - else if (str[i] >= '0' && str[i] <= '9') { - tmp = (str[i] - '0'); - } - else { - return false; - } - result = (result << 4) | tmp; - i++; - } - return true; -} - -bool from_hex(const string& str, string& result) { - result.clear(); - for (size_t i = 0; i < str.size(); i+= 2) { - uint8_t value = 0; - for (size_t j = i; j < i + 2 && j < str.size(); ++j) { - if (str[j] >= 'A' && str[j] <= 'F') { - value = (value << 4) | (str[j] - 'A' + 10); - } - else if (str[j] >= 'a' && str[j] <= 'f') { - value = (value << 4) | (str[j] - 'a' + 10); - } - else if (str[j] >= '0' && str[j] <= '9') { - value = (value << 4) | (str[j] - '0'); - } - else { - return false; - } - } - result.push_back(value); - } - return true; -} - -void skip_line(istream& input) { - int c = 0; - while (c != '\n' && input) { - c = input.get(); - } -} - -struct InterfaceCollector { - set ifaces; - - #ifdef _WIN32 - bool operator() (PIP_ADAPTER_ADDRESSES addr) { - ifaces.insert(addr->AdapterName); - return false; - } - #else - bool operator() (struct ifaddrs* addr) { - ifaces.insert(addr->ifa_name); - return false; - } - #endif -}; - addrinfo* resolve_domain(const string& to_resolve, int family) { addrinfo* result, hints = addrinfo(); hints.ai_socktype = SOCK_STREAM; @@ -163,47 +91,6 @@ addrinfo* resolve_domain(const string& to_resolve, int family) { } } -#if defined(BSD) || defined(__FreeBSD_kernel__) -vector query_route_table(int family) { - int mib[6]; - vector buf; - size_t len; - - mib[0] = CTL_NET; - mib[1] = AF_ROUTE; - mib[2] = 0; - mib[3] = family; - mib[4] = NET_RT_DUMP; - mib[5] = 0; - if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - throw runtime_error("sysctl failed"); - } - - buf.resize(len); - if (sysctl(mib, 6, &buf[0], &len, NULL, 0) < 0) { - throw runtime_error("sysctl failed"); - } - - return buf; -} - -void parse_header(struct rt_msghdr* rtm, vector& addrs) { - char* ptr = (char *)(rtm + 1); - // Iterate from RTA_DST (0) to RTA_NETMASK (2) - for (int i = 0; i < 3; ++i) { - sockaddr* sa = 0; - if ((rtm->rtm_addrs & (1 << i)) != 0) { - sa = (struct sockaddr *)ptr; - ptr += sa->sa_len; - if (sa->sa_family == 0) { - sa = 0; - } - } - addrs[i] = sa; - } -} -#endif - namespace Tins { /** \endcond */ @@ -256,230 +143,6 @@ HWAddress<6> resolve_hwaddr(IPv4Address ip, PacketSender& sender) { return resolve_hwaddr(sender.default_interface(), ip, sender); } -#if defined(BSD) || defined(__FreeBSD_kernel__) - -vector route_entries() { - vector output; - vector buffer = query_route_table(AF_INET); - char* next = &buffer[0], *end = &buffer[buffer.size()]; - rt_msghdr* rtm; - vector sa(32); - char iface_name[IF_NAMESIZE]; - while (next < end) { - rtm = (rt_msghdr*)next; - // Filter: - // * RTF_STATIC (only manually added routes) - if ((rtm->rtm_flags & (RTF_STATIC)) != 0) { - parse_header(rtm, sa); - if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { - RouteEntry entry; - entry.destination = IPv4Address(((struct sockaddr_in *)sa[RTAX_DST])->sin_addr.s_addr); - entry.gateway = IPv4Address(((struct sockaddr_in *)sa[RTAX_GATEWAY])->sin_addr.s_addr); - if (sa[RTAX_NETMASK]) { - entry.mask = IPv4Address(((struct sockaddr_in *)sa[RTAX_NETMASK])->sin_addr.s_addr); - } - entry.interface = iface_name; - entry.metric = 0; - output.push_back(entry); - } - } - next += rtm->rtm_msglen; - } - return output; -} - -vector route6_entries() { - vector output; - vector buffer = query_route_table(AF_INET6); - char* next = &buffer[0], *end = &buffer[buffer.size()]; - rt_msghdr* rtm; - vector sa(9); - char iface_name[IF_NAMESIZE]; - while (next < end) { - rtm = (rt_msghdr*)next; - // Filter protocol-cloned entries - bool process_entry = true; - // These were removed in recent versions of FreeBSD - #if defined(RTF_WASCLONED) && defined(RTF_PRCLONING) - process_entry = (rtm->rtm_flags & RTF_WASCLONED) == 0 || - (rtm->rtm_flags & RTF_PRCLONING) == 0; - #endif - if (process_entry) { - parse_header(rtm, sa); - if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { - Route6Entry entry; - entry.destination = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_DST])->sin6_addr.s6_addr); - entry.gateway = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_GATEWAY])->sin6_addr.s6_addr); - int prefix_length = 0; - if (sa[RTAX_NETMASK]) { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa[RTAX_NETMASK]; - for (size_t i = 0; i < 16; ++i) { - uint8_t this_byte = sin->sin6_addr.s6_addr[i]; - // Stop when we find a zero byte - if (this_byte == 0) { - break; - } - switch (this_byte) { - case 0xff: - prefix_length += 8; - break; - case 0xfe: - prefix_length += 7; - break; - case 0xfc: - prefix_length += 6; - break; - case 0xf8: - prefix_length += 5; - break; - case 0xf0: - prefix_length += 4; - break; - case 0xe0: - prefix_length += 3; - break; - case 0xc0: - prefix_length += 2; - break; - case 0x80: - prefix_length += 1; - break; - default: - break; - } - } - } - entry.mask = IPv6Address::from_prefix_length(prefix_length); - entry.interface = iface_name; - entry.metric = 0; - output.push_back(entry); - } - } - next += rtm->rtm_msglen; - } - return output; -} - -#elif defined(_WIN32) - -vector route_entries() { - vector output; - MIB_IPFORWARDTABLE* table; - ULONG size = 0; - GetIpForwardTable(0, &size, 0); - vector buffer(size); - table = (MIB_IPFORWARDTABLE*)&buffer[0]; - GetIpForwardTable(table, &size, 0); - - for (DWORD i = 0; i < table->dwNumEntries; i++) { - MIB_IPFORWARDROW* row = &table->table[i]; - if (row->dwForwardType == MIB_IPROUTE_TYPE_INDIRECT || - row->dwForwardType == MIB_IPROUTE_TYPE_DIRECT) { - RouteEntry entry; - entry.interface = NetworkInterface::from_index(row->dwForwardIfIndex).name(); - entry.destination = IPv4Address(row->dwForwardDest); - entry.mask = IPv4Address(row->dwForwardMask); - entry.gateway = IPv4Address(row->dwForwardNextHop); - entry.metric = row->dwForwardMetric1; - output.push_back(entry); - } - } - return output; -} - -vector route6_entries() { - vector output; - MIB_IPFORWARD_TABLE2* table; - GetIpForwardTable2(AF_INET6, &table); - for (ULONG i = 0; i < table->NumEntries; i++) { - MIB_IPFORWARD_ROW2* row = &table->Table[i]; - if (true) { - try { - Route6Entry entry; - entry.interface = NetworkInterface::from_index(row->InterfaceIndex).name(); - entry.destination = IPv6Address(row->DestinationPrefix.Prefix.Ipv6.sin6_addr.s6_addr); - entry.mask = IPv6Address::from_prefix_length(row->DestinationPrefix.PrefixLength); - entry.gateway = IPv6Address(row->NextHop.Ipv6.sin6_addr.s6_addr); - entry.metric = row->Metric; - output.push_back(entry); - } - catch (invalid_interface&) { - - } - } - } - FreeMibTable(table); - return output; -} - -#else // GNU/LINUX - -vector route_entries() { - using namespace Tins::Internals; - vector output; - ifstream input("/proc/net/route"); - string destination, mask, metric, gw; - uint32_t dummy; - skip_line(input); - RouteEntry entry; - while (input >> entry.interface >> destination >> gw) { - for (unsigned i(0); i < 4; ++i) { - input >> metric; - } - input >> mask; - from_hex(destination, dummy); - entry.destination = IPv4Address(dummy); - from_hex(mask, dummy); - entry.mask = IPv4Address(dummy); - from_hex(gw, dummy); - entry.gateway = IPv4Address(dummy); - from_hex(metric, dummy); - entry.metric = dummy; - skip_line(input); - output.push_back(entry); - } - return output; -} - -vector route6_entries() { - using namespace Tins::Internals; - vector output; - ifstream input("/proc/net/ipv6_route"); - string destination, mask_length, metric, next_hop, dummy, flags; - Route6Entry entry; - while (input >> destination >> mask_length) { - string temporary; - uint32_t temporary_int; - for (unsigned i(0); i < 2; ++i) { - input >> dummy; - } - input >> next_hop; - input >> metric; - for (unsigned i(0); i < 2; ++i) { - input >> dummy; - } - input >> flags >> entry.interface; - from_hex(destination, temporary); - entry.destination = IPv6Address((const uint8_t*)&temporary[0]); - from_hex(mask_length, temporary_int); - entry.mask = IPv6Address::from_prefix_length(temporary_int); - from_hex(next_hop, temporary); - entry.gateway = IPv6Address((const uint8_t*)&temporary[0]); - from_hex(metric, temporary_int); - entry.metric = temporary_int; - // Process flags - from_hex(flags, temporary_int); - // Skip: - // * 0x01000000 -> cache entries - if ((temporary_int & 0x01000000) == 0) { - output.push_back(entry); - } - } - return output; -} - -#endif - bool gateway_from_ip(IPv4Address ip, IPv4Address& gw_addr) { typedef vector entries_type; entries_type entries; @@ -493,37 +156,6 @@ bool gateway_from_ip(IPv4Address ip, IPv4Address& gw_addr) { } return false; } - -#ifdef _WIN32 -set network_interfaces() { - set output; - ULONG size; - ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size); - std::vector buffer(size); - if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) { - PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0]; - while (iface) { - output.insert(iface->AdapterName); - iface = iface->Next; - } - } - return output; -} -#else -set network_interfaces() { - set output; - struct ifaddrs* ifaddrs = 0; - struct ifaddrs* if_it = 0; - getifaddrs(&ifaddrs); - for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) { - output.insert(if_it->ifa_name); - } - if (ifaddrs) { - freeifaddrs(ifaddrs); - } - return output; -} -#endif // _WIN32 string to_string(PDU::PDUType pduType) { #define ENUM_TEXT(p) case(PDU::p): return #p; diff --git a/src/utils/routing_utils.cpp b/src/utils/routing_utils.cpp new file mode 100644 index 0000000..31fca0d --- /dev/null +++ b/src/utils/routing_utils.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2017, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "utils/routing_utils.h" +#ifndef _WIN32 + #if defined(BSD) || defined(__FreeBSD_kernel__) + #include + #include + #include + #include + #include + #include + #include + #else + #include + #endif + #include + #include + #include + #ifdef __ANDROID_API__ + #include + #include + #endif +#else + #include + #include + #include + #undef interface +#endif +#include +#include +#include "network_interface.h" + +using std::vector; +using std::string; +using std::set; +using std::ifstream; +using std::istream; + +namespace Tins { +namespace Utils { + +struct InterfaceCollector { + set ifaces; + + #ifdef _WIN32 + bool operator() (PIP_ADAPTER_ADDRESSES addr) { + ifaces.insert(addr->AdapterName); + return false; + } + #else + bool operator() (struct ifaddrs* addr) { + ifaces.insert(addr->ifa_name); + return false; + } + #endif +}; + +bool from_hex(const string& str, uint32_t& result) { + size_t i = 0; + result = 0; + while (i < str.size()) { + uint8_t tmp; + if (str[i] >= 'A' && str[i] <= 'F') { + tmp = (str[i] - 'A' + 10); + } + else if (str[i] >= '0' && str[i] <= '9') { + tmp = (str[i] - '0'); + } + else { + return false; + } + result = (result << 4) | tmp; + i++; + } + return true; +} + +bool from_hex(const string& str, string& result) { + result.clear(); + for (size_t i = 0; i < str.size(); i+= 2) { + uint8_t value = 0; + for (size_t j = i; j < i + 2 && j < str.size(); ++j) { + if (str[j] >= 'A' && str[j] <= 'F') { + value = (value << 4) | (str[j] - 'A' + 10); + } + else if (str[j] >= 'a' && str[j] <= 'f') { + value = (value << 4) | (str[j] - 'a' + 10); + } + else if (str[j] >= '0' && str[j] <= '9') { + value = (value << 4) | (str[j] - '0'); + } + else { + return false; + } + } + result.push_back(value); + } + return true; +} + +void skip_line(istream& input) { + int c = 0; + while (c != '\n' && input) { + c = input.get(); + } +} + +#if defined(BSD) || defined(__FreeBSD_kernel__) +vector query_route_table(int family) { + int mib[6]; + vector buf; + size_t len; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = family; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + throw runtime_error("sysctl failed"); + } + + buf.resize(len); + if (sysctl(mib, 6, &buf[0], &len, NULL, 0) < 0) { + throw runtime_error("sysctl failed"); + } + + return buf; +} + +void parse_header(struct rt_msghdr* rtm, vector& addrs) { + char* ptr = (char *)(rtm + 1); + // Iterate from RTA_DST (0) to RTA_NETMASK (2) + for (int i = 0; i < 3; ++i) { + sockaddr* sa = 0; + if ((rtm->rtm_addrs & (1 << i)) != 0) { + sa = (struct sockaddr *)ptr; + ptr += sa->sa_len; + if (sa->sa_family == 0) { + sa = 0; + } + } + addrs[i] = sa; + } +} + +vector route_entries() { + vector output; + vector buffer = query_route_table(AF_INET); + char* next = &buffer[0], *end = &buffer[buffer.size()]; + rt_msghdr* rtm; + vector sa(32); + char iface_name[IF_NAMESIZE]; + while (next < end) { + rtm = (rt_msghdr*)next; + // Filter: + // * RTF_STATIC (only manually added routes) + if ((rtm->rtm_flags & (RTF_STATIC)) != 0) { + parse_header(rtm, sa); + if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { + RouteEntry entry; + entry.destination = IPv4Address(((struct sockaddr_in *)sa[RTAX_DST])->sin_addr.s_addr); + entry.gateway = IPv4Address(((struct sockaddr_in *)sa[RTAX_GATEWAY])->sin_addr.s_addr); + if (sa[RTAX_NETMASK]) { + entry.mask = IPv4Address(((struct sockaddr_in *)sa[RTAX_NETMASK])->sin_addr.s_addr); + } + entry.interface = iface_name; + entry.metric = 0; + output.push_back(entry); + } + } + next += rtm->rtm_msglen; + } + return output; +} + +vector route6_entries() { + vector output; + vector buffer = query_route_table(AF_INET6); + char* next = &buffer[0], *end = &buffer[buffer.size()]; + rt_msghdr* rtm; + vector sa(9); + char iface_name[IF_NAMESIZE]; + while (next < end) { + rtm = (rt_msghdr*)next; + // Filter protocol-cloned entries + bool process_entry = true; + // These were removed in recent versions of FreeBSD + #if defined(RTF_WASCLONED) && defined(RTF_PRCLONING) + process_entry = (rtm->rtm_flags & RTF_WASCLONED) == 0 || + (rtm->rtm_flags & RTF_PRCLONING) == 0; + #endif + if (process_entry) { + parse_header(rtm, sa); + if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { + Route6Entry entry; + entry.destination = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_DST])->sin6_addr.s6_addr); + entry.gateway = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_GATEWAY])->sin6_addr.s6_addr); + int prefix_length = 0; + if (sa[RTAX_NETMASK]) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa[RTAX_NETMASK]; + for (size_t i = 0; i < 16; ++i) { + uint8_t this_byte = sin->sin6_addr.s6_addr[i]; + // Stop when we find a zero byte + if (this_byte == 0) { + break; + } + switch (this_byte) { + case 0xff: + prefix_length += 8; + break; + case 0xfe: + prefix_length += 7; + break; + case 0xfc: + prefix_length += 6; + break; + case 0xf8: + prefix_length += 5; + break; + case 0xf0: + prefix_length += 4; + break; + case 0xe0: + prefix_length += 3; + break; + case 0xc0: + prefix_length += 2; + break; + case 0x80: + prefix_length += 1; + break; + default: + break; + } + } + } + entry.mask = IPv6Address::from_prefix_length(prefix_length); + entry.interface = iface_name; + entry.metric = 0; + output.push_back(entry); + } + } + next += rtm->rtm_msglen; + } + return output; +} + +#elif defined(_WIN32) + +vector route_entries() { + vector output; + MIB_IPFORWARDTABLE* table; + ULONG size = 0; + GetIpForwardTable(0, &size, 0); + vector buffer(size); + table = (MIB_IPFORWARDTABLE*)&buffer[0]; + GetIpForwardTable(table, &size, 0); + + for (DWORD i = 0; i < table->dwNumEntries; i++) { + MIB_IPFORWARDROW* row = &table->table[i]; + if (row->dwForwardType == MIB_IPROUTE_TYPE_INDIRECT || + row->dwForwardType == MIB_IPROUTE_TYPE_DIRECT) { + RouteEntry entry; + entry.interface = NetworkInterface::from_index(row->dwForwardIfIndex).name(); + entry.destination = IPv4Address(row->dwForwardDest); + entry.mask = IPv4Address(row->dwForwardMask); + entry.gateway = IPv4Address(row->dwForwardNextHop); + entry.metric = row->dwForwardMetric1; + output.push_back(entry); + } + } + return output; +} + +vector route6_entries() { + vector output; + MIB_IPFORWARD_TABLE2* table; + GetIpForwardTable2(AF_INET6, &table); + for (ULONG i = 0; i < table->NumEntries; i++) { + MIB_IPFORWARD_ROW2* row = &table->Table[i]; + if (true) { + try { + Route6Entry entry; + entry.interface = NetworkInterface::from_index(row->InterfaceIndex).name(); + entry.destination = IPv6Address(row->DestinationPrefix.Prefix.Ipv6.sin6_addr.s6_addr); + entry.mask = IPv6Address::from_prefix_length(row->DestinationPrefix.PrefixLength); + entry.gateway = IPv6Address(row->NextHop.Ipv6.sin6_addr.s6_addr); + entry.metric = row->Metric; + output.push_back(entry); + } + catch (invalid_interface&) { + + } + } + } + FreeMibTable(table); + return output; +} + +#else // GNU/LINUX + +vector route_entries() { + using namespace Tins::Internals; + vector output; + ifstream input("/proc/net/route"); + string destination, mask, metric, gw; + uint32_t dummy; + skip_line(input); + RouteEntry entry; + while (input >> entry.interface >> destination >> gw) { + for (unsigned i(0); i < 4; ++i) { + input >> metric; + } + input >> mask; + from_hex(destination, dummy); + entry.destination = IPv4Address(dummy); + from_hex(mask, dummy); + entry.mask = IPv4Address(dummy); + from_hex(gw, dummy); + entry.gateway = IPv4Address(dummy); + from_hex(metric, dummy); + entry.metric = dummy; + skip_line(input); + output.push_back(entry); + } + return output; +} + +vector route6_entries() { + using namespace Tins::Internals; + vector output; + ifstream input("/proc/net/ipv6_route"); + string destination, mask_length, metric, next_hop, dummy, flags; + Route6Entry entry; + while (input >> destination >> mask_length) { + string temporary; + uint32_t temporary_int; + for (unsigned i(0); i < 2; ++i) { + input >> dummy; + } + input >> next_hop; + input >> metric; + for (unsigned i(0); i < 2; ++i) { + input >> dummy; + } + input >> flags >> entry.interface; + from_hex(destination, temporary); + entry.destination = IPv6Address((const uint8_t*)&temporary[0]); + from_hex(mask_length, temporary_int); + entry.mask = IPv6Address::from_prefix_length(temporary_int); + from_hex(next_hop, temporary); + entry.gateway = IPv6Address((const uint8_t*)&temporary[0]); + from_hex(metric, temporary_int); + entry.metric = temporary_int; + // Process flags + from_hex(flags, temporary_int); + // Skip: + // * 0x01000000 -> cache entries + if ((temporary_int & 0x01000000) == 0) { + output.push_back(entry); + } + } + return output; +} + +#endif + +#ifdef _WIN32 +set network_interfaces() { + set output; + ULONG size; + ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size); + vector buffer(size); + if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) { + PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0]; + while (iface) { + output.insert(iface->AdapterName); + iface = iface->Next; + } + } + return output; +} +#else +set network_interfaces() { + set output; + struct ifaddrs* ifaddrs = 0; + struct ifaddrs* if_it = 0; + getifaddrs(&ifaddrs); + for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) { + output.insert(if_it->ifa_name); + } + if (ifaddrs) { + freeifaddrs(ifaddrs); + } + return output; +} +#endif // _WIN32 + +} // Utils +} // Tins