Compare commits

..
Author SHA1 Message Date
Aaron JohnsonandClaude 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
Aaron Johnson a4e021e04e Refactor dump logic into diagnostic/ with try/catch isolation, prepping for -j feature 2025-06-23 12:48:02 -07:00
Grant Limberg f4053c8af3 Merge pull request #2451 from zerotier/dependabot/cargo/rustybits/crossbeam-channel-0.5.15
Bump crossbeam-channel from 0.5.13 to 0.5.15 in /rustybits
2025-05-02 12:38:07 -07:00
dependabot[bot] bcc766f70d Bump crossbeam-channel from 0.5.13 to 0.5.15 in /rustybits
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.13 to 0.5.15.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.13...crossbeam-channel-0.5.15)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.15
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-10 14:38:32 +00:00
Grant Limberg dcadc2c4d4 Merge pull request #2449 from zerotier/dependabot/cargo/rustybits/openssl-0.10.72
Bump openssl from 0.10.70 to 0.10.72 in /rustybits
2025-04-08 10:36:25 -07:00
Grant Limberg 55ed5d61e0 Merge branch 'dev' into dependabot/cargo/rustybits/openssl-0.10.72 2025-04-08 08:08:28 -07:00
Grant Limberg 86f424130d Merge pull request #2450 from zerotier/dependabot/cargo/rustybits/tokio-1.43.1
Bump tokio from 1.42.0 to 1.43.1 in /rustybits
2025-04-08 08:08:08 -07:00
dependabot[bot] 4e9b69f0a7 Bump tokio from 1.42.0 to 1.43.1 in /rustybits
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.42.0 to 1.43.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.42.0...tokio-1.43.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.43.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 02:02:25 +00:00
dependabot[bot] 0a17738f39 Bump openssl from 0.10.70 to 0.10.72 in /rustybits
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.70 to 0.10.72.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.70...openssl-v0.10.72)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.72
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-04 20:53:49 +00:00
Grant Limberg cadfa0bc4d Merge pull request #2445 from zerotier/docker-local-conf
Allow setting local.conf content from Docker environment variable
2025-04-03 09:42:11 -07:00
Grant Limberg f2521c8536 Allow setting local.conf content from Docker environment variable 2025-03-26 11:23:46 -07:00
Grant Limberg d353e0c6fa Merge pull request #2442 from zerotier/dependabot/cargo/rustybits/ring-0.17.13
Bump ring from 0.17.8 to 0.17.13 in /rustybits
2025-03-19 08:30:23 -07:00
Grant Limberg 346731ea5d Merge branch 'dev' into dependabot/cargo/rustybits/ring-0.17.13 2025-03-11 10:47:33 -07:00
Grant Limberg 2ef38d84d1 Merge pull request #2444 from zerotier:gl/temporal-sdk-fix
temporal sdk fix
2025-03-10 16:39:39 -07:00
Grant Limberg 5630d48a66 temporal sdk fix
missing WorkflowIdConflictPolicy
2025-03-10 16:35:10 -07:00
Grant Limberg b44a9b8b62 Merge pull request #2443 from zerotier:gl/onelogin-fix
Fix AuthInfo Provider not being set
2025-03-10 16:20:06 -07:00
Grant Limberg f5b0fc6a8f Fix AuthInfo Provider not being set 2025-03-10 16:04:06 -07:00
dependabot[bot] a20bc772f2 Bump ring from 0.17.8 to 0.17.13 in /rustybits
Bumps [ring](https://github.com/briansmith/ring) from 0.17.8 to 0.17.13.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 17:04:31 +00:00
Travis LaDuke 010c4b0d64 Merge pull request #2441 from zerotier/laduke/update-actions
Run CI on pull requests
2025-03-05 09:18:20 -08:00
travisladuke 5998f1497a Run CI on pull requests
If the PR is from an external repo,
the action won't run without approval.
right now we can't run the actions on external PRs.
2025-03-03 10:01:33 -08:00
Grant Limberg a2162c01e3 Merge pull request #2430 from zerotier/dependabot/cargo/rustybits/openssl-0.10.70
Bump openssl from 0.10.68 to 0.10.70 in /rustybits
2025-02-04 15:45:25 -08:00
Grant Limberg 870c221690 Merge branch 'dev' into dependabot/cargo/rustybits/openssl-0.10.70 2025-02-04 15:38:52 -08:00
Grant Limberg 87ad848202 Merge pull request #2432 from zerotier:ci-update
Update upload-artifact action to use v4
2025-02-04 15:38:14 -08:00
Grant Limberg b736b6835d Update upload-artifact action to use v4
V3 is no longer supported and throws an error on use
2025-02-04 15:30:25 -08:00
dependabot[bot] 91bec01da8 Bump openssl from 0.10.68 to 0.10.70 in /rustybits
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.68 to 0.10.70.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.68...openssl-v0.10.70)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 18:50:34 +00:00
Grant Limberg b5c51796b9 Merge pull request #2417 from zerotier/rust-deps
update rust dependencies
2024-12-04 14:46:54 -08:00
Grant Limberg 724002f142 update rust dependencies 2024-12-04 13:43:34 -08:00
Grant Limberg 377a9d6f41 Merge pull request #2409 from zerotier/dependabot/cargo/rustybits/rustls-0.23.18
Bump rustls from 0.23.15 to 0.23.18 in /rustybits
2024-11-25 11:27:22 -08:00
dependabot[bot] 003b4cf876 Bump rustls from 0.23.15 to 0.23.18 in /rustybits
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.15 to 0.23.18.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.15...v/0.23.18)

