Skip to content

Commit 422fb90

Browse files
tstennercboulay
authored andcommitted
Enumerate network interfaces
1 parent 215058f commit 422fb90

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ add_library(lslobj OBJECT
5454
src/lsl_outlet_c.cpp
5555
src/lsl_streaminfo_c.cpp
5656
src/lsl_xml_element_c.cpp
57+
src/netinterfaces.h
58+
src/netinterfaces.cpp
5759
src/portable_archive/portable_archive_exception.hpp
5860
src/portable_archive/portable_archive_includes.hpp
5961
src/portable_archive/portable_iarchive.hpp
@@ -145,6 +147,7 @@ target_link_libraries(lslboost
145147
Threads::Threads
146148
PRIVATE
147149
$<$<PLATFORM_ID:Windows>:bcrypt>
150+
$<$<PLATFORM_ID:Windows>:iphlpapi>
148151
)
149152
target_compile_features(lslboost PUBLIC cxx_std_11 cxx_lambda_init_captures)
150153

src/netinterfaces.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include "netinterfaces.h"
2+
#include "loguru/loguru.hpp"
3+
4+
using lslboost::asio::ip::address_v4;
5+
6+
lslboost::asio::ip::address_v6 sinaddr_to_asio(sockaddr_in6 *addr) {
7+
lslboost::asio::ip::address_v6::bytes_type buf;
8+
memcpy(buf.data(), addr->sin6_addr.s6_addr, sizeof(addr->sin6_addr));
9+
return lslboost::asio::ip::make_address_v6(buf, addr->sin6_scope_id);
10+
}
11+
12+
#if defined(_WIN32)
13+
#undef UNICODE
14+
#include <winsock2.h>
15+
// Headers that need to be included after winsock2.h:
16+
#include <iphlpapi.h>
17+
#include <ws2ipdef.h>
18+
19+
typedef IP_ADAPTER_UNICAST_ADDRESS_LH Addr;
20+
typedef IP_ADAPTER_ADDRESSES *AddrList;
21+
22+
std::vector<lsl::netif> lsl::get_local_interfaces() {
23+
// It's a windows machine, we assume it has 512KB free memory
24+
DWORD outBufLen = 1 << 19;
25+
AddrList ifaddrs = (AddrList) new char[outBufLen];
26+
27+
std::vector<lsl::netif> ret;
28+
29+
ULONG res = GetAdaptersAddresses(AF_UNSPEC,
30+
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, ifaddrs,
31+
&outBufLen);
32+
33+
if (res == NO_ERROR) {
34+
for (AddrList addr = ifaddrs; addr != 0; addr = addr->Next) {
35+
// Interface isn't up or doesn't support multicast? Skip it.
36+
LOG_F(INFO, "netif '%s' (status: %d, multicast: %d", addr->AdapterName,
37+
addr->OperStatus, !addr->NoMulticast);
38+
if (addr->OperStatus != IfOperStatusUp) continue;
39+
if (addr->NoMulticast) continue;
40+
41+
lsl::netif if_;
42+
if_.name = addr->AdapterName;
43+
if_.ifindex = addr->IfIndex;
44+
45+
// Find the first IPv4 address
46+
if (addr->Ipv4Enabled) {
47+
for (Addr *uaddr = addr->FirstUnicastAddress; uaddr != 0; uaddr = uaddr->Next) {
48+
if (uaddr->Address.lpSockaddr->sa_family != AF_INET) continue;
49+
50+
if_.addr = lslboost::asio::ip::make_address_v4(
51+
ntohl(reinterpret_cast<sockaddr_in *>(uaddr->Address.lpSockaddr)
52+
->sin_addr.s_addr));
53+
ret.emplace_back(std::move(if_));
54+
break;
55+
}
56+
}
57+
58+
if (addr->Ipv6Enabled) {
59+
LOG_F(INFO, "\tIPv6 ifindex %d", if_.ifindex);
60+
for (Addr *uaddr = addr->FirstUnicastAddress; uaddr != 0; uaddr = uaddr->Next) {
61+
if (uaddr->Address.lpSockaddr->sa_family != AF_INET6) continue;
62+
63+
if_.addr = sinaddr_to_asio(
64+
reinterpret_cast<sockaddr_in6 *>(uaddr->Address.lpSockaddr));
65+
ret.emplace_back(std::move(if_));
66+
break;
67+
}
68+
}
69+
}
70+
} else {
71+
LOG_F(ERROR, "Couldn't enumerate network interfaces: %d", res);
72+
}
73+
delete[]((char *)ifaddrs);
74+
return ret;
75+
}
76+
#elif defined(__APPLE__) || defined(__linux__)
77+
#include <arpa/inet.h>
78+
#include <ifaddrs.h>
79+
#include <map>
80+
#include <net/if.h>
81+
#include <sys/types.h>
82+
83+
std::vector<lsl::netif> lsl::get_local_interfaces() {
84+
std::vector<lsl::netif> res;
85+
ifaddrs *ifs;
86+
if (getifaddrs(&ifs)) {
87+
LOG_F(ERROR, "Couldn't enumerate network interfaces: %d", errno);
88+
return res;
89+
}
90+
for (auto addr = ifs; addr != nullptr; addr = addr->ifa_next) {
91+
// No address? Skip.
92+
if (addr->ifa_addr == nullptr) continue;
93+
LOG_F(INFO, "netif '%s' (status: %d, multicast: %d, broadcast: %d)", addr->ifa_name,
94+
addr->ifa_flags & IFF_MULTICAST, addr->ifa_flags & IFF_UP,
95+
addr->ifa_flags & IFF_BROADCAST);
96+
// Interface doesn't support multicast? Skip.
97+
if (!(addr->ifa_flags & IFF_MULTICAST)) continue;
98+
// Interface isn't active? Skip.
99+
if (!(addr->ifa_flags & IFF_UP)) continue;
100+
101+
lsl::netif if_;
102+
103+
if (addr->ifa_addr->sa_family == AF_INET) {
104+
if_.addr = lslboost::asio::ip::make_address_v4(
105+
ntohl(reinterpret_cast<sockaddr_in *>(addr->ifa_addr)->sin_addr.s_addr));
106+
LOG_F(INFO, "\tIPv4 addr: %x", if_.addr.to_v4().to_uint());
107+
} else if (addr->ifa_addr->sa_family == AF_INET6) {
108+
if_.addr = sinaddr_to_asio(reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr));
109+
LOG_F(INFO, "\tIPv6 addr: %s", if_.addr.to_string().c_str());
110+
} else
111+
continue;
112+
113+
if_.ifindex = if_nametoindex(addr->ifa_name);
114+
res.emplace_back(std::move(if_));
115+
}
116+
freeifaddrs(ifs);
117+
return res;
118+
}
119+
#else
120+
121+
std::vector<lsl::netif> get_interface_addresses() {
122+
LOG_F(WARN, "No implementation to enumerate network interfaces found.");
123+
return std::vector<lsl::netif>();
124+
}
125+
#endif

src/netinterfaces.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
#include "common.h"
3+
#include <cstdint>
4+
#include <string>
5+
#include <vector>
6+
#include <boost/asio/ip/address.hpp>
7+
8+
namespace lsl {
9+
10+
class netif {
11+
public:
12+
lslboost::asio::ip::address addr;
13+
uint32_t ifindex;
14+
std::string name;
15+
};
16+
17+
/// Enumerate all local interface addresses (IPv6 index, IPv4 address)-pairs
18+
std::vector<netif> get_local_interfaces();
19+
} // namespace lsl

0 commit comments

Comments
 (0)