Skip to content

Commit c458c8e

Browse files
committed
added functoin to net utils to get all ip and netmask details for all adapters
1 parent 73310af commit c458c8e

4 files changed

Lines changed: 336 additions & 6 deletions

File tree

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"request": "launch",
2222
"program": "${input:unitTestProgramWin}",
2323
"args": [
24-
"--gtest_filter=GoogleFlatBuffers.testCase_flatbufferOverTcp",
24+
"--gtest_filter=AsioTest.testCase_testNetUtils_GetIpAndSubnetMasks",
2525
"--gtest_break_on_failure",
2626
"--gtest_catch_exceptions=0"
2727
],

Include/Asio/NetworkUtils.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838

3939
namespace core_lib
4040
{
41-
41+
namespace net_utils
42+
{
4243
using ip_octets_t = std::vector<uint8_t>;
4344

4445
// Give an IP address, e.g. 192.168.1.1, get a vector of the octets,
@@ -116,6 +117,9 @@ CORE_LIBRARY_DLL_SHARED_API int32_t SubnetMaskToCidrPrefix(std::string_view subn
116117
CORE_LIBRARY_DLL_SHARED_API int32_t SubnetMaskToCidrPrefix(std::string const& subnetMask);
117118
#endif
118119

120+
// Convert a CIDR prefix length to a uint32_t subnet mask in network byte order.
121+
CORE_LIBRARY_DLL_SHARED_API uint32_t PrefixToMaskNetworkOrder(uint8_t prefixLength);
122+
119123
// Create a CIDR address from an IP and subnet mask.
120124
// e.g. 192.168.10.1/255.255.0.0 becomes
121125
// 192.168.10.1/16
@@ -158,7 +162,31 @@ CORE_LIBRARY_DLL_SHARED_API std::pair<std::string, std::string> GetIpAddressAndN
158162
CORE_LIBRARY_DLL_SHARED_API std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string const& adapterName);
159163
#endif
160164

165+
struct IPv4Address
166+
{
167+
std::string address;
168+
std::string netmask;
169+
};
170+
171+
struct IPv4Adapter
172+
{
173+
std::string name;
174+
bool supportsBroadcast{false};
175+
bool supportsMulticast{false};
176+
std::vector<IPv4Address> addresses;
177+
};
178+
179+
enum class eInterfaceFilter
180+
{
181+
All,
182+
RealOnly
183+
};
184+
185+
// Get a list of all the IPv4 adapters on the system. If filter is eInterfaceFilter::RealOnly then
186+
// only return real adapters and not loopback, tunnel etc. interfaces.
187+
CORE_LIBRARY_DLL_SHARED_API std::vector<IPv4Adapter> GetIPv4Adapters(eInterfaceFilter filter = eInterfaceFilter::All);
161188

189+
} // namespace net_utils
162190
} // namespace core_lib
163191

164192
#endif // HGLQTNETWORKUTILS_H

Source/Asio/NetworkUtils.cpp

Lines changed: 273 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <iterator>
77
#include <limits>
88
#include <bitset>
9+
#include <unordered_map>
910
#if BOOST_OS_LINUX
1011
#include <system_error> // For std::error_code.
1112
#include <cstring> // For calls in GetIpAddressAndNetmask
@@ -33,6 +34,8 @@
3334

