如何获取 Mac OS 网络配置



Background

我正在开发监视公司网络中机器的应用程序。 每台机器都会运行我的应用程序,它会跟踪许多关于机器状态的事情。

我要跟踪的一件事是网络配置。

  • 网络接口列表
  • IPv4
  • IPv6
  • 名字

问题

我没有问题获取网络设备列表(SCNetworkInterfaceCopyAll(,或获取其MAC地址或BSD名称。

我在获取有关两种协议(IPv4 和 IPv6(的计算机 IP 地址的信息时遇到问题。

我尝试使用 SCNetworkInterfaceGetConfiguration 和 SCNetworkInterfaceGetExtendedConfiguration,但我只有空值,SCError 返回 kSCStatusInvalidArgument。

SCNetworkInterfaceGetExtendedConfiguration的情况下,我使用了值:kSCEntNetIPSeckSCEntNetIPv4kSCEntNetIPv6

文档不精确和清晰,所以我找到了一些使用此 API 的项目,这给了我一些提示,但仍然没有帮助。

我做错了什么?

这里有一些我用来探索 API 的测试代码(这是我将应用程序移植到 Mac C++代码(:

#include <iostream>
#include <string>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPreferences.h>
#include <SystemConfiguration/SCNetwork.h>
#include <SystemConfiguration/SCNetworkReachability.h>
#include <SystemConfiguration/SCNetworkConnection.h>
#include <SystemConfiguration/SCNetworkConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>
inline void myRelease(CFTypeRef p)
{
if (p) CFRelease(p);
}
inline std::string toStd(CFStringRef s)
{
if (!s) {
return {};
}
if (auto fastCString = CFStringGetCStringPtr(s, kCFStringEncodingUTF8)) {
return fastCString;
}
auto len = CFStringGetLength(s);
auto size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
std::string result(size, '');
CFStringGetBytes(s, { 0, len }, kCFStringEncodingUTF8, '?', 0,
reinterpret_cast<UInt8 *>(&result[0]), result.size(), &size);
result.resize(size);
return result;
}
std::ostream &operator<<(std::ostream &out, CFStringRef s) {
return out << toStd(s);
}
struct DebugCf {
DebugCf(CFTypeRef p) : mP(p) {}
std::string toString() const {
if (!mP) {
return "<null>";
}
auto idOfType = CFGetTypeID(mP);
if (CFStringGetTypeID() == idOfType) {
return toStd((CFStringRef)mP);
}
auto s = CFCopyDescription(mP);
auto result = toStd(s);
CFRelease(s);
return result;
}
private:
CFTypeRef mP;
};
std::ostream &operator<<(std::ostream &out, const DebugCf d) {
return out << d.toString();
}
#define LOGCF(x) " " #x "["<< DebugCf(x) << "], "
std::string SCErrorString()
{
switch (SCError()) {
case kSCStatusOK: return "OK";
case kSCStatusFailed: return "Failed";
case kSCStatusInvalidArgument: return "InvalidArgument";
case kSCStatusAccessError: return "AccessError";
case kSCStatusNoKey: return "NoKey";
case kSCStatusKeyExists: return "KeyExists";
case kSCStatusLocked: return "Locked";
case kSCStatusNeedLock: return "NeedLock";
case kSCStatusNoStoreSession: return "NoStoreSession";
case kSCStatusNoStoreServer: return "NoStoreServer";
case kSCStatusNotifierActive: return "NotifierActive";
case kSCStatusNoPrefsSession: return "NoPrefsSession";
case kSCStatusPrefsBusy: return "PrefsBusy";
case kSCStatusNoConfigFile: return "NoConfigFile";
case kSCStatusNoLink: return "NoLink";
case kSCStatusStale: return "Stale";
case kSCStatusMaxLink: return "MaxLink";
case kSCStatusReachabilityUnknown: return "ReachabilityUnknown";
default:
return std::to_string(SCError());
}
}
void exploreInterface(SCNetworkInterfaceRef netInterface) {
auto macString = SCNetworkInterfaceGetHardwareAddressString(netInterface);
auto bsdName = SCNetworkInterfaceGetBSDName(netInterface);
std::cout << bsdName << ' ' << macString << 'n';
CFDictionaryRef current = NULL;
CFDictionaryRef active = NULL;
CFArrayRef available = NULL;
auto result = SCNetworkInterfaceCopyMediaOptions(netInterface,
&current,
&active,
&available,
1);
std::cout << "SCNetworkInterfaceCopyMediaOptions: "
<< (bool)result << " e=" << SCErrorString()
// << LOGCF(current) << LOGCF(active) << LOGCF(available)
<< 'n';
auto interfaceConfig = SCNetworkInterfaceGetConfiguration(netInterface);
std::cout << "SCNetworkInterfaceGetConfiguration: " << LOGCF(interfaceConfig)
<< "e = " << SCErrorString() << 'n';

auto interfaceExtConfig = SCNetworkInterfaceGetExtendedConfiguration(netInterface, kSCEntNetIPSec);
std::cout << "SCNetworkInterfaceGetExtendedConfiguration: " << LOGCF(interfaceExtConfig)
<< "e = " << SCErrorString() << 'n';
std::cout << "SCNetworkInterfaceGetInterfaceType: "
<< SCNetworkInterfaceGetInterfaceType(netInterface)
<< "  SCNetworkInterfaceGetLocalizedDisplayName: "
<< SCNetworkInterfaceGetLocalizedDisplayName(netInterface) << 'n';
auto supportedProtocols = SCNetworkInterfaceGetSupportedProtocolTypes(netInterface);
std::cout << "SCNetworkInterfaceGetSupportedProtocolTypes: "
<< LOGCF(supportedProtocols) << 'n';
myRelease(current);
myRelease(active);
myRelease(available);
}
int main(int argc, const char * argv[]) {
auto allNetwork = SCNetworkInterfaceCopyAll();
auto count = CFArrayGetCount(allNetwork);
for (CFIndex i=0; i<count; ++i) {
auto netInterface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(allNetwork, i);
exploreInterface(netInterface);
std::cout << "-------------------------n";
}
CFRelease(allNetwork);
return 0;
}

感谢@Hofi和@johnelemans的帮助。这个提示足以找到答案。 对于可能遇到类似问题的其他人:

要获取Mac地址,必须使用SCDynamicStore。从中我可以获取稍后需要的BSD名称来获取连接详细信息。

现在使用SCDynamicStore API有点奇怪。 要探索可以做什么,可以使用命令行工具:scutil。 最好在没有参数的情况下运行它并键入以下命令:

list
list State:/Network/Interfaces/.*
get State:/Network/Interface/en0/IPv6
d.show

我在这里找到了例子。

根据从此工具获得的信息,我提出了如何获取所需数据。 C++测试代码:

#include <iostream>
#include <string>
#include <SystemConfiguration/SystemConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>
inline void myRelease(CFTypeRef p)
{
if (p) CFRelease(p);
}
inline std::string toStd(CFStringRef s)
{
if (!s) {
return {};
}
if (auto fastCString = CFStringGetCStringPtr(s, kCFStringEncodingUTF8)) {
return fastCString;
}
auto len = CFStringGetLength(s);
auto size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s), kCFStringEncodingUTF8) + 1;
std::string result(size, '');
CFStringGetBytes(s, { 0, len }, kCFStringEncodingUTF8, '?', 0,
reinterpret_cast<UInt8 *>(&result[0]), result.size(), &size);
result.resize(size);
return result;
}
std::ostream &operator<<(std::ostream &out, CFStringRef s) {
return out << toStd(s);
}
struct DebugCf {
DebugCf(CFTypeRef p) : mP(p) {}
std::string toString() const {
if (!mP) {
return "<null>";
}
auto idOfType = CFGetTypeID(mP);
if (CFStringGetTypeID() == idOfType) {
return toStd((CFStringRef)mP);
}
auto s = CFCopyDescription(mP);
auto result = toStd(s);
CFRelease(s);
return result;
}
private:
CFTypeRef mP;
};
std::ostream &operator<<(std::ostream &out, const DebugCf d) {
return out << d.toString();
}
#define LOGCF(x) " " #x "["<< DebugCf(x) << "], "
std::string SCErrorString()
{
switch (SCError()) {
case kSCStatusOK: return "OK";
case kSCStatusFailed: return "Failed";
case kSCStatusInvalidArgument: return "InvalidArgument";
case kSCStatusAccessError: return "AccessError";
case kSCStatusNoKey: return "NoKey";
case kSCStatusKeyExists: return "KeyExists";
case kSCStatusLocked: return "Locked";
case kSCStatusNeedLock: return "NeedLock";
case kSCStatusNoStoreSession: return "NoStoreSession";
case kSCStatusNoStoreServer: return "NoStoreServer";
case kSCStatusNotifierActive: return "NotifierActive";
case kSCStatusNoPrefsSession: return "NoPrefsSession";
case kSCStatusPrefsBusy: return "PrefsBusy";
case kSCStatusNoConfigFile: return "NoConfigFile";
case kSCStatusNoLink: return "NoLink";
case kSCStatusStale: return "Stale";
case kSCStatusMaxLink: return "MaxLink";
case kSCStatusReachabilityUnknown: return "ReachabilityUnknown";
default:
return std::to_string(SCError());
}
}
void exploreServiceQuery(CFStringRef query, CFStringRef serviceId, SCDynamicStoreRef scSession) {
auto resolvedQuery =
CFStringCreateWithFormat(kCFAllocatorDefault,
NULL,
query, serviceId);
auto dic = (CFDictionaryRef)SCDynamicStoreCopyValue(scSession,
resolvedQuery);
std::cout << resolvedQuery << " - " << LOGCF(dic) << 'n';
myRelease(dic);
CFRelease(resolvedQuery);
}
void printIpv4Data(SCDynamicStoreRef scSession,
CFStringRef bsdName) {
exploreServiceQuery(CFSTR("State:/Network/Interface/%@/IPv4"),
bsdName,
scSession);
}
void printIpv6Data(SCDynamicStoreRef scSession,
CFStringRef bsdName) {
exploreServiceQuery(CFSTR("State:/Network/Interface/%@/IPv6"),
bsdName,
scSession);
}
void desiriedData() {
auto allInterfaces = SCNetworkInterfaceCopyAll();
auto scSession = SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("Custom"),
NULL,
NULL);
auto count = CFArrayGetCount(allInterfaces);
for (CFIndex i = 0; i < count; ++i) {
auto netInterface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(allInterfaces, i);
auto macString = SCNetworkInterfaceGetHardwareAddressString(netInterface);
auto bsdName = SCNetworkInterfaceGetBSDName(netInterface);
std::cout << bsdName << " " << macString << 'n';
printIpv4Data(scSession, bsdName);
printIpv6Data(scSession, bsdName);
}
CFRelease(scSession);
CFRelease(allInterfaces);
}
int main(int argc, const char * argv[]) {
desiriedData();
return 0;
}

最新更新