2021-03-09 22:24:35 +00:00
|
|
|
#include "net.hpp"
|
2019-01-13 22:39:10 +00:00
|
|
|
|
2021-03-09 22:24:35 +00:00
|
|
|
#include "net_if.hpp"
|
2020-05-08 17:23:21 +00:00
|
|
|
#include <stdexcept>
|
2019-10-22 15:08:52 +00:00
|
|
|
|
2018-07-24 03:23:00 +00:00
|
|
|
#ifdef ANDROID
|
2021-03-09 22:24:35 +00:00
|
|
|
#include <llarp/android/ifaddrs.h>
|
2018-07-24 02:34:12 +00:00
|
|
|
#endif
|
2019-01-13 22:39:10 +00:00
|
|
|
|
2018-07-30 04:38:14 +00:00
|
|
|
#ifndef _WIN32
|
2018-02-01 17:06:49 +00:00
|
|
|
#include <arpa/inet.h>
|
2018-11-06 14:06:09 +00:00
|
|
|
#ifndef ANDROID
|
2018-08-08 12:36:10 +00:00
|
|
|
#include <ifaddrs.h>
|
2018-11-06 14:06:09 +00:00
|
|
|
#endif
|
2019-10-30 20:55:56 +00:00
|
|
|
#endif
|
2019-10-11 20:16:03 +00:00
|
|
|
|
2021-03-09 22:24:35 +00:00
|
|
|
#include "ip.hpp"
|
|
|
|
#include "ip_range.hpp"
|
2022-07-16 00:41:14 +00:00
|
|
|
#include <llarp/util/logging.hpp>
|
2021-03-09 22:24:35 +00:00
|
|
|
#include <llarp/util/str.hpp>
|
2018-02-01 17:07:01 +00:00
|
|
|
|
2021-03-02 22:49:25 +00:00
|
|
|
#ifdef ANDROID
|
2021-03-09 22:24:35 +00:00
|
|
|
#include <llarp/android/ifaddrs.h>
|
2021-03-02 18:18:22 +00:00
|
|
|
#else
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2018-12-12 02:52:51 +00:00
|
|
|
#include <cstdio>
|
2020-06-24 13:24:07 +00:00
|
|
|
#include <list>
|
2018-11-03 13:19:18 +00:00
|
|
|
|
2018-05-22 15:54:19 +00:00
|
|
|
bool
|
|
|
|
operator==(const sockaddr& a, const sockaddr& b)
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (a.sa_family != b.sa_family)
|
2018-09-04 12:41:25 +00:00
|
|
|
return false;
|
2020-04-07 18:38:56 +00:00
|
|
|
switch (a.sa_family)
|
2018-05-22 15:54:19 +00:00
|
|
|
{
|
|
|
|
case AF_INET:
|
2018-09-04 12:41:25 +00:00
|
|
|
return *((const sockaddr_in*)&a) == *((const sockaddr_in*)&b);
|
2018-05-22 15:54:19 +00:00
|
|
|
case AF_INET6:
|
2018-09-04 12:41:25 +00:00
|
|
|
return *((const sockaddr_in6*)&a) == *((const sockaddr_in6*)&b);
|
2018-05-22 15:54:19 +00:00
|
|
|
default:
|
2018-09-04 12:41:25 +00:00
|
|
|
return false;
|
2018-05-22 15:54:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator<(const sockaddr_in6& a, const sockaddr_in6& b)
|
|
|
|
{
|
2019-05-07 12:31:34 +00:00
|
|
|
return a.sin6_addr < b.sin6_addr || a.sin6_port < b.sin6_port;
|
2018-05-22 15:54:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator<(const in6_addr& a, const in6_addr& b)
|
|
|
|
{
|
|
|
|
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
|
|
|
|
}
|
|
|
|
|
2018-09-04 12:41:25 +00:00
|
|
|
bool
|
|
|
|
operator==(const in6_addr& a, const in6_addr& b)
|
|
|
|
{
|
|
|
|
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator==(const sockaddr_in& a, const sockaddr_in& b)
|
|
|
|
{
|
|
|
|
return a.sin_port == b.sin_port && a.sin_addr.s_addr == b.sin_addr.s_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator==(const sockaddr_in6& a, const sockaddr_in6& b)
|
|
|
|
{
|
|
|
|
return a.sin6_port == b.sin6_port && a.sin6_addr == b.sin6_addr;
|
|
|
|
}
|
|
|
|
|
2018-07-30 04:38:14 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <iphlpapi.h>
|
2018-08-08 12:36:10 +00:00
|
|
|
#include <strsafe.h>
|
2018-07-30 04:38:14 +00:00
|
|
|
|
2018-11-19 07:55:19 +00:00
|
|
|
// current strategy: mingw 32-bit builds call an inlined version of the function
|
|
|
|
// microsoft c++ and mingw 64-bit builds call the normal function
|
2018-07-30 04:38:14 +00:00
|
|
|
#define DEFAULT_BUFFER_SIZE 15000
|
|
|
|
|
2018-11-19 07:55:19 +00:00
|
|
|
// in any case, we still need to implement some form of
|
|
|
|
// getifaddrs(3) with compatible semantics on NT...
|
|
|
|
// daemon.ini section [bind] will have something like
|
|
|
|
// [bind]
|
|
|
|
// Ethernet=1090
|
|
|
|
// inside, since that's what we use in windows to refer to
|
|
|
|
// network interfaces
|
2018-07-30 04:38:14 +00:00
|
|
|
struct llarp_nt_ifaddrs_t
|
|
|
|
{
|
|
|
|
struct llarp_nt_ifaddrs_t* ifa_next; /* Pointer to the next structure. */
|
|
|
|
char* ifa_name; /* Name of this network interface. */
|
|
|
|
unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */
|
|
|
|
struct sockaddr* ifa_addr; /* Network address of this interface. */
|
|
|
|
struct sockaddr* ifa_netmask; /* Netmask of this interface. */
|
|
|
|
};
|
|
|
|
|
|
|
|
// internal struct
|
|
|
|
struct _llarp_nt_ifaddrs_t
|
|
|
|
{
|
|
|
|
struct llarp_nt_ifaddrs_t _ifa;
|
|
|
|
char _name[256];
|
|
|
|
struct sockaddr_storage _addr;
|
|
|
|
struct sockaddr_storage _netmask;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void*
|
|
|
|
_llarp_nt_heap_alloc(const size_t n_bytes)
|
|
|
|
{
|
|
|
|
/* Does not appear very safe with re-entrant calls on XP */
|
|
|
|
return HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
_llarp_nt_heap_free(void* mem)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, mem);
|
|
|
|
}
|
|
|
|
#define llarp_nt_new0(struct_type, n_structs) \
|
|
|
|
((struct_type*)malloc((size_t)sizeof(struct_type) * (size_t)(n_structs)))
|
|
|
|
|
|
|
|
int
|
|
|
|
llarp_nt_sockaddr_pton(const char* src, struct sockaddr* dst)
|
|
|
|
{
|
2018-09-20 19:24:13 +00:00
|
|
|
struct addrinfo hints;
|
|
|
|
struct addrinfo* result = nullptr;
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
2020-04-07 18:38:56 +00:00
|
|
|
hints.ai_family = AF_UNSPEC;
|
2018-08-08 12:36:10 +00:00
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
2020-04-07 18:38:56 +00:00
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
const int status = getaddrinfo(src, nullptr, &hints, &result);
|
|
|
|
if (!status)
|
2018-07-30 04:38:14 +00:00
|
|
|
{
|
|
|
|
memcpy(dst, result->ai_addr, result->ai_addrlen);
|
|
|
|
freeaddrinfo(result);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-19 07:55:19 +00:00
|
|
|
/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes
|
|
|
|
* 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced
|
|
|
|
* with -D_USE_32BIT_TIME_T with side effects to everything else.
|
|
|
|
*
|
|
|
|
* Only supports IPv4 addressing similar to SIOCGIFCONF socket option.
|
|
|
|
*
|
|
|
|
* Interfaces that are not "operationally up" will return the address 0.0.0.0,
|
|
|
|
* this includes adapters with static IP addresses but with disconnected cable.
|
|
|
|
* This is documented under the GetIpAddrTable API. Interface status can only
|
|
|
|
* be determined by the address, a separate flag is introduced with the
|
|
|
|
* GetAdapterAddresses API.
|
|
|
|
*
|
|
|
|
* The IPv4 loopback interface is not included.
|
|
|
|
*
|
|
|
|
* Available in Windows 2000 and Wine 1.0.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
_llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap)
|
|
|
|
{
|
|
|
|
DWORD dwRet;
|
2020-04-07 18:38:56 +00:00
|
|
|
ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE;
|
2018-11-19 07:55:19 +00:00
|
|
|
PIP_ADAPTER_INFO pAdapterInfo = nullptr;
|
2020-04-07 18:38:56 +00:00
|
|
|
PIP_ADAPTER_INFO pAdapter = nullptr;
|
2018-11-19 07:55:19 +00:00
|
|
|
|
|
|
|
/* loop to handle interfaces coming online causing a buffer overflow
|
|
|
|
* between first call to list buffer length and second call to enumerate.
|
|
|
|
*/
|
2020-04-07 18:38:56 +00:00
|
|
|
for (unsigned i = 3; i; i--)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
fprintf(stderr, "IP_ADAPTER_INFO buffer length %lu bytes.\n", ulOutBufLen);
|
|
|
|
#endif
|
|
|
|
pAdapterInfo = (IP_ADAPTER_INFO*)_llarp_nt_heap_alloc(ulOutBufLen);
|
2020-04-07 18:38:56 +00:00
|
|
|
dwRet = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
|
|
|
|
if (ERROR_BUFFER_OVERFLOW == dwRet)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
_llarp_nt_heap_free(pAdapterInfo);
|
|
|
|
pAdapterInfo = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
switch (dwRet)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
case ERROR_SUCCESS: /* NO_ERROR */
|
|
|
|
break;
|
|
|
|
case ERROR_BUFFER_OVERFLOW:
|
|
|
|
errno = ENOBUFS;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (pAdapterInfo)
|
2018-11-19 07:55:19 +00:00
|
|
|
_llarp_nt_heap_free(pAdapterInfo);
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
errno = dwRet;
|
|
|
|
#ifdef DEBUG
|
|
|
|
fprintf(stderr, "system call failed: %lu\n", GetLastError());
|
|
|
|
#endif
|
2020-04-07 18:38:56 +00:00
|
|
|
if (pAdapterInfo)
|
2018-11-19 07:55:19 +00:00
|
|
|
_llarp_nt_heap_free(pAdapterInfo);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* count valid adapters */
|
|
|
|
int n = 0, k = 0;
|
2020-04-07 18:38:56 +00:00
|
|
|
for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
for (IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
/* skip null adapters */
|
2020-04-07 18:38:56 +00:00
|
|
|
if (strlen(pIPAddr->IpAddress.String) == 0)
|
2018-11-19 07:55:19 +00:00
|
|
|
continue;
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
fprintf(stderr, "GetAdaptersInfo() discovered %d interfaces.\n", n);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* contiguous block for adapter list */
|
2020-04-07 18:38:56 +00:00
|
|
|
struct _llarp_nt_ifaddrs_t* ifa = llarp_nt_new0(struct _llarp_nt_ifaddrs_t, n);
|
2018-11-19 07:55:19 +00:00
|
|
|
struct _llarp_nt_ifaddrs_t* ift = ifa;
|
2020-04-07 18:38:56 +00:00
|
|
|
int val = 0;
|
2018-11-19 07:55:19 +00:00
|
|
|
/* now populate list */
|
2020-04-07 18:38:56 +00:00
|
|
|
for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
for (IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
/* skip null adapters */
|
2020-04-07 18:38:56 +00:00
|
|
|
if (strlen(pIPAddr->IpAddress.String) == 0)
|
2018-11-19 07:55:19 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* address */
|
|
|
|
ift->_ifa.ifa_addr = (struct sockaddr*)&ift->_addr;
|
2020-04-07 18:38:56 +00:00
|
|
|
val = llarp_nt_sockaddr_pton(pIPAddr->IpAddress.String, ift->_ifa.ifa_addr);
|
2020-01-08 14:57:23 +00:00
|
|
|
assert(1 == val);
|
2018-11-19 07:55:19 +00:00
|
|
|
|
|
|
|
/* name */
|
|
|
|
#ifdef DEBUG
|
2020-04-07 18:38:56 +00:00
|
|
|
fprintf(stderr, "name:%s IPv4 index:%lu\n", pAdapter->AdapterName, pAdapter->Index);
|
2018-11-19 07:55:19 +00:00
|
|
|
#endif
|
|
|
|
ift->_ifa.ifa_name = ift->_name;
|
|
|
|
StringCchCopyN(ift->_ifa.ifa_name, 128, pAdapter->AdapterName, 128);
|
|
|
|
|
|
|
|
/* flags: assume up, broadcast and multicast */
|
|
|
|
ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK)
|
2018-11-19 07:55:19 +00:00
|
|
|
ift->_ifa.ifa_flags |= IFF_LOOPBACK;
|
|
|
|
|
|
|
|
/* netmask */
|
|
|
|
ift->_ifa.ifa_netmask = (sockaddr*)&ift->_netmask;
|
2020-04-07 18:38:56 +00:00
|
|
|
val = llarp_nt_sockaddr_pton(pIPAddr->IpMask.String, ift->_ifa.ifa_netmask);
|
2020-01-08 14:57:23 +00:00
|
|
|
assert(1 == val);
|
2018-11-19 07:55:19 +00:00
|
|
|
|
|
|
|
/* next */
|
2020-04-07 18:38:56 +00:00
|
|
|
if (k++ < (n - 1))
|
2018-11-19 07:55:19 +00:00
|
|
|
{
|
|
|
|
ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1);
|
2020-04-07 18:38:56 +00:00
|
|
|
ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next);
|
2018-11-19 07:55:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ift->_ifa.ifa_next = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
if (pAdapterInfo)
|
2018-11-19 07:55:19 +00:00
|
|
|
_llarp_nt_heap_free(pAdapterInfo);
|
|
|
|
*ifap = (struct llarp_nt_ifaddrs_t*)ifa;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// an implementation of if_nametoindex(3) based on GetAdapterIndex(2)
|
|
|
|
// with a fallback to GetAdaptersAddresses(2) commented out for now
|
|
|
|
// unless it becomes evident that the first codepath fails in certain
|
|
|
|
// edge cases?
|
2018-07-30 04:38:14 +00:00
|
|
|
static unsigned
|
2020-04-16 06:36:31 +00:00
|
|
|
_llarp_nt_nametoindex(const char* ifname)
|
2018-07-30 04:38:14 +00:00
|
|
|
{
|
|
|
|
ULONG ifIndex;
|
2020-04-16 06:36:31 +00:00
|
|
|
DWORD dwRet;
|
2018-07-30 04:38:14 +00:00
|
|
|
char szAdapterName[256];
|
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!ifname)
|
2018-07-30 04:38:14 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
StringCchCopyN(szAdapterName, sizeof(szAdapterName), ifname, 256);
|
|
|
|
dwRet = GetAdapterIndex((LPWSTR)szAdapterName, &ifIndex);
|
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!dwRet)
|
2018-07-30 04:38:14 +00:00
|
|
|
return ifIndex;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the emulated getifaddrs(3) itself.
|
|
|
|
static bool
|
|
|
|
llarp_nt_getifaddrs(struct llarp_nt_ifaddrs_t** ifap)
|
|
|
|
{
|
|
|
|
assert(nullptr != ifap);
|
|
|
|
#ifdef DEBUG
|
2020-04-07 18:38:56 +00:00
|
|
|
fprintf(stderr, "llarp_nt_getifaddrs (ifap:%p error:%p)\n", (void*)ifap, (void*)errno);
|
2018-07-30 04:38:14 +00:00
|
|
|
#endif
|
2018-11-19 07:55:19 +00:00
|
|
|
return _llarp_nt_getadaptersinfo(ifap);
|
2018-07-30 04:38:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
llarp_nt_freeifaddrs(struct llarp_nt_ifaddrs_t* ifa)
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!ifa)
|
2018-07-30 04:38:14 +00:00
|
|
|
return;
|
|
|
|
free(ifa);
|
|
|
|
}
|
|
|
|
|
|
|
|
// emulated if_nametoindex(3)
|
|
|
|
static unsigned
|
|
|
|
llarp_nt_if_nametoindex(const char* ifname)
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!ifname)
|
2018-07-30 04:38:14 +00:00
|
|
|
return 0;
|
2020-04-16 06:36:31 +00:00
|
|
|
return _llarp_nt_nametoindex(ifname);
|
2018-07-30 04:38:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// fix up names for win32
|
|
|
|
#define ifaddrs llarp_nt_ifaddrs_t
|
|
|
|
#define getifaddrs llarp_nt_getifaddrs
|
|
|
|
#define freeifaddrs llarp_nt_freeifaddrs
|
|
|
|
#define if_nametoindex llarp_nt_if_nametoindex
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// jeff's original code
|
2018-05-22 15:54:19 +00:00
|
|
|
bool
|
|
|
|
llarp_getifaddr(const char* ifname, int af, struct sockaddr* addr)
|
|
|
|
{
|
2018-02-01 17:07:01 +00:00
|
|
|
ifaddrs* ifa = nullptr;
|
2020-04-07 18:38:56 +00:00
|
|
|
bool found = false;
|
2018-02-01 17:07:01 +00:00
|
|
|
socklen_t sl = sizeof(sockaddr_in6);
|
2020-04-07 18:38:56 +00:00
|
|
|
if (af == AF_INET)
|
2018-05-22 15:54:19 +00:00
|
|
|
sl = sizeof(sockaddr_in);
|
2018-06-14 20:42:30 +00:00
|
|
|
|
2018-07-30 04:38:14 +00:00
|
|
|
#ifndef _WIN32
|
2020-04-07 18:38:56 +00:00
|
|
|
if (getifaddrs(&ifa) == -1)
|
2018-07-30 04:38:14 +00:00
|
|
|
#else
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!strcmp(ifname, "lo") || !strcmp(ifname, "lo0"))
|
2018-12-20 18:55:58 +00:00
|
|
|
{
|
2020-05-04 16:51:57 +00:00
|
|
|
if (addr)
|
|
|
|
{
|
|
|
|
sockaddr_in* lo = (sockaddr_in*)addr;
|
|
|
|
lo->sin_family = af;
|
|
|
|
lo->sin_port = 0;
|
|
|
|
inet_pton(af, "127.0.0.1", &lo->sin_addr);
|
|
|
|
}
|
2018-12-20 18:55:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!getifaddrs(&ifa))
|
2018-07-30 04:38:14 +00:00
|
|
|
#endif
|
2018-05-22 15:54:19 +00:00
|
|
|
return false;
|
2018-02-01 17:07:01 +00:00
|
|
|
ifaddrs* i = ifa;
|
2020-04-07 18:38:56 +00:00
|
|
|
while (i)
|
2018-05-22 15:54:19 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (i->ifa_addr)
|
2018-05-18 13:17:58 +00:00
|
|
|
{
|
2018-07-05 15:44:06 +00:00
|
|
|
// llarp::LogInfo(__FILE__, "scanning ", i->ifa_name, " af: ",
|
2018-06-08 13:12:17 +00:00
|
|
|
// std::to_string(i->ifa_addr->sa_family));
|
2020-08-28 11:05:29 +00:00
|
|
|
if (std::string_view{i->ifa_name} == std::string_view{ifname} && i->ifa_addr->sa_family == af)
|
2018-05-22 15:54:19 +00:00
|
|
|
{
|
2018-07-26 10:21:19 +00:00
|
|
|
// can't do this here
|
2018-07-27 00:21:57 +00:00
|
|
|
// llarp::Addr a(*i->ifa_addr);
|
|
|
|
// if(!a.isPrivate())
|
2018-07-26 10:21:19 +00:00
|
|
|
//{
|
2018-07-27 00:21:57 +00:00
|
|
|
// llarp::LogInfo(__FILE__, "found ", ifname, " af: ", af);
|
2020-05-04 16:51:57 +00:00
|
|
|
if (addr)
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
2020-05-04 16:51:57 +00:00
|
|
|
memcpy(addr, i->ifa_addr, sl);
|
|
|
|
if (af == AF_INET6)
|
|
|
|
{
|
|
|
|
// set scope id
|
|
|
|
auto* ip6addr = (sockaddr_in6*)addr;
|
|
|
|
ip6addr->sin6_scope_id = if_nametoindex(ifname);
|
|
|
|
ip6addr->sin6_flowinfo = 0;
|
|
|
|
}
|
2018-05-23 13:49:00 +00:00
|
|
|
}
|
2018-07-27 00:21:57 +00:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2018-07-26 10:21:19 +00:00
|
|
|
//}
|
2018-02-01 17:06:49 +00:00
|
|
|
}
|
2018-02-01 17:07:01 +00:00
|
|
|
i = i->ifa_next;
|
2018-02-01 17:06:49 +00:00
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (ifa)
|
2018-05-22 15:54:19 +00:00
|
|
|
freeifaddrs(ifa);
|
2018-02-01 17:07:01 +00:00
|
|
|
return found;
|
2018-02-01 17:06:49 +00:00
|
|
|
}
|
2018-08-08 12:36:10 +00:00
|
|
|
|
2018-07-27 00:21:57 +00:00
|
|
|
namespace llarp
|
|
|
|
{
|
2019-07-03 14:32:51 +00:00
|
|
|
static void
|
2020-04-07 18:38:56 +00:00
|
|
|
IterAllNetworkInterfaces(std::function<void(ifaddrs* const)> visit)
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
|
|
|
ifaddrs* ifa = nullptr;
|
2018-07-30 04:38:14 +00:00
|
|
|
#ifndef _WIN32
|
2020-04-07 18:38:56 +00:00
|
|
|
if (getifaddrs(&ifa) == -1)
|
2018-07-30 04:38:14 +00:00
|
|
|
#else
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!getifaddrs(&ifa))
|
2018-07-30 04:38:14 +00:00
|
|
|
#endif
|
2019-07-03 14:32:51 +00:00
|
|
|
return;
|
|
|
|
|
2018-07-27 00:21:57 +00:00
|
|
|
ifaddrs* i = ifa;
|
2020-04-07 18:38:56 +00:00
|
|
|
while (i)
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
2019-07-03 14:32:51 +00:00
|
|
|
visit(i);
|
2019-07-16 19:16:49 +00:00
|
|
|
i = i->ifa_next;
|
2019-07-03 14:32:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
if (ifa)
|
2019-07-03 14:32:51 +00:00
|
|
|
freeifaddrs(ifa);
|
|
|
|
}
|
2020-06-11 11:44:02 +00:00
|
|
|
namespace net
|
|
|
|
{
|
|
|
|
std::string
|
|
|
|
LoopbackInterfaceName()
|
|
|
|
{
|
|
|
|
const auto loopback = IPRange::FromIPv4(127, 0, 0, 0, 8);
|
|
|
|
std::string ifname;
|
|
|
|
IterAllNetworkInterfaces([&ifname, loopback](ifaddrs* const i) {
|
|
|
|
if (i->ifa_addr and i->ifa_addr->sa_family == AF_INET)
|
|
|
|
{
|
|
|
|
llarp::nuint32_t addr{((sockaddr_in*)i->ifa_addr)->sin_addr.s_addr};
|
2020-06-24 13:24:07 +00:00
|
|
|
if (loopback.Contains(xntohl(addr)))
|
2020-06-11 11:44:02 +00:00
|
|
|
{
|
|
|
|
ifname = i->ifa_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (ifname.empty())
|
|
|
|
{
|
|
|
|
throw std::runtime_error(
|
|
|
|
"we have no ipv4 loopback interface for some ungodly reason, yeah idk fam");
|
|
|
|
}
|
|
|
|
return ifname;
|
|
|
|
}
|
|
|
|
} // namespace net
|
2019-07-03 14:32:51 +00:00
|
|
|
|
|
|
|
bool
|
|
|
|
GetBestNetIF(std::string& ifname, int af)
|
|
|
|
{
|
|
|
|
bool found = false;
|
2019-07-09 19:20:01 +00:00
|
|
|
IterAllNetworkInterfaces([&](ifaddrs* i) {
|
2020-04-07 18:38:56 +00:00
|
|
|
if (found)
|
2019-07-03 14:32:51 +00:00
|
|
|
return;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (i->ifa_addr)
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (i->ifa_addr->sa_family == af)
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
2020-05-11 15:22:42 +00:00
|
|
|
llarp::SockAddr a(*i->ifa_addr);
|
2020-05-11 15:11:44 +00:00
|
|
|
llarp::IpAddress ip(a);
|
2018-12-17 13:25:27 +00:00
|
|
|
|
2020-05-06 20:38:44 +00:00
|
|
|
if (!ip.isBogon())
|
2018-07-27 00:21:57 +00:00
|
|
|
{
|
|
|
|
ifname = i->ifa_name;
|
2020-04-07 18:38:56 +00:00
|
|
|
found = true;
|
2018-07-27 00:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-03 14:32:51 +00:00
|
|
|
});
|
2018-07-27 00:21:57 +00:00
|
|
|
return found;
|
|
|
|
}
|
2018-09-03 12:03:43 +00:00
|
|
|
|
2019-07-03 14:32:51 +00:00
|
|
|
// TODO: ipv6?
|
2020-06-24 13:24:07 +00:00
|
|
|
std::optional<IPRange>
|
2019-07-03 14:32:51 +00:00
|
|
|
FindFreeRange()
|
2018-10-03 10:35:39 +00:00
|
|
|
{
|
2020-06-24 13:24:07 +00:00
|
|
|
std::list<IPRange> currentRanges;
|
2019-07-09 19:20:01 +00:00
|
|
|
IterAllNetworkInterfaces([&](ifaddrs* i) {
|
2020-04-07 18:38:56 +00:00
|
|
|
if (i && i->ifa_addr)
|
2019-07-03 14:32:51 +00:00
|
|
|
{
|
|
|
|
const auto fam = i->ifa_addr->sa_family;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (fam != AF_INET)
|
2019-07-03 14:32:51 +00:00
|
|
|
return;
|
2019-07-30 23:42:13 +00:00
|
|
|
auto* addr = (sockaddr_in*)i->ifa_addr;
|
|
|
|
auto* mask = (sockaddr_in*)i->ifa_netmask;
|
2019-07-03 14:32:51 +00:00
|
|
|
nuint32_t ifaddr{addr->sin_addr.s_addr};
|
|
|
|
nuint32_t ifmask{mask->sin_addr.s_addr};
|
2019-09-10 02:28:23 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
// do not delete, otherwise GCC will do horrible things to this lambda
|
|
|
|
LogDebug("found ", ifaddr, " with mask ", ifmask);
|
|
|
|
#endif
|
2020-04-07 18:38:56 +00:00
|
|
|
if (addr->sin_addr.s_addr)
|
2019-09-10 02:28:23 +00:00
|
|
|
// skip unconfig'd adapters (windows passes these through the unix-y
|
|
|
|
// wrapper)
|
2020-05-20 22:48:13 +00:00
|
|
|
currentRanges.emplace_back(
|
|
|
|
IPRange{net::ExpandV4(xntohl(ifaddr)), net::ExpandV4(xntohl(ifmask))});
|
2019-07-03 14:32:51 +00:00
|
|
|
}
|
|
|
|
});
|
2021-10-22 19:53:19 +00:00
|
|
|
auto ownsRange = [¤tRanges](const IPRange& range) -> bool {
|
2020-06-24 13:24:07 +00:00
|
|
|
for (const auto& ownRange : currentRanges)
|
2019-07-03 14:32:51 +00:00
|
|
|
{
|
2021-10-22 19:53:19 +00:00
|
|
|
if (ownRange * range)
|
2020-06-24 13:24:07 +00:00
|
|
|
return true;
|
2019-07-03 14:32:51 +00:00
|
|
|
}
|
2020-06-24 13:24:07 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
// generate possible ranges to in order of attempts
|
|
|
|
std::list<IPRange> possibleRanges;
|
2020-07-01 14:14:45 +00:00
|
|
|
for (byte_t oct = 16; oct < 32; ++oct)
|
2020-06-24 13:24:07 +00:00
|
|
|
{
|
2020-07-01 14:14:45 +00:00
|
|
|
possibleRanges.emplace_back(IPRange::FromIPv4(172, oct, 0, 1, 16));
|
2018-10-03 10:35:39 +00:00
|
|
|
}
|
2020-06-24 13:24:07 +00:00
|
|
|
for (byte_t oct = 0; oct < 255; ++oct)
|
2019-09-09 12:10:26 +00:00
|
|
|
{
|
2020-07-01 14:14:45 +00:00
|
|
|
possibleRanges.emplace_back(IPRange::FromIPv4(10, oct, 0, 1, 16));
|
2019-09-09 12:10:26 +00:00
|
|
|
}
|
2020-06-24 13:24:07 +00:00
|
|
|
for (byte_t oct = 0; oct < 255; ++oct)
|
2019-09-09 12:10:26 +00:00
|
|
|
{
|
2020-07-01 14:14:45 +00:00
|
|
|
possibleRanges.emplace_back(IPRange::FromIPv4(192, 168, oct, 1, 24));
|
2020-06-24 13:24:07 +00:00
|
|
|
}
|
|
|
|
// for each possible range pick the first one we don't own
|
|
|
|
for (const auto& range : possibleRanges)
|
|
|
|
{
|
|
|
|
if (not ownsRange(range))
|
|
|
|
return range;
|
2019-09-09 12:10:26 +00:00
|
|
|
}
|
2020-05-04 16:51:57 +00:00
|
|
|
return std::nullopt;
|
2018-10-03 10:35:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-04 16:51:57 +00:00
|
|
|
std::optional<std::string>
|
2019-07-03 14:32:51 +00:00
|
|
|
FindFreeTun()
|
2018-10-03 10:35:39 +00:00
|
|
|
{
|
2020-05-04 16:51:57 +00:00
|
|
|
int num = 0;
|
2020-04-07 18:38:56 +00:00
|
|
|
while (num < 255)
|
2018-10-03 10:35:39 +00:00
|
|
|
{
|
2022-07-16 00:41:14 +00:00
|
|
|
std::string iftestname = fmt::format("lokitun{}", num);
|
2020-05-04 16:51:57 +00:00
|
|
|
bool found = llarp_getifaddr(iftestname.c_str(), AF_INET, nullptr);
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!found)
|
2018-10-03 10:35:39 +00:00
|
|
|
{
|
2020-05-04 16:51:57 +00:00
|
|
|
return iftestname;
|
2018-10-03 10:35:39 +00:00
|
|
|
}
|
|
|
|
num++;
|
|
|
|
}
|
2020-05-04 16:51:57 +00:00
|
|
|
return std::nullopt;
|
2018-10-03 10:35:39 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 15:01:05 +00:00
|
|
|
std::optional<SockAddr>
|
2021-02-17 11:37:21 +00:00
|
|
|
GetInterfaceAddr(const std::string& ifname, int af)
|
2018-09-03 12:03:43 +00:00
|
|
|
{
|
|
|
|
sockaddr_storage s;
|
2020-05-04 18:39:14 +00:00
|
|
|
sockaddr* sptr = (sockaddr*)&s;
|
2021-02-16 15:59:18 +00:00
|
|
|
sptr->sa_family = af;
|
2020-04-07 18:38:56 +00:00
|
|
|
if (!llarp_getifaddr(ifname.c_str(), af, sptr))
|
2020-05-04 18:39:14 +00:00
|
|
|
return std::nullopt;
|
2021-02-22 15:01:05 +00:00
|
|
|
return SockAddr{*sptr};
|
2018-09-03 12:03:43 +00:00
|
|
|
}
|
|
|
|
|
2021-02-16 15:59:18 +00:00
|
|
|
std::optional<huint128_t>
|
2021-02-17 11:37:21 +00:00
|
|
|
GetInterfaceIPv6Address(std::string ifname)
|
2021-02-16 15:59:18 +00:00
|
|
|
{
|
|
|
|
sockaddr_storage s;
|
|
|
|
sockaddr* sptr = (sockaddr*)&s;
|
|
|
|
sptr->sa_family = AF_INET6;
|
|
|
|
if (!llarp_getifaddr(ifname.c_str(), AF_INET6, sptr))
|
|
|
|
return std::nullopt;
|
|
|
|
llarp::SockAddr addr{*sptr};
|
|
|
|
return addr.asIPv6();
|
|
|
|
}
|
|
|
|
|
2022-05-20 17:10:04 +00:00
|
|
|
namespace net
|
2018-09-03 12:03:43 +00:00
|
|
|
{
|
2022-05-20 17:10:04 +00:00
|
|
|
namespace
|
2018-09-03 12:03:43 +00:00
|
|
|
{
|
2022-05-20 17:10:04 +00:00
|
|
|
SockAddr
|
|
|
|
All(int af)
|
|
|
|
{
|
|
|
|
if (af == AF_INET)
|
|
|
|
{
|
|
|
|
sockaddr_in addr{};
|
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
addr.sin_port = htons(0);
|
|
|
|
return SockAddr{addr};
|
|
|
|
}
|
2022-05-26 14:57:39 +00:00
|
|
|
if (af == AF_INET6)
|
|
|
|
{
|
|
|
|
sockaddr_in6 addr6{};
|
|
|
|
addr6.sin6_family = AF_INET6;
|
|
|
|
addr6.sin6_port = htons(0);
|
|
|
|
addr6.sin6_addr = IN6ADDR_ANY_INIT;
|
|
|
|
return SockAddr{addr6};
|
|
|
|
}
|
2022-07-16 00:41:14 +00:00
|
|
|
throw std::invalid_argument{fmt::format("{} is not a valid address family", af)};
|
2022-05-20 17:10:04 +00:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
std::optional<SockAddr>
|
|
|
|
AllInterfaces(SockAddr pub)
|
2018-09-03 12:03:43 +00:00
|
|
|
{
|
2022-05-20 17:10:04 +00:00
|
|
|
std::optional<SockAddr> found;
|
|
|
|
IterAllNetworkInterfaces([pub, &found](auto* ifa) {
|
|
|
|
if (found)
|
|
|
|
return;
|
|
|
|
if (auto ifa_addr = ifa->ifa_addr)
|
|
|
|
{
|
|
|
|
if (ifa_addr->sa_family != pub.Family())
|
|
|
|
return;
|
|
|
|
|
|
|
|
SockAddr addr{*ifa->ifa_addr};
|
|
|
|
|
|
|
|
if (addr == pub)
|
|
|
|
found = addr;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// 0.0.0.0 is used in our compat shim as our public ip so we check for that special case
|
|
|
|
const auto zero = IPRange::FromIPv4(0, 0, 0, 0, 8);
|
|
|
|
// when we cannot find an address but we are looking for 0.0.0.0 just default to the old style
|
|
|
|
if (not found and (pub.isIPv4() and zero.Contains(pub.asIPv4())))
|
|
|
|
found = All(pub.Family());
|
|
|
|
return found;
|
2018-09-03 12:03:43 +00:00
|
|
|
}
|
2022-05-20 17:10:04 +00:00
|
|
|
} // namespace net
|
2018-09-03 12:03:43 +00:00
|
|
|
|
2021-04-26 14:40:10 +00:00
|
|
|
#if !defined(TESTNET)
|
|
|
|
static constexpr std::array bogonRanges_v6 = {
|
|
|
|
// zero
|
|
|
|
IPRange{huint128_t{0}, netmask_ipv6_bits(128)},
|
|
|
|
// loopback
|
|
|
|
IPRange{huint128_t{1}, netmask_ipv6_bits(128)},
|
|
|
|
// yggdrasil
|
|
|
|
IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)},
|
|
|
|
// multicast
|
|
|
|
IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
|
|
|
|
// local
|
|
|
|
IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
|
|
|
|
// local
|
|
|
|
IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}};
|
|
|
|
|
|
|
|
static constexpr std::array bogonRanges_v4 = {
|
|
|
|
IPRange::FromIPv4(0, 0, 0, 0, 8),
|
|
|
|
IPRange::FromIPv4(10, 0, 0, 0, 8),
|
|
|
|
IPRange::FromIPv4(100, 64, 0, 0, 10),
|
|
|
|
IPRange::FromIPv4(127, 0, 0, 0, 8),
|
|
|
|
IPRange::FromIPv4(169, 254, 0, 0, 16),
|
|
|
|
IPRange::FromIPv4(172, 16, 0, 0, 12),
|
|
|
|
IPRange::FromIPv4(192, 0, 0, 0, 24),
|
|
|
|
IPRange::FromIPv4(192, 0, 2, 0, 24),
|
|
|
|
IPRange::FromIPv4(192, 88, 99, 0, 24),
|
|
|
|
IPRange::FromIPv4(192, 168, 0, 0, 16),
|
|
|
|
IPRange::FromIPv4(198, 18, 0, 0, 15),
|
|
|
|
IPRange::FromIPv4(198, 51, 100, 0, 24),
|
|
|
|
IPRange::FromIPv4(203, 0, 113, 0, 24),
|
|
|
|
IPRange::FromIPv4(224, 0, 0, 0, 4),
|
|
|
|
IPRange::FromIPv4(240, 0, 0, 0, 4)};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2018-10-15 12:02:32 +00:00
|
|
|
bool
|
|
|
|
IsBogon(const in6_addr& addr)
|
|
|
|
{
|
2019-05-19 14:39:50 +00:00
|
|
|
#if defined(TESTNET)
|
2018-11-08 15:15:02 +00:00
|
|
|
(void)addr;
|
2018-10-21 14:19:49 +00:00
|
|
|
return false;
|
|
|
|
#else
|
2021-04-26 14:40:10 +00:00
|
|
|
if (not ipv6_is_mapped_ipv4(addr))
|
2020-05-04 19:05:33 +00:00
|
|
|
{
|
2021-04-26 14:40:10 +00:00
|
|
|
const auto ip = net::In6ToHUInt(addr);
|
|
|
|
for (const auto& range : bogonRanges_v6)
|
|
|
|
{
|
|
|
|
if (range.Contains(ip))
|
|
|
|
return true;
|
|
|
|
}
|
2018-10-15 12:02:32 +00:00
|
|
|
return false;
|
2020-05-04 19:05:33 +00:00
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
return IsIPv4Bogon(
|
|
|
|
ipaddr_ipv4_bits(addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]));
|
2018-10-21 14:19:49 +00:00
|
|
|
#endif
|
2018-10-15 12:02:32 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 14:18:23 +00:00
|
|
|
bool
|
|
|
|
IsBogon(const huint128_t ip)
|
|
|
|
{
|
|
|
|
const nuint128_t netIP{ntoh128(ip.h)};
|
|
|
|
in6_addr addr{};
|
|
|
|
std::copy_n((const uint8_t*)&netIP.n, 16, &addr.s6_addr[0]);
|
|
|
|
return IsBogon(addr);
|
|
|
|
}
|
|
|
|
|
2018-10-15 12:02:32 +00:00
|
|
|
bool
|
2019-04-08 18:21:01 +00:00
|
|
|
IsBogonRange(const in6_addr& host, const in6_addr&)
|
2018-10-15 12:02:32 +00:00
|
|
|
{
|
|
|
|
// TODO: implement me
|
2019-04-08 18:21:01 +00:00
|
|
|
return IsBogon(host);
|
2018-10-15 12:02:32 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 19:19:12 +00:00
|
|
|
#if !defined(TESTNET)
|
2018-10-15 12:02:32 +00:00
|
|
|
bool
|
|
|
|
IsIPv4Bogon(const huint32_t& addr)
|
|
|
|
{
|
2021-04-26 14:40:10 +00:00
|
|
|
for (const auto& bogon : bogonRanges_v4)
|
2018-10-15 12:02:32 +00:00
|
|
|
{
|
2020-06-24 13:24:07 +00:00
|
|
|
if (bogon.Contains(addr))
|
2018-10-15 12:02:32 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-20 19:19:12 +00:00
|
|
|
#else
|
|
|
|
bool
|
2020-05-20 22:48:13 +00:00
|
|
|
IsIPv4Bogon(const huint32_t&)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-20 19:19:12 +00:00
|
|
|
#endif
|
2022-05-20 17:10:04 +00:00
|
|
|
bool
|
|
|
|
HasInterfaceAddress(std::variant<nuint32_t, nuint128_t> ip)
|
|
|
|
{
|
|
|
|
bool found{false};
|
|
|
|
IterAllNetworkInterfaces([ip, &found](const auto* iface) {
|
|
|
|
if (found or iface == nullptr)
|
|
|
|
return;
|
|
|
|
if (auto addr = iface->ifa_addr;
|
|
|
|
addr and (addr->sa_family == AF_INET or addr->sa_family == AF_INET6))
|
|
|
|
{
|
|
|
|
found = SockAddr{*iface->ifa_addr}.getIP() == ip;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return found;
|
|
|
|
}
|
2020-05-20 19:19:12 +00:00
|
|
|
|
2018-08-08 12:36:10 +00:00
|
|
|
} // namespace llarp
|