nat64-configuration.c [plain text]
#include "nat64-configuration.h"
#include <TargetConditionals.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "ip_plugin.h"
#define INET6 1
#include <string.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <network/nat64.h>
static CFMutableSetRef nat64_prefix_requests = NULL;
static dispatch_queue_t
nat64_dispatch_queue()
{
static dispatch_once_t once;
static dispatch_queue_t q;
dispatch_once(&once, ^{
q = dispatch_queue_create("nat64 prefix request queue", NULL);
});
return q;
}
static __inline__ void
_nat64_prefix_request_complete(const char *if_name,
int32_t num_prefixes,
nw_nat64_prefix_t *prefixes)
{
struct if_nat64req req;
int ret;
int s;
SC_log(LOG_DEBUG, "%s: _nat64_prefix_request_complete", if_name);
bzero(&req, sizeof(req));
strlcpy(req.ifnat64_name, if_name, sizeof(req.ifnat64_name));
if (num_prefixes == 0) {
SC_log(LOG_INFO, "%s: nat64 prefix not (or no longer) available", if_name);
}
for (int32_t i = 0; i < num_prefixes; i++) {
char prefix_str[NW_NAT64_PREFIX_STR_LENGTH] = {0};
nw_nat64_write_prefix_to_string(&prefixes[i], prefix_str, sizeof(prefix_str));
SC_log(LOG_DEBUG, "%s: nat64 prefix[%d] = %s", if_name, i, prefix_str);
if (i < NAT64_MAX_NUM_PREFIXES) {
req.ifnat64_prefixes[i].prefix_len = prefixes[i].length;
bcopy(&prefixes[i].data,
&req.ifnat64_prefixes[i].ipv6_prefix,
MIN(sizeof(req.ifnat64_prefixes[i].ipv6_prefix), sizeof(prefixes[i].data))); }
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
return;
}
ret = ioctl(s, SIOCSIFNAT64PREFIX, &req);
close(s);
if (ret == -1) {
if ((errno != ENOENT) || (num_prefixes != 0)) {
SC_log(LOG_ERR, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name, strerror(errno));
}
return;
}
SC_log(LOG_INFO, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : "");
return;
}
static void
_nat64_prefix_request_start(const void *value)
{
unsigned int if_index;
char *if_name;
CFStringRef interface = (CFStringRef)value;
bool ok;
SC_log(LOG_DEBUG, "%@: _nat64_prefix_request_start", interface);
if_name = _SC_cfstring_to_cstring(interface, NULL, 0, kCFStringEncodingASCII);
if (if_name == NULL) {
SC_log(LOG_NOTICE, "%@: could not convert interface name", interface);
return;
}
if_index = my_if_nametoindex(if_name);
if (if_index == 0) {
SC_log(LOG_NOTICE, "%s: no interface index", if_name);
CFAllocatorDeallocate(NULL, if_name);
return;
}
CFSetAddValue(nat64_prefix_requests, interface);
CFRetain(interface);
ok = nw_nat64_copy_prefixes_async(&if_index,
nat64_dispatch_queue(),
^(int32_t num_prefixes, nw_nat64_prefix_t *prefixes) {
if (num_prefixes >= 0) {
_nat64_prefix_request_complete(if_name, num_prefixes, prefixes);
} else {
SC_log(LOG_ERR,
"%s: nw_nat64_copy_prefixes_async() num_prefixes(%d) < 0",
if_name,
num_prefixes);
}
if (num_prefixes <= 0) {
CFSetRemoveValue(nat64_prefix_requests, interface);
}
CFRelease(interface);
CFAllocatorDeallocate(NULL, if_name);
});
if (!ok) {
SC_log(LOG_ERR, "%s: nw_nat64_copy_prefixes_async() failed", if_name);
CFSetRemoveValue(nat64_prefix_requests, interface);
CFRelease(interface);
CFAllocatorDeallocate(NULL, if_name);
}
return;
}
static void
_nat64_prefix_request(const void *value, void *context)
{
CFSetRef changes = (CFSetRef)context;
CFStringRef interface = (CFStringRef)value;
if (!CFSetContainsValue(nat64_prefix_requests, interface) ||
((changes != NULL) && CFSetContainsValue(changes, interface))) {
_nat64_prefix_request_start(interface);
}
return;
}
static void
_nat64_prefix_update(const void *value, void *context)
{
#pragma unused(context)
CFStringRef interface = (CFStringRef)value;
if (CFSetContainsValue(nat64_prefix_requests, interface)) {
_nat64_prefix_request_start(interface);
}
return;
}
#pragma mark -
#pragma mark NAT64 prefix functions (for IPMonitor)
__private_extern__
Boolean
is_nat64_prefix_request(CFStringRef change, CFStringRef *interface)
{
CFArrayRef components;
static CFStringRef prefix = NULL;
Boolean yn = FALSE;
static dispatch_once_t once;
dispatch_once(&once, ^{
prefix = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
});
*interface = NULL;
if (!CFStringHasPrefix(change, prefix) ||
!CFStringHasSuffix(change, kSCEntNetNAT64PrefixRequest)) {
return FALSE;
}
components = CFStringCreateArrayBySeparatingStrings(NULL, change, CFSTR("/"));
if (CFArrayGetCount(components) == 5) {
*interface = CFArrayGetValueAtIndex(components, 3);
CFRetain(*interface);
yn = TRUE;
}
CFRelease(components);
return yn;
}
__private_extern__ void
nat64_prefix_request_add_pattern(CFMutableArrayRef patterns)
{
CFStringRef pattern;
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetNAT64PrefixRequest);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
return;
}
__private_extern__
void
nat64_configuration_init(CFBundleRef bundle)
{
#pragma unused(bundle)
nat64_prefix_requests = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
return;
}
__private_extern__
void
nat64_configuration_update(CFSetRef requests, CFSetRef changes)
{
if (changes != NULL) {
CFSetApplyFunction(changes, _nat64_prefix_update, NULL);
}
if (requests != NULL) {
CFSetApplyFunction(requests, _nat64_prefix_request, (void *)changes);
}
return;
}