+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _NETWORK_STATE_INFORMATION_LOGGING_H
+#define _NETWORK_STATE_INFORMATION_LOGGING_H
+
+#include <Availability.h>
+#include <TargetConditionals.h>
+#include <sys/cdefs.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+
+#include <network_information.h>
+#include "network_state_information_priv.c"
+
+__BEGIN_DECLS
+
+#ifndef my_log
+#define my_log(__level, __format, ...) SC_log(__level, __format, ## __VA_ARGS__)
+#define MY_LOG_DEFINED_LOCALLY
+#endif // !my_log
+
+#ifndef my_log_context_type
+#define my_log_context_type void *
+#define MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY
+#endif // !my_log_context_type
+
+#ifndef my_log_context_name
+#define my_log_context_name context
+#define MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY
+#endif // !my_log_context_name
+
+#include "SCNetworkReachabilityLogging.h"
+
+/*
+ * _nwi_ifstate_flags_str()
+ *
+ * Returns a string representation of the nwi_state flags.
+ * "(IPv4,IPv6,DNS,...)"
+ */
+static __inline__ void
+_nwi_ifstate_flags_str(nwi_ifstate_flags flags, char *str, size_t len)
+{
+ size_t n;
+ nwi_ifstate_flags remaining;
+
+ assert(len >= sizeof("(0x01234567)")); // check min buffer size
+
+ flags &= NWI_IFSTATE_FLAGS_MASK;
+ if (flags == 0) {
+ str[0] = '\0';
+ return;
+ }
+
+ len--; // leave room for the closing paren
+ n = strlcpy(str, "(", len);
+ remaining = flags;
+
+ if ((remaining & NWI_IFSTATE_FLAGS_HAS_IPV4) &&
+ (n < len) && ((len - n) > sizeof("IPv4,"))) {
+ n = strlcat(str, "IPv4,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_HAS_IPV4;
+ }
+
+ if ((remaining & NWI_IFSTATE_FLAGS_HAS_IPV6) &&
+ (n < len) && ((len - n) > sizeof("IPv6,"))) {
+ n = strlcat(str, "IPv6,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_HAS_IPV6;
+ }
+
+ if ((remaining & NWI_IFSTATE_FLAGS_HAS_DNS) &&
+ (n < len) && ((len - n) > sizeof("DNS,"))) {
+ n = strlcat(str, "DNS,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_HAS_DNS;
+ }
+
+ if ((remaining & NWI_IFSTATE_FLAGS_NOT_IN_LIST) &&
+ (n < len) && ((len - n) > sizeof("NOT-IN-LIST,"))) {
+ n = strlcat(str, "NOT-IN-LIST,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_NOT_IN_LIST;
+ }
+
+ if ((remaining & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) &&
+ (n < len) && ((len - n) > sizeof("SIGNATURE,"))) {
+ n = strlcat(str, "SIGNATURE,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_HAS_SIGNATURE;
+ }
+
+ if ((remaining & NWI_IFSTATE_FLAGS_NOT_IN_IFLIST) &&
+ (n < len) && ((len - n) > sizeof("NOT-IN-IFLIST,"))) {
+ n = strlcat(str, "NOT-IN-IFLIST,", len);
+ remaining &= ~NWI_IFSTATE_FLAGS_NOT_IN_IFLIST;
+ }
+
+ if (remaining != 0) {
+ if ((n >= len) ||
+ ((len - n) <= sizeof("0x01234567,"))) {
+ // if we don't have enough space, truncate and start over
+ n = strlcpy(str, "(", len);
+ remaining = flags;
+ }
+
+ n += snprintf(str + n, len - n, ",%p", (void *)remaining);
+ }
+
+ if (n-- > 0) {
+ str[n] = ')'; // trailing "," --> ")"
+ }
+
+ return;
+}
+
+static __inline__ const char *
+_nwi_ifstate_rank_str(Rank rank)
+{
+ const char *str = "???";
+
+ switch (RANK_ASSERTION_MASK(rank)) {
+ case kRankAssertionFirst:
+ str = "First";
+ break;
+ case kRankAssertionDefault:
+ str = "Default";
+ break;
+ case kRankAssertionLast:
+ str = "Last";
+ break;
+ case kRankAssertionNever:
+ str = "Never";
+ break;
+ case kRankAssertionScoped:
+ str = "Scoped";
+ break;
+ default:
+ str = "???";
+ break;
+ }
+
+ return str;
+}
+
+static __inline__ void
+_nwi_ifstate_log(nwi_ifstate_t ifstate, boolean_t debug, my_log_context_type my_log_context_name)
+{
+#if defined(MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY) && defined(MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY)
+#pragma unused(my_log_context_name)
+#endif
+ union {
+ const void *bytes;
+ const struct in_addr *ia;
+ const struct in6_addr *ia6;
+ } addr;
+ char addr_str[INET6_ADDRSTRLEN];
+ nwi_ifstate_flags flags_ifstate;
+ char flags_str[100];
+ const char *if_name;
+ SCNetworkReachabilityFlags reach_flags;
+ char reach_str[100];
+ const struct sockaddr *vpn_addr;
+
+ // nwi_ifstate flags
+ flags_ifstate = nwi_ifstate_get_flags(ifstate);
+ if (debug) {
+ flags_ifstate |= NWI_IFSTATE_FLAGS(ifstate->flags);
+ }
+ flags_ifstate &= ~NWI_IFSTATE_FLAGS_HAS_SIGNATURE; // exclude flag ('cause we'll report the signature only if present)
+ _nwi_ifstate_flags_str(flags_ifstate, flags_str, sizeof(flags_str));
+
+ // if_name
+ if_name = nwi_ifstate_get_ifname(ifstate);
+
+ // reachability flags
+ reach_flags = nwi_ifstate_get_reachability_flags(ifstate);
+
+ // IP address
+ addr.bytes = nwi_ifstate_get_address(ifstate);
+ if (inet_ntop(ifstate->af, addr.bytes, addr_str, sizeof(addr_str)) == NULL) {
+ strlcpy(addr_str, "???", sizeof(addr_str));
+ }
+
+ // verbose format (e.g. scutil)
+ my_log(LOG_INFO, " %7s : flags : %p %s",
+ if_name,
+ (void *)flags_ifstate,
+ flags_str);
+
+ my_log(LOG_INFO, " address : %s", addr_str);
+
+ // VPN server address
+ vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
+ if (vpn_addr != NULL) {
+ char vpn_str[INET6_ADDRSTRLEN];
+
+ _SC_sockaddr_to_string(vpn_addr, vpn_str, sizeof(vpn_str));
+ my_log(LOG_INFO, " VPN server : %s", vpn_str);
+ }
+
+ // reachability
+ __SCNetworkReachability_flags_string(reach_flags, TRUE, reach_str, sizeof(reach_str));
+ my_log(LOG_INFO, " reach : %s", reach_str);
+
+ if (debug) {
+ uint64_t generation;
+ Rank rank;
+ uint32_t rank_index;
+ const char *rank_str;
+ const uint8_t *signature;
+ int signature_length;
+
+ // Rank
+ rank = ifstate->rank;
+ rank_str = _nwi_ifstate_rank_str(rank);
+ rank_index = RANK_INDEX_MASK(rank);
+ if (rank_index != kRankIndexMask) {
+ my_log(LOG_INFO, " rank : 0x%08x (%s, %u)", rank, rank_str, rank_index);
+ } else {
+ my_log(LOG_INFO, " rank : 0x%08x (%s, Last)", rank, rank_str);
+ }
+
+ // signature
+ signature = nwi_ifstate_get_signature(ifstate, AF_UNSPEC, &signature_length);
+ if (signature != NULL) {
+ CFDataRef digest;
+
+ digest = CFDataCreate(NULL, signature, CC_SHA1_DIGEST_LENGTH);
+ my_log(LOG_INFO, " signature : %@", digest);
+ CFRelease(digest);
+ }
+
+ // generation
+ generation = nwi_ifstate_get_generation(ifstate);
+ my_log(LOG_INFO, " generation : %llu", generation);
+ }
+
+ return;
+}
+
+static __inline__ void
+_nwi_state_reachability_log(nwi_state_t state, boolean_t debug, int af, my_log_context_type my_log_context_name)
+{
+#if defined(MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY) && defined(MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY)
+#pragma unused(my_log_context_name)
+#endif
+ uint32_t flags;
+ char flags_str[100];
+
+ flags = nwi_state_get_reachability_flags(state, af);
+ __SCNetworkReachability_flags_string(flags, TRUE, flags_str, sizeof(flags_str));
+ if (!debug) {
+ my_log(LOG_INFO, "%s", "");
+ }
+ my_log(LOG_INFO, " REACH : flags %s", flags_str);
+
+ return;
+}
+
+static __inline__ void
+_nwi_state_log(nwi_state_t state, boolean_t debug, my_log_context_type my_log_context_name)
+{
+#if defined(MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY) && defined(MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY)
+#pragma unused(my_log_context_name)
+#endif
+ unsigned int count;
+ nwi_ifindex_t i;
+ nwi_ifstate_t ifstate;
+
+ if (!debug) {
+ my_log(LOG_INFO, "%s", "Network information");
+ } else {
+ my_log(LOG_INFO,
+ "Network information (generation %llu size=%lu)",
+ nwi_state_get_generation(state),
+ nwi_state_size(state));
+ }
+
+ // report IPv4 state
+ if (!debug) {
+ // show regular interfaces
+ my_log(LOG_INFO, "%s", "");
+ my_log(LOG_INFO, "%s", "IPv4 network interface information");
+ ifstate = nwi_state_get_first_ifstate(state, AF_INET);
+ if (ifstate == NULL) {
+ my_log(LOG_INFO, "%s", " No IPv4 states found");
+ } else {
+ while (ifstate != NULL) {
+ _nwi_ifstate_log(ifstate, debug, my_log_context_name);
+ ifstate = nwi_ifstate_get_next(ifstate, AF_INET);
+ }
+ }
+ } else {
+ my_log(LOG_INFO, "%s", "IPv4 network interface information");
+ if (state->ipv4_count > 0) {
+ // show ALL interfaces
+ for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET);
+ i < state->ipv4_count; i++, ifstate++) {
+ _nwi_ifstate_log(ifstate, debug, my_log_context_name);
+ }
+ } else {
+ my_log(LOG_INFO, "%s", " No IPv4 states found");
+ }
+ }
+ _nwi_state_reachability_log(state, debug, AF_INET, my_log_context_name);
+
+ // report IPv6 state
+ if (!debug) {
+ // show regular interfaces
+ my_log(LOG_INFO, "%s", "");
+ my_log(LOG_INFO, "%s", "IPv6 network interface information");
+ ifstate = nwi_state_get_first_ifstate(state, AF_INET6);
+ if (ifstate == NULL) {
+ my_log(LOG_INFO, "%s", " No IPv6 states found\n");
+ } else {
+ while (ifstate != NULL) {
+ _nwi_ifstate_log(ifstate, debug, my_log_context_name);
+ ifstate = nwi_ifstate_get_next(ifstate, AF_INET6);
+ }
+ }
+ } else {
+ my_log(LOG_INFO, "%s", "IPv6 network interface information");
+ if (state->ipv6_count > 0) {
+ // show ALL interfaces
+ for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET6);
+ i < state->ipv6_count; i++, ifstate++) {
+ _nwi_ifstate_log(ifstate, debug, my_log_context_name);
+ }
+ } else {
+ my_log(LOG_INFO, "%s", " No IPv6 states found\n");
+ }
+ }
+ _nwi_state_reachability_log(state, debug, AF_INET6, my_log_context_name);
+
+ count = nwi_state_get_interface_names(state, NULL, 0);
+ if (count > 0) {
+ const char *names[count];
+
+ count = nwi_state_get_interface_names(state, names, count);
+ if (count > 0) {
+ char str[count * (IFNAMSIZ + 1)];
+
+ memset(str, 0, sizeof(str));
+ for (unsigned int i = 0; i < count; i++) {
+ if (i != 0) {
+ strlcat(str, " ", sizeof(str));
+ }
+ strlcat(str, names[i], sizeof(str));
+ }
+
+ if (!debug) {
+ my_log(LOG_INFO, "%s", "");
+ }
+ my_log(LOG_INFO, "Network interfaces: %s", str);
+ }
+ }
+
+ return;
+}
+
+#ifdef MY_LOG_DEFINED_LOCALLY
+#undef my_log
+#undef MY_LOG_DEFINED_LOCALLY
+#endif // MY_LOG_DEFINED_LOCALLY
+
+#ifdef MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY
+#undef my_log_context_type
+#undef MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY
+#endif // MY_LOG_CONTEXT_TYPE_DEFINED_LOCALLY
+
+#ifdef MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY
+#undef my_log_context_name
+#undef MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY
+#endif // MY_LOG_CONTEXT_NAME_DEFINED_LOCALLY
+
+__END_DECLS
+
+#endif // _NETWORK_STATE_INFORMATION_LOGGING_H