mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 10:45:57 +01:00
410 lines
14 KiB
C++
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
|