---
updated-dependencies:
- dependency-name: rustls
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-25 17:00:23 +00:00
Joseph Henry b12dd19d44 Merge pull request #2405 from zerotier/jh-fix-openbsd-tap
Build fix for OpenBSD - See ticket #2397
2024-11-13 15:12:18 -08:00
Joseph Henry 82c6454950 Fix broken ifdef on OpenBSD 2024-11-13 09:01:09 -08:00
Joseph Henry 969c0ee6e3 Omit CPU pinning code on OpenBSD 2024-11-12 14:40:24 -08:00
Joseph Henry c092b63987 Merge pull request #2365 from zerotier/laduke/2361-phy-mtu
Lower ZT_MIN_PHYSMTU
2024-11-12 09:38:11 -08:00
Joseph Henry 47efee1df4 Merge branch 'dev' into laduke/2361-phy-mtu 2024-11-12 06:20:29 -08:00
Joseph Henry 18a5452de8 Merge pull request #2395 from zerotier/core-objs
Move osutils/OSUtils.o into CORE_OBJS
2024-11-12 06:20:04 -08:00
Joseph Henry c3794ba8d4 Merge branch 'dev' into core-objs 2024-11-12 06:05:51 -08:00
Joseph Henry 75a5b4438b Build fix for OpenBSD - See ticket #2397 2024-11-12 05:59:49 -08:00
Grant Limberg f959c2f4ca Move osutils/OSUtils.o into CORE_OBJS
When building via `make core` to make libzerotiercore.a, you can't link unless OSUtils.cpp is also built & linked.
2024-10-25 09:32:13 -07:00
travisladuke 55bbd2aec6 Lower ZT_MIN_PHYSMTU
regarding #2361
2024-10-18 15:44:34 -07:00
46 changed files with 1746 additions and 600 deletions

No files matched your search