3435
namespace core_lib
3536
{
37+
namespace net_utils
38+
{
3639

3740
#if defined(IS_CPP17)
3841
ip_octets_t OctetsFromIpAddress(std::string_view ipAddress)
@@ -210,6 +213,22 @@ int32_t SubnetMaskToCidrPrefix(std::string const& subnetMask)
210213
return static_cast<int32_t>(maskBits.count());
211214
}
212215

216+
uint32_t PrefixToMaskNetworkOrder(uint8_t prefixLength)
217+
{
218+
if (prefixLength == 0)
219+
{
220+
return 0;
221+
}
222+
223+
if (prefixLength >= 32)
224+
{
225+
return 0xFFFFFFFFu;
226+
}
227+
228+
return htonl(0xFFFFFFFFu << (32 - prefixLength));
229+
}
230+
231+
213232
#if defined(IS_CPP17)
214233
std::string MakeCidrAddress(std::string_view address, std::string_view subnetMask)
215234
#else
@@ -277,8 +296,8 @@ bool IsAddressAndNetmaskOnSameSubnetAsAdapter(std::string const& ipAddress,
277296
try
278297
{
279298
auto tempNetMask = netmask.empty() ? adapterNetmask : netmask;
280-
auto broadcastAddress1 = core_lib::BuildBroadcastAddress(ipAddress, tempNetMask);
281-
auto broadcastAddress2 = core_lib::BuildBroadcastAddress(adapterAddress, adapterNetmask);
299+
auto broadcastAddress1 = BuildBroadcastAddress(ipAddress, tempNetMask);
300+
auto broadcastAddress2 = BuildBroadcastAddress(adapterAddress, adapterNetmask);
282301

283302
return broadcastAddress1 == broadcastAddress2;
284303
}
@@ -290,9 +309,9 @@ bool IsAddressAndNetmaskOnSameSubnetAsAdapter(std::string const& ipAddress,
290309
}
291310

292311
#if defined(IS_CPP17)
293-
std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string_view adapterName);
312+
std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string_view adapterName)
294313
#else
295-
std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string const& adapterName);
314+
std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string const& adapterName)
296315
#endif
297316
#if BOOST_OS_LINUX
298317
{
@@ -417,4 +436,254 @@ std::pair<std::string, std::string> GetIpAddressAndNetmask(std::string const& ad
417436
}
418437
#endif
419438

439+
bool IsBadIPv4HostOrder(uint32_t hostIp)
440+
{
441+
if (hostIp == 0)
442+
return true;
443+
444+
if ((hostIp & 0xFF000000u) == 0x7F000000u)
445+
{
446+
return true;
447+
}
448+
449+
if ((hostIp & 0xFFFF0000u) == 0xA9FE0000u)
450+
{
451+
return true;
452+
}
453+
454+
return false;
455+
}
456+
457+
std::string IPv4ToString(const in_addr& address)
458+
{
459+
char buffer[INET_ADDRSTRLEN]{};
460+
461+
if (!inet_ntop(AF_INET, &address, buffer, sizeof(buffer)))
462+
{
463+
return {};
464+
}
465+
466+
return buffer;
467+
}
468+
469+
#if BOOST_OS_LINUX
470+
bool IsInterfaceUp(const ifaddrs* iface)
471+
{
472+
return iface && (iface->ifa_flags & IFF_UP);
473+
}
474+
475+
bool SupportsBroadcast(const ifaddrs* iface)
476+
{
477+
return iface && (iface->ifa_flags & IFF_BROADCAST);
478+
}
479+
480+
bool SupportsMulticast(const ifaddrs* iface)
481+
{
482+
return iface && (iface->ifa_flags & IFF_MULTICAST);
483+
}
484+
485+
std::vector<IPv4Adapter> GetIPv4Adapters(eInterfaceFilter filter = eInterfaceFilter::All)
486+
{
487+
std::vector<IPv4Adapter> adapters;
488+
std::unordered_map<std::string, size_t> adapterIndex;
489+
490+
ifaddrs* interfaceList = nullptr;
491+
492+
if (getifaddrs(&interfaceList) != 0 || !interfaceList)
493+
{
494+
return adapters;
495+
}
496+
497+
for (auto* iface = interfaceList; iface; iface = iface->ifa_next)
498+
{
499+
if (!iface->ifa_name)
500+
{
501+
continue;
502+
}
503+
504+
if (!iface->ifa_addr || !iface->ifa_netmask)
505+
{
506+
continue;
507+
}
508+
509+
if (iface->ifa_addr->sa_family != AF_INET)
510+
{
511+
continue;
512+
}
513+
514+
if (filter == eInterfaceFilter::RealOnly && !IsInterfaceUp(iface))
515+
{
516+
continue;
517+
}
518+
519+
auto* address = reinterpret_cast<sockaddr_in*>(iface->ifa_addr);
520+
auto* mask = reinterpret_cast<sockaddr_in*>(iface->ifa_netmask);
521+
522+
uint32_t hostIp = ntohl(address->sin_addr.s_addr);
523+
524+
if (filter == eInterfaceFilter::RealOnly && IsBadIPv4HostOrder(hostIp))
525+
{
526+
continue;
527+
}
528+
529+
std::string ipString = IPv4ToString(address->sin_addr);
530+
std::string maskString = IPv4ToString(mask->sin_addr);
531+
532+
if (ipString.empty() || maskString.empty())
533+
{
534+
continue;
535+
}
536+
537+
std::string adapterName = iface->ifa_name;
538+
size_t index;
539+
540+
auto it = adapterIndex.find(adapterName);
541+
542+
if (it == adapterIndex.end())
543+
{
544+
index = adapters.size();
545+
adapterIndex.emplace(adapterName, index);
546+
547+
IPv4Adapter record;
548+
record.name = adapterName;
549+
record.supportsBroadcast = SupportsBroadcast(iface);
550+
record.supportsMulticast = SupportsMulticast(iface);
551+
552+
adapters.push_back(std::move(record));
553+
}
554+
else
555+
{
556+
index = it->second;
557+
558+
adapters[index].supportsBroadcast =
559+
adapters[index].supportsBroadcast || SupportsBroadcast(iface);
560+
561+
adapters[index].supportsMulticast =
562+
adapters[index].supportsMulticast || SupportsMulticast(iface);
563+
}
564+
565+
adapters[index].addresses.push_back({ipString, maskString});
566+
}
567+
568+
freeifaddrs(interfaceList);
569+
570+
adapters.erase(
571+
std::remove_if(
572+
adapters.begin(),
573+
adapters.end(),
574+
[](const IPv4Adapter& adapter)
575+
{
576+
return adapter.addresses.empty();
577+
}),
578+
adapters.end());
579+
580+
return adapters;
581+
}
582+
#else
583+
bool IsAdapterUp(const IP_ADAPTER_ADDRESSES* adapter)
584+
{
585+
return adapter && adapter->OperStatus == IfOperStatusUp;
586+
}
587+
588+
inline bool SupportsMulticast(const IP_ADAPTER_ADDRESSES* adapter)
589+
{
590+
return adapter && ((adapter->Flags & IP_ADAPTER_NO_MULTICAST) == 0);
591+
}
592+
593+
std::vector<IPv4Adapter> GetIPv4Adapters(eInterfaceFilter filter)
594+
{
595+
std::vector<IPv4Adapter> adapters;
596+
597+
ULONG bufferSize = 0;
598+
599+
GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufferSize);
600+
601+
if (bufferSize == 0)
602+
{
603+
return adapters;
604+
}
605+
606+
std::vector<unsigned char> buffer(bufferSize);
607+
608+
auto* adapterList = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buffer.data());
609+
610+
if (GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, adapterList, &bufferSize) != NO_ERROR)
611+
{
612+
return adapters;
613+
}
614+
615+
for (auto* adapter = adapterList; adapter; adapter = adapter->Next)
616+
{
617+
if ((filter == eInterfaceFilter::RealOnly) && !IsAdapterUp(adapter))
618+
{
619+
continue;
620+
}
621+
622+
IPv4Adapter record;
623+
624+
record.name = string_utils::WStringToString(adapter->FriendlyName);
625+
626+
if (record.name.empty())
627+
{
628+
continue;
629+
}
630+
631+
record.supportsMulticast = SupportsMulticast(adapter);
632+
633+
for (auto* address = adapter->FirstUnicastAddress; address; address = address->Next)
634+
{
635+
auto* socketAddress = address->Address.lpSockaddr;
636+
637+
if (!socketAddress || (socketAddress->sa_family != AF_INET))
638+
{
639+
continue;
640+
}
641+
642+
auto* ipv4 = reinterpret_cast<sockaddr_in*>(socketAddress);
643+
644+
uint32_t hostIp = ntohl(ipv4->sin_addr.s_addr);
645+
646+
if ((filter == eInterfaceFilter::RealOnly) && IsBadIPv4HostOrder(hostIp))
647+
{
648+
continue;
649+
}
650+
651+
std::string ipString = IPv4ToString(ipv4->sin_addr);
652+
653+
if (ipString.empty())
654+
{
655+
continue;
656+
}
657+
658+
uint32_t maskNetwork = PrefixToMaskNetworkOrder(address->OnLinkPrefixLength);
659+
660+
in_addr maskAddr{};
661+
maskAddr.s_addr = maskNetwork;
662+
663+
std::string maskString = IPv4ToString(maskAddr);
664+
665+
if (maskString.empty())
666+
{
667+
continue;
668+
}
669+
670+
record.addresses.push_back({ipString, maskString});
671+
}
672+
673+
if (record.addresses.empty())
674+
{
675+
continue;
676+
}
677+
678+
// Broadcast policy — matches your KM loopback usage
679+
record.supportsBroadcast = true;
680+
681+
adapters.emplace_back(std::move(record));
682+
}
683+
684+
return adapters;
685+
}
686+
#endif // #if BOOST_OS_LINUX
687+
688+
} // namespace net_utils
420689
} // namespace core_lib

0 commit comments

Comments
 (0)