ZeroTierOne/diagnostic/node_state_interfaces_apple.cpp
Aaron Johnson 45e3223591 feat: Add comprehensive JSON diagnostic output with schema validation
Implements structured JSON diagnostic output for node state export with full
schema documentation. This feature provides machine-readable diagnostics for
automated analysis, monitoring, and AI/MCP integration.

Key changes:
- Add `zerotier-cli diagnostic` command for JSON node state export
- Add `zerotier-cli dump -j` as alias for JSON output
- Add `zerotier-cli diagnostic --schema` to print JSON schema
- Implement platform-specific interface collection (Linux, BSD, macOS, Windows)
- Create modular diagnostic/ directory with isolated try/catch error handling
- Add comprehensive JSON schema (diagnostic_schema.json) for validation
- Include build-time schema embedding for offline access
- Add Python and Rust scripts for schema embedding during build
- Update build systems to compile new diagnostic modules

The diagnostic output includes:
- Node configuration and identity
- Network memberships and settings
- Interface states and IP addresses
- Peer connections and statistics
- Moon orbits
- Controller networks (if applicable)

All diagnostic collection is wrapped in try/catch blocks to ensure partial
failures don't prevent overall output generation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-08 12:40:06 -07:00

61 lines
No EOL
2.8 KiB
C++

#include "diagnostic/node_state_interfaces_apple.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <vector>
void addNodeStateInterfacesJson(nlohmann::json& j) {
try {
std::vector<nlohmann::json> interfaces_json;
CFArrayRef interfaces = SCNetworkInterfaceCopyAll();
CFIndex size = CFArrayGetCount(interfaces);
for(CFIndex i = 0; i < size; ++i) {
SCNetworkInterfaceRef iface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(interfaces, i);
char stringBuffer[512] = {};
CFStringRef tmp = SCNetworkInterfaceGetBSDName(iface);
CFStringGetCString(tmp,stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
std::string ifName(stringBuffer);
int mtuCur, mtuMin, mtuMax;
SCNetworkInterfaceCopyMTU(iface, &mtuCur, &mtuMin, &mtuMax);
nlohmann::json iface_json;
iface_json["name"] = ifName;
iface_json["mtu"] = mtuCur;
tmp = SCNetworkInterfaceGetHardwareAddressString(iface);
CFStringGetCString(tmp, stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
iface_json["mac"] = stringBuffer;
tmp = SCNetworkInterfaceGetInterfaceType(iface);
CFStringGetCString(tmp, stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
iface_json["type"] = stringBuffer;
std::vector<std::string> addresses;
struct ifaddrs *ifap, *ifa;
void *addr;
getifaddrs(&ifap);
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (strcmp(ifName.c_str(), ifa->ifa_name) == 0) {
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in*)ifa->ifa_addr;
addr = &ipv4->sin_addr;
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)ifa->ifa_addr;
addr = &ipv6->sin6_addr;
} else {
continue;
}
inet_ntop(ifa->ifa_addr->sa_family, addr, stringBuffer, sizeof(stringBuffer));
addresses.push_back(stringBuffer);
}
}
iface_json["addresses"] = addresses;
interfaces_json.push_back(iface_json);
}
j["network_interfaces"] = interfaces_json;
} catch (const std::exception& e) {
j["network_interfaces"] = std::string("Exception: ") + e.what();
} catch (...) {
j["network_interfaces"] = "Unknown error retrieving interfaces";
}
}