+4 -1
View File
@@ -1,4 +1,7 @@
on: [ push ]
on:
pull_request:
push:
workflow_dispatch:
jobs:
build_ubuntu:
+2 -1
View File
@@ -1,4 +1,5 @@
on:
pull_request:
push:
workflow_dispatch:
@@ -44,7 +45,7 @@ jobs:
sudo ./.github/workflows/validate-linux.sh
- name: Archive test results
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{github.sha}}-test-results
path: "*test-results*"
+27
View File
@@ -10,3 +10,30 @@ file(GLOB core_src_glob ${PROJ_DIR}/node/*.cpp)
add_library(zerotiercore STATIC ${core_src_glob})
target_compile_options(zerotiercore PRIVATE ${ZT_DEFS})
# Build the Rust embedding tool
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json
COMMAND rustc ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json.rs -o ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json.rs
COMMENT "Building Rust JSON embedding tool"
)
# Embed diagnostic_schema.json as a C string
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic/diagnostic_schema_embed.c
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json ${CMAKE_CURRENT_SOURCE_DIR}/../diagnostic/diagnostic_schema.json ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic/diagnostic_schema_embed.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../diagnostic/diagnostic_schema.json ${CMAKE_CURRENT_SOURCE_DIR}/ci/scripts/embed_json
COMMENT "Embedding diagnostic_schema.json as C string"
)
set(DIAGNOSTIC_SCHEMA_EMBED_SRC
diagnostic/diagnostic_schema_embed.c
diagnostic/diagnostic_schema_embed.h
)
# Add the generated source to your main target (replace <your_target> with actual target name)
target_sources(zerotiercore PRIVATE
${DIAGNOSTIC_SCHEMA_EMBED_SRC}
)
+1
View File
@@ -64,6 +64,7 @@ You can control a few settings including the identity used and the authtoken use
- `ZEROTIER_API_SECRET`: replaces the `authtoken.secret` before booting and allows you to manage the control socket's authentication key.
- `ZEROTIER_IDENTITY_PUBLIC`: the `identity.public` file for zerotier-one. Use `zerotier-idtool` to generate one of these for you.
- `ZEROTIER_IDENTITY_SECRET`: the `identity.secret` file for zerotier-one. Use `zerotier-idtool` to generate one of these for you.
- `ZEROTIER_LOCAL_CONF`: Sets the the `local.conf` file content for zerotier-one
### Tips
+9
View File
@@ -195,3 +195,12 @@ Then visit [http://localhost:9993/app/app1/](http://localhost:9993/app/app1/) an
Requests to paths don't exist return the app root index.html, as is customary for SPAs.
If you want, you can write some javascript that talks to the service or controller [api](https://docs.zerotier.com/service/v1).
## Diagnostic Output Documentation
The diagnostic output (used by `zerotier-cli diagnostic` and `zerotier-cli dump -j`) is documented in the [diagnostic/](diagnostic/) directory:
- [diagnostic_output.md](diagnostic/diagnostic_output.md): Field descriptions, example output, and integration notes
- [diagnostic_schema.json](diagnostic/diagnostic_schema.json): JSON Schema for validation and integration
See these files for details on the output format and how to integrate with MCP, AI, or other automated systems.
+30
View File
@@ -0,0 +1,30 @@
import sys
import os
import json
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <input.json> <output.c>")
sys.exit(1)
input_path = sys.argv[1]
output_path = sys.argv[2]
with open(input_path, 'r', encoding='utf-8') as f:
data = f.read()
# Optionally, minify JSON to save space
try:
minified = json.dumps(json.loads(data), separators=(",", ":"))
except Exception:
minified = data
c_array = ','.join(str(ord(c)) for c in minified)
header = "#include \"diagnostic_schema_embed.h\"\n\n"
array_decl = f"const char ZT_DIAGNOSTIC_SCHEMA_JSON[] = \"{minified.replace('\\', '\\\\').replace('"', '\\"').replace(chr(10), '\\n').replace(chr(13), '')}\";\n"
len_decl = f"const unsigned int ZT_DIAGNOSTIC_SCHEMA_JSON_LEN = sizeof(ZT_DIAGNOSTIC_SCHEMA_JSON) - 1;\n"
with open(output_path, 'w', encoding='utf-8') as out:
out.write(header)
out.write(array_decl)
out.write(len_decl)
+39
View File
@@ -0,0 +1,39 @@
use std::env;
use std::fs;
use std::io::Write;
use std::path::Path;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
eprintln!("Usage: {} <input.json> <output.c>", args[0]);
std::process::exit(1);
}
let input_path = &args[1];
let output_path = &args[2];
let data = fs::read_to_string(input_path).expect("Failed to read input file");
// Minify JSON
let minified = match serde_json::from_str::<serde_json::Value>(&data) {
Ok(json) => serde_json::to_string(&json).unwrap_or(data.clone()),
Err(_) => data.clone(),
};
let escaped = minified
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\r', "");
let header = "#include \"diagnostic_schema_embed.h\"\n\n";
let array_decl = format!(
"const char ZT_DIAGNOSTIC_SCHEMA_JSON[] = \"{}\";\n",
escaped
);
let len_decl = "const unsigned int ZT_DIAGNOSTIC_SCHEMA_JSON_LEN = sizeof(ZT_DIAGNOSTIC_SCHEMA_JSON) - 1;\n";
let mut out = fs::File::create(output_path).expect("Failed to create output file");
out.write_all(header.as_bytes()).unwrap();
out.write_all(array_decl.as_bytes()).unwrap();
out.write_all(len_decl.as_bytes()).unwrap();
}
+2 -1
View File
@@ -1548,7 +1548,8 @@ void EmbeddedNetworkController::_request(
authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_SSO_PROVIDER, info.ssoProvider.c_str());
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
}
DB::cleanMember(member);
+5
View File
@@ -0,0 +1,5 @@
#include "diagnostic_schema_embed.h"
// This file will be auto-generated at build time from diagnostic/diagnostic_schema.json
const char ZT_DIAGNOSTIC_SCHEMA_JSON[] = "PLACEHOLDER: schema will be embedded here";
const unsigned int ZT_DIAGNOSTIC_SCHEMA_JSON_LEN = sizeof(ZT_DIAGNOSTIC_SCHEMA_JSON) - 1;
+5
View File
@@ -0,0 +1,5 @@
#pragma once
// Embedded diagnostic_schema.json
extern const char ZT_DIAGNOSTIC_SCHEMA_JSON[];
extern const unsigned int ZT_DIAGNOSTIC_SCHEMA_JSON_LEN;
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <sstream>
void dumpInterfaces(std::stringstream& dump);
+52
View File
@@ -0,0 +1,52 @@
#include "diagnostic/dump_interfaces.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
void dumpInterfaces(std::stringstream& dump) {
CFArrayRef interfaces = SCNetworkInterfaceCopyAll();
CFIndex size = CFArrayGetCount(interfaces);
for(CFIndex i = 0; i < size; ++i) {
SCNetworkInterfaceRef iface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(interfaces, i);
dump << "Interface " << i << "\n-----------\n";
CFStringRef tmp = SCNetworkInterfaceGetBSDName(iface);
char stringBuffer[512] = {};
CFStringGetCString(tmp,stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
dump << "Name: " << stringBuffer << "\n";
std::string ifName(stringBuffer);
int mtuCur, mtuMin, mtuMax;
SCNetworkInterfaceCopyMTU(iface, &mtuCur, &mtuMin, &mtuMax);
dump << "MTU: " << mtuCur << "\n";
tmp = SCNetworkInterfaceGetHardwareAddressString(iface);
CFStringGetCString(tmp, stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
dump << "MAC: " << stringBuffer << "\n";
tmp = SCNetworkInterfaceGetInterfaceType(iface);
CFStringGetCString(tmp, stringBuffer, sizeof(stringBuffer), kCFStringEncodingUTF8);
dump << "Type: " << stringBuffer << "\n";
dump << "Addresses:" << "\n";
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));
dump << stringBuffer << "\n";
}
}
dump << "\n";
}
}
+53
View File
@@ -0,0 +1,53 @@
#include "diagnostic/dump_interfaces.hpp"
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <sstream>
void dumpInterfaces(std::stringstream& dump) {
struct ifaddrs *ifap, *ifa;
if (getifaddrs(&ifap) != 0) {
dump << "ERROR: getifaddrs failed\n";
return;
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) continue;
dump << "Interface: " << ifa->ifa_name << "\n";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
struct ifreq ifr;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
if (ioctl(sock, SIOCGIFMTU, &ifr) == 0) {
dump << "MTU: " << ifr.ifr_mtu << "\n";
}
if (ifa->ifa_addr->sa_family == AF_LINK) {
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa->ifa_addr;
unsigned char* mac = (unsigned char*)LLADDR(sdl);
char macStr[32];
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
dump << "MAC: " << macStr << "\n";
}
close(sock);
}
dump << "Addresses:\n";
if (ifa->ifa_addr->sa_family == AF_INET) {
char addr[INET_ADDRSTRLEN];
struct sockaddr_in* sa = (struct sockaddr_in*)ifa->ifa_addr;
inet_ntop(AF_INET, &(sa->sin_addr), addr, INET_ADDRSTRLEN);
dump << addr << "\n";
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
char addr[INET6_ADDRSTRLEN];
struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ifa->ifa_addr;
inet_ntop(AF_INET6, &(sa6->sin6_addr), addr, INET6_ADDRSTRLEN);
dump << addr << "\n";
}
dump << "\n";
}
freeifaddrs(ifap);
}
+69
View File
@@ -0,0 +1,69 @@
#include "diagnostic/dump_interfaces.hpp"
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sstream>
void dumpInterfaces(std::stringstream& dump) {
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
struct ifconf ifc;
char buf[1024];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
ioctl(sock, SIOCGIFCONF, &ifc);
struct ifreq *it = ifc.ifc_req;
const struct ifreq * const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for(; it != end; ++it) {
struct ifreq ifr;
strcpy(ifr.ifr_name, it->ifr_name);
if(ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (!(ifr.ifr_flags & IFF_LOOPBACK)) { // skip loopback
dump << "Interface: " << ifr.ifr_name << "\n";
if (ioctl(sock, SIOCGIFMTU, &ifr) == 0) {
dump << "MTU: " << ifr.ifr_mtu << "\n";
}
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
unsigned char mac_addr[6];
memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6);
char macStr[18];
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0],
mac_addr[1],
mac_addr[2],
mac_addr[3],
mac_addr[4],
mac_addr[5]);
dump << "MAC: " << macStr << "\n";
}
dump << "Addresses:" << "\n";
struct ifaddrs *ifap, *ifa;
void *addr;
getifaddrs(&ifap);
for(ifa = ifap; ifa; ifa = ifa->ifa_next) {
if(strcmp(ifr.ifr_name, ifa->ifa_name) == 0 && ifa->ifa_addr != NULL) {
char stringBuffer[128];
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));
dump << stringBuffer << "\n";
}
}
dump << "\n";
}
}
}
close(sock);
}
+53
View File
@@ -0,0 +1,53 @@
#include "diagnostic/dump_interfaces.hpp"
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <sstream>
void dumpInterfaces(std::stringstream& dump) {
struct ifaddrs *ifap, *ifa;
if (getifaddrs(&ifap) != 0) {
dump << "ERROR: getifaddrs failed\n";
return;
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) continue;
dump << "Interface: " << ifa->ifa_name << "\n";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
struct ifreq ifr;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
if (ioctl(sock, SIOCGIFMTU, &ifr) == 0) {
dump << "MTU: " << ifr.ifr_mtu << "\n";
}
if (ifa->ifa_addr->sa_family == AF_LINK) {
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa->ifa_addr;
unsigned char* mac = (unsigned char*)LLADDR(sdl);
char macStr[32];
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
dump << "MAC: " << macStr << "\n";
}
close(sock);
}
dump << "Addresses:\n";
if (ifa->ifa_addr->sa_family == AF_INET) {
char addr[INET_ADDRSTRLEN];
struct sockaddr_in* sa = (struct sockaddr_in*)ifa->ifa_addr;
inet_ntop(AF_INET, &(sa->sin_addr), addr, INET_ADDRSTRLEN);
dump << addr << "\n";
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
char addr[INET6_ADDRSTRLEN];
struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ifa->ifa_addr;
inet_ntop(AF_INET6, &(sa6->sin6_addr), addr, INET6_ADDRSTRLEN);
dump << addr << "\n";
}
dump << "\n";
}
freeifaddrs(ifap);
}
+67
View File
@@ -0,0 +1,67 @@
#include "diagnostic/dump_interfaces.hpp"
#include <winsock2.h>
#include <windows.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#include <string>
void dumpInterfaces(std::stringstream& dump) {
ULONG buffLen = 16384;
PIP_ADAPTER_ADDRESSES addresses;
ULONG ret = 0;
do {
addresses = (PIP_ADAPTER_ADDRESSES)malloc(buffLen);
ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, addresses, &buffLen);
if (ret == ERROR_BUFFER_OVERFLOW) {
free(addresses);
addresses = NULL;
}
else {
break;
}
} while (ret == ERROR_BUFFER_OVERFLOW);
int i = 0;
if (ret == NO_ERROR) {
PIP_ADAPTER_ADDRESSES curAddr = addresses;
while (curAddr) {
dump << "Interface " << i << "\n-----------\n";
dump << "Name: " << curAddr->AdapterName << "\n";
dump << "MTU: " << curAddr->Mtu << "\n";
char macBuffer[64] = {};
sprintf(macBuffer, "%02x:%02x:%02x:%02x:%02x:%02x",
curAddr->PhysicalAddress[0],
curAddr->PhysicalAddress[1],
curAddr->PhysicalAddress[2],
curAddr->PhysicalAddress[3],
curAddr->PhysicalAddress[4],
curAddr->PhysicalAddress[5]);
dump << "MAC: " << macBuffer << "\n";
dump << "Type: " << curAddr->IfType << "\n";
dump << "Addresses:" << "\n";
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
pUnicast = curAddr->FirstUnicastAddress;
if (pUnicast) {
for (int j = 0; pUnicast != NULL; ++j) {
char buf[128] = {};
DWORD bufLen = 128;
LPSOCKADDR a = pUnicast->Address.lpSockaddr;
WSAAddressToStringA(
pUnicast->Address.lpSockaddr,
pUnicast->Address.iSockaddrLength,
NULL,
buf,
&bufLen
);
dump << buf << "\n";
pUnicast = pUnicast->Next;
}
}
dump << "\n";
curAddr = curAddr->Next;
}
}
if (addresses) {
free(addresses);
addresses = NULL;
}
}
+55
View File
@@ -0,0 +1,55 @@
#include "diagnostic/dump_sections.hpp"
#include "node/InetAddress.hpp"
#include "osdep/OSUtils.hpp"
#include "osdep/Http.hpp"
#include <sstream>
#include <string>
#include <map>
void dumpStatus(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders) {
dump << "status" << ZT_EOL_S << "------" << ZT_EOL_S;
std::map<std::string, std::string> responseHeaders;
std::string responseBody;
unsigned int scode = ZeroTier::Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
if (scode != 200) {
dump << responseBody << ZT_EOL_S;
return;
}
dump << responseBody << ZT_EOL_S;
}
void dumpNetworks(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders) {
dump << ZT_EOL_S << "networks" << ZT_EOL_S << "--------" << ZT_EOL_S;
std::map<std::string, std::string> responseHeaders;
std::string responseBody;
unsigned int scode = ZeroTier::Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
if (scode != 200) {
dump << responseBody << ZT_EOL_S;
return;
}
dump << responseBody << ZT_EOL_S;
}
void dumpPeers(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders) {
dump << ZT_EOL_S << "peers" << ZT_EOL_S << "-----" << ZT_EOL_S;
std::map<std::string, std::string> responseHeaders;
std::string responseBody;
unsigned int scode = ZeroTier::Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
if (scode != 200) {
dump << responseBody << ZT_EOL_S;
return;
}
dump << responseBody << ZT_EOL_S;
}
void dumpLocalConf(std::stringstream& dump, const std::string& homeDir) {
dump << ZT_EOL_S << "local.conf" << ZT_EOL_S << "----------" << ZT_EOL_S;
std::string localConf;
ZeroTier::OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "local.conf").c_str(), localConf);
if (localConf.empty()) {
dump << "None Present" << ZT_EOL_S;
}
else {
dump << localConf << ZT_EOL_S;
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <sstream>
#include <string>
#include <map>
#include "node/InetAddress.hpp"
void dumpStatus(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders);
void dumpNetworks(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders);
void dumpPeers(std::stringstream& dump, const ZeroTier::InetAddress& addr, std::map<std::string,std::string>& requestHeaders);
void dumpLocalConf(std::stringstream& dump, const std::string& homeDir);
@@ -0,0 +1,61 @@
#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";
}
}
@@ -0,0 +1,3 @@
#pragma once
#include <nlohmann/json.hpp>
void addNodeStateInterfacesJson(nlohmann::json& j);
Loaded 20 of 46 files, more files were not shown because too many files have changed in this diff. Show more