1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 10:45:57 +01:00
Files
libtins/src/network_interface.cpp
2022-04-24 08:44:52 -07:00

410 lines
14 KiB
C++

/*
* 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 <stdexcept>
#include <vector>
#include <cstring>
#include <iterator>
#include <tins/macros.h>
#ifndef _WIN32
#include <netinet/in.h>
#if defined(BSD) || defined(__FreeBSD_kernel__)
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <sys/socket.h>
#else
#include <linux/if_packet.h>
#endif
#include <ifaddrs.h>
#include <net/if.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#undef interface
#endif
#include <tins/network_interface.h>
#include <tins/endianness.h>
#include <tins/exceptions.h>
#include <tins/utils/routing_utils.h>
using std::string;
using std::wstring;
using std::vector;
using std::set;
using std::copy;
/** \cond */
struct InterfaceInfoCollector {
typedef Tins::NetworkInterface::Info info_type;
info_type* info;
int iface_id;
const char* iface_name;
bool found_hw;
bool found_ip;
InterfaceInfoCollector(info_type* res, int id, const char* if_name)
: info(res), iface_id(id), iface_name(if_name), found_hw(false), found_ip(false) { }
#ifndef _WIN32
bool operator() (const struct ifaddrs* addr) {
using Tins::Endian::host_to_be;
using Tins::IPv4Address;
#if defined(BSD) || defined(__FreeBSD_kernel__)
#define TINS_BROADCAST_ADDR(addr) (addr->ifa_dstaddr)
#define TINS_BROADCAST_FLAGS (IFF_BROADCAST | IFF_POINTOPOINT)
const struct sockaddr_dl* addr_ptr = ((struct sockaddr_dl*)addr->ifa_addr);
if (addr->ifa_addr->sa_family == AF_LINK && addr_ptr->sdl_index == iface_id) {
info->hw_addr = (const uint8_t*)LLADDR(addr_ptr);
found_hw = true;
info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
}
#else
#define TINS_BROADCAST_ADDR(addr) (addr->ifa_broadaddr)
#define TINS_BROADCAST_FLAGS (IFF_BROADCAST)
const struct sockaddr_ll* addr_ptr = ((struct sockaddr_ll*)addr->ifa_addr);
if (!addr->ifa_addr) {
return false;
}
if (addr->ifa_addr->sa_family == AF_PACKET && addr_ptr->sll_ifindex == iface_id) {
info->hw_addr = addr_ptr->sll_addr;
found_hw = true;
info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
}
#endif
else if (!std::strcmp(addr->ifa_name, iface_name)) {
if (addr->ifa_addr->sa_family == AF_INET) {
info->ip_addr = IPv4Address(((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr);
info->netmask = IPv4Address(((struct sockaddr_in *)addr->ifa_netmask)->sin_addr.s_addr);
if ((addr->ifa_flags & (TINS_BROADCAST_FLAGS))) {
info->bcast_addr = IPv4Address(
((struct sockaddr_in *)TINS_BROADCAST_ADDR(addr))->sin_addr.s_addr);
}
else {
info->bcast_addr = 0;
}
found_ip = true;
}
else if (addr->ifa_addr->sa_family == AF_INET6) {
Tins::NetworkInterface::IPv6Prefix prefix;
prefix.address = ((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr.s6_addr;
Tins::IPv6Address mask = ((struct sockaddr_in6 *)addr->ifa_netmask)->sin6_addr.s6_addr;
prefix.prefix_length = 0;
for (Tins::IPv6Address::iterator iter = mask.begin(); iter != mask.end(); ++iter) {
if (*iter == 255) {
prefix.prefix_length += 8;
}
else {
uint8_t current_value = 128;
while (*iter > 0) {
prefix.prefix_length += 1;
*iter &= ~current_value;
current_value /= 2;
}
break;
}
}
info->ipv6_addrs.push_back(prefix);
}
}
#undef TINS_BROADCAST_ADDR
#undef TINS_BROADCAST_FLAGS
return found_ip && found_hw;
}
#else // _WIN32
bool operator() (const IP_ADAPTER_ADDRESSES* iface) {
using Tins::IPv4Address;
using Tins::Endian::host_to_be;
if (iface_id == uint32_t(iface->IfIndex)) {
copy(iface->PhysicalAddress, iface->PhysicalAddress + 6, info->hw_addr.begin());
found_hw = true;
info->is_up = (iface->OperStatus == IfOperStatusUp);
IP_ADAPTER_UNICAST_ADDRESS* unicast = iface->FirstUnicastAddress;
while (unicast) {
int family = ((const struct sockaddr*)unicast->Address.lpSockaddr)->sa_family;
if (family == AF_INET) {
info->ip_addr = IPv4Address(((const struct sockaddr_in *)unicast->Address.lpSockaddr)->sin_addr.s_addr);
info->netmask = IPv4Address(host_to_be<uint32_t>(0xffffffff << (32 - unicast->OnLinkPrefixLength)));
info->bcast_addr = IPv4Address((info->ip_addr & info->netmask) | ~info->netmask);
found_ip = true;
}
else if (family == AF_INET6) {
Tins::NetworkInterface::IPv6Prefix prefix;
prefix.address = ((const struct sockaddr_in6 *)unicast->Address.lpSockaddr)->sin6_addr.s6_addr;
prefix.prefix_length = unicast->OnLinkPrefixLength;
info->ipv6_addrs.push_back(prefix);
found_ip = true;
}
unicast = unicast->Next;
}
}
return found_ip && found_hw;
}
#endif // _WIN32
};
#ifdef _WIN32
template <typename T, typename U>
T find_adapter_address_info(uint32_t iface_id, U (IP_ADAPTER_ADDRESSES::*member)) {
ULONG size;
::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
vector<uint8_t> 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) {
if (iface->IfIndex == iface_id) {
return T(iface->*member);
}
iface = iface->Next;
}
}
throw Tins::invalid_interface();
}
#endif // _WIN32
/** \endcond */
namespace Tins {
// static
NetworkInterface NetworkInterface::default_interface() {
return NetworkInterface(IPv4Address(uint32_t(0)));
}
vector<NetworkInterface> NetworkInterface::all() {
const set<string> interfaces = Utils::network_interfaces();
vector<NetworkInterface> output;
for (set<string>::const_iterator it = interfaces.begin(); it != interfaces.end(); ++it) {
output.push_back(*it);
}
return output;
}
NetworkInterface::NetworkInterface()
: iface_id_(0) {
}
NetworkInterface NetworkInterface::from_index(id_type identifier) {
NetworkInterface iface;
iface.iface_id_ = identifier;
return iface;
}
NetworkInterface::NetworkInterface(const char* name) {
iface_id_ = name ? resolve_index(name) : 0;
}
NetworkInterface::NetworkInterface(const std::string& name) {
iface_id_ = resolve_index(name.c_str());
}
NetworkInterface::NetworkInterface(IPv4Address ip)
: iface_id_(0) {
typedef vector<Utils::RouteEntry> entries_type;
if (ip == "127.0.0.1") {
#if defined(BSD) || defined(__FreeBSD_kernel__)
iface_id_ = resolve_index("lo0");
#else
iface_id_ = resolve_index("lo");
#endif
}
else {
const Utils::RouteEntry* best_match = 0;
entries_type entries;
uint32_t ip_int = ip;
Utils::route_entries(std::back_inserter(entries));
for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) {
if ((ip_int & it->mask) == it->destination) {
if (!best_match || it->mask > best_match->mask || it->metric < best_match->metric) {
best_match = &*it;
}
}
}
if (!best_match) {
throw invalid_interface();
}
iface_id_ = resolve_index(best_match->interface.c_str());
}
}
NetworkInterface::NetworkInterface(IPv6Address ipv6)
: iface_id_(0) {
typedef vector<Utils::Route6Entry> entries_type;
if (ipv6 == "::1") {
#if defined(BSD) || defined(__FreeBSD_kernel__)
iface_id_ = resolve_index("lo0");
#else
iface_id_ = resolve_index("lo");
#endif
}
else {
const Utils::Route6Entry* best_match = 0;
entries_type entries;
Utils::route6_entries(std::back_inserter(entries));
for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) {
if ((ipv6 & it->mask) == it->destination) {
if (!best_match || it->mask > best_match->mask || it->metric < best_match->metric) {
best_match = &*it;
}
}
}
if (!best_match) {
throw invalid_interface();
}
iface_id_ = resolve_index(best_match->interface.c_str());
}
}
string NetworkInterface::name() const {
#ifndef _WIN32
char iface_name[IF_NAMESIZE];
if (!if_indextoname(iface_id_, iface_name)) {
throw invalid_interface();
}
return iface_name;
#else // _WIN32
return find_adapter_address_info<string>(iface_id_, &IP_ADAPTER_ADDRESSES::AdapterName);
#endif // WIN32
}
wstring NetworkInterface::friendly_name() const {
#ifndef _WIN32
string n = name();
return wstring(n.begin(), n.end());
#else // _WIN32
return find_adapter_address_info<wstring>(iface_id_, &IP_ADAPTER_ADDRESSES::FriendlyName);
#endif // WIN32
}
NetworkInterface::Info NetworkInterface::addresses() const {
return info();
}
NetworkInterface::Info NetworkInterface::info() const {
const std::string& iface_name = name();
Info info;
InterfaceInfoCollector collector(&info, iface_id_, iface_name.c_str());
info.is_up = false;
#ifdef _WIN32
ULONG size;
::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
std::vector<uint8_t> 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) {
collector(iface);
iface = iface->Next;
}
}
#else // _WIN32
#ifndef ANDROID
struct ifaddrs* ifaddrs = 0;
struct ifaddrs* if_it = 0;
getifaddrs(&ifaddrs);
for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) {
collector(if_it);
}
if (ifaddrs) {
freeifaddrs(ifaddrs);
}
#else
throw new std::runtime_error("android ifaddr not supported");
#endif
#endif // _WIN32
// If we didn't even get the hw address or ip address, this went wrong
if (!collector.found_hw && !collector.found_ip) {
throw invalid_interface();
}
return info;
}
bool NetworkInterface::is_loopback() const {
return info().ip_addr.is_loopback();
}
bool NetworkInterface::is_up() const {
return info().is_up;
}
NetworkInterface::address_type NetworkInterface::hw_address() const {
return info().hw_addr;
}
IPv4Address NetworkInterface::ipv4_address() const {
return info().ip_addr;
}
IPv4Address NetworkInterface::ipv4_mask() const {
return info().netmask;
}
IPv4Address NetworkInterface::ipv4_broadcast() const {
return info().bcast_addr;
}
vector<NetworkInterface::IPv6Prefix> NetworkInterface::ipv6_addresses() const {
return info().ipv6_addrs;
}
NetworkInterface::id_type NetworkInterface::resolve_index(const char* name) {
#ifndef _WIN32
id_type id = if_nametoindex(name);
if (!id) {
throw invalid_interface();
}
return id;
#else // _WIN32
ULONG size;
::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
vector<uint8_t> 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) {
if (strcmp(iface->AdapterName, name) == 0) {
return iface->IfIndex;
}
iface = iface->Next;
}
}
throw invalid_interface();
#endif // _WIN32
}
} // Tins