IPConfigurationService.c [plain text]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <CoreFoundation/CFRuntime.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <pthread.h>
#include <mach/mach_error.h>
#include <mach/mach_port.h>
#include "ipconfig_types.h"
#include "ipconfig_ext.h"
#include "symbol_scope.h"
#include "cfutil.h"
#include "ipconfig.h"
#include "IPConfigurationLog.h"
#include "IPConfigurationServiceInternal.h"
#include "IPConfigurationService.h"
const CFStringRef
kIPConfigurationServiceOptionMTU = _kIPConfigurationServiceOptionMTU;
const CFStringRef
kIPConfigurationServiceOptionPerformNUD = _kIPConfigurationServiceOptionPerformNUD;
const CFStringRef
kIPConfigurationServiceOptionIPv6Entity = _kIPConfigurationServiceOptionIPv6Entity;
struct __IPConfigurationService {
CFRuntimeBase cf_base;
mach_port_t server;
if_name_t ifname;
CFDataRef serviceID_data;
CFStringRef store_key;
};
STATIC CFStringRef __IPConfigurationServiceCopyDebugDesc(CFTypeRef cf);
STATIC void __IPConfigurationServiceDeallocate(CFTypeRef cf);
STATIC CFTypeID __kIPConfigurationServiceTypeID = _kCFRuntimeNotATypeID;
STATIC const CFRuntimeClass __IPConfigurationServiceClass = {
0,
"IPConfigurationService",
NULL,
NULL,
__IPConfigurationServiceDeallocate,
NULL,
NULL,
NULL,
__IPConfigurationServiceCopyDebugDesc
};
STATIC CFStringRef
__IPConfigurationServiceCopyDebugDesc(CFTypeRef cf)
{
CFAllocatorRef allocator = CFGetAllocator(cf);
CFMutableStringRef result;
IPConfigurationServiceRef service = (IPConfigurationServiceRef)cf;
result = CFStringCreateMutable(allocator, 0);
CFStringAppendFormat(result, NULL,
CFSTR("<IPConfigurationService %p [%p]> {"),
cf, allocator);
CFStringAppendFormat(result, NULL, CFSTR("ifname = %s, serviceID = %.*s"),
service->ifname,
(int)CFDataGetLength(service->serviceID_data),
CFDataGetBytePtr(service->serviceID_data));
CFStringAppend(result, CFSTR("}"));
return (result);
}
STATIC void
__IPConfigurationServiceDeallocate(CFTypeRef cf)
{
kern_return_t kret;
IPConfigurationServiceRef service = (IPConfigurationServiceRef)cf;
inline_data_t service_id;
int service_id_len;
ipconfig_status_t status;
service_id_len = (int)CFDataGetLength(service->serviceID_data);
bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id,
service_id_len);
kret = ipconfig_remove_service_on_interface(service->server,
service->ifname,
service_id, service_id_len,
&status);
if (kret != KERN_SUCCESS) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_remove_service_on_interface(%s %.*s) "
"failed, %s",
service->ifname, service_id_len, service_id,
mach_error_string(kret));
}
else if (status != ipconfig_status_success_e) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_remove_service_on_interface(%s %.*s)"
" failed: %s",
service->ifname, service_id_len, service_id,
ipconfig_status_string(status));
}
mach_port_deallocate(mach_task_self(), service->server);
service->server = MACH_PORT_NULL;
my_CFRelease(&service->serviceID_data);
my_CFRelease(&service->store_key);
return;
}
STATIC void
__IPConfigurationServiceInitialize(void)
{
__kIPConfigurationServiceTypeID
= _CFRuntimeRegisterClass(&__IPConfigurationServiceClass);
return;
}
STATIC void
__IPConfigurationServiceRegisterClass(void)
{
STATIC pthread_once_t initialized = PTHREAD_ONCE_INIT;
pthread_once(&initialized, __IPConfigurationServiceInitialize);
return;
}
STATIC IPConfigurationServiceRef
__IPConfigurationServiceAllocate(CFAllocatorRef allocator)
{
IPConfigurationServiceRef service;
int size;
__IPConfigurationServiceRegisterClass();
size = sizeof(*service) - sizeof(CFRuntimeBase);
service = (IPConfigurationServiceRef)
_CFRuntimeCreateInstance(allocator,
__kIPConfigurationServiceTypeID, size, NULL);
bzero(((void *)service) + sizeof(CFRuntimeBase), size);
return (service);
}
STATIC CFDictionaryRef
config_dict_create(pid_t pid, CFDictionaryRef requested_ipv6_config,
CFNumberRef mtu, CFBooleanRef perform_nud)
{
int count;
CFDictionaryRef config_dict;
CFDictionaryRef ipv6_dict;
const void * keys[4];
CFDictionaryRef options;
const void * values[4];
count = 0;
keys[count] = _kIPConfigurationServiceOptionMonitorPID;
values[count] = kCFBooleanTrue;
count++;
keys[count] = _kIPConfigurationServiceOptionNoPublish;
values[count] = kCFBooleanTrue;
count++;
if (mtu != NULL) {
keys[count] = kIPConfigurationServiceOptionMTU;
values[count] = mtu;
count++;
}
if (perform_nud != NULL) {
keys[count] = kIPConfigurationServiceOptionPerformNUD;
values[count] = perform_nud;
count++;
}
options
= CFDictionaryCreate(NULL, keys, values, count,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (requested_ipv6_config != NULL) {
ipv6_dict = requested_ipv6_config;
}
else {
ipv6_dict
= CFDictionaryCreate(NULL,
(const void * *)&kSCPropNetIPv6ConfigMethod,
(const void * *)&kSCValNetIPv6ConfigMethodAutomatic,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
keys[0] = kIPConfigurationServiceOptions;
values[0] = options;
keys[1] = kSCEntNetIPv6;
values[1] = ipv6_dict;
config_dict
= CFDictionaryCreate(NULL, keys, values, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(options);
if (ipv6_dict != requested_ipv6_config) {
CFRelease(ipv6_dict);
}
return (config_dict);
}
static Boolean
ipv6_config_is_valid(CFDictionaryRef config)
{
CFStringRef config_method;
Boolean is_valid = FALSE;
config_method = CFDictionaryGetValue(config, kSCPropNetIPv6ConfigMethod);
if (isA_CFString(config_method) == NULL) {
goto done;
}
if (CFEqual(config_method, kSCValNetIPv6ConfigMethodManual)) {
CFArrayRef addresses;
CFIndex count;
CFArrayRef prefix_lengths;
addresses = CFDictionaryGetValue(config, kSCPropNetIPv6Addresses);
prefix_lengths = CFDictionaryGetValue(config,
kSCPropNetIPv6PrefixLength);
if (isA_CFArray(addresses) == NULL
|| isA_CFArray(prefix_lengths) == NULL) {
goto done;
}
count = CFArrayGetCount(addresses);
if (count == 0 || count != CFArrayGetCount(prefix_lengths)) {
goto done;
}
}
else if (CFEqual(config_method, kSCValNetIPv6ConfigMethodAutomatic)
|| CFEqual(config_method, kSCValNetIPv6ConfigMethodLinkLocal)) {
}
else {
goto done;
}
is_valid = TRUE;
done:
return (is_valid);
}
PRIVATE_EXTERN CFTypeID
IPConfigurationServiceGetTypeID(void)
{
__IPConfigurationServiceRegisterClass();
return (__kIPConfigurationServiceTypeID);
}
IPConfigurationServiceRef
IPConfigurationServiceCreate(CFStringRef interface_name,
CFDictionaryRef options)
{
CFDictionaryRef config_dict;
CFDataRef data = NULL;
if_name_t if_name;
kern_return_t kret;
CFNumberRef mtu = NULL;
CFBooleanRef perform_nud = NULL;
CFDictionaryRef requested_ipv6_config = NULL;
mach_port_t server = MACH_PORT_NULL;
IPConfigurationServiceRef service = NULL;
CFStringRef serviceID;
inline_data_t service_id;
unsigned int service_id_len;
ipconfig_status_t status = ipconfig_status_success_e;
boolean_t tried_to_delete = FALSE;
void * xml_data_ptr = NULL;
int xml_data_len = 0;
if (options != NULL) {
mtu = CFDictionaryGetValue(options,
kIPConfigurationServiceOptionMTU);
if (mtu != NULL && isA_CFNumber(mtu) == NULL) {
IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
kIPConfigurationServiceOptionMTU);
mtu = NULL;
}
perform_nud
= CFDictionaryGetValue(options,
kIPConfigurationServiceOptionPerformNUD);
if (perform_nud != NULL && isA_CFBoolean(perform_nud) == NULL) {
IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
kIPConfigurationServiceOptionPerformNUD);
perform_nud = NULL;
}
requested_ipv6_config
= CFDictionaryGetValue(options,
kIPConfigurationServiceOptionIPv6Entity);
if (requested_ipv6_config != NULL
&& ipv6_config_is_valid(requested_ipv6_config) == FALSE) {
IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
kIPConfigurationServiceOptionIPv6Entity);
requested_ipv6_config = NULL;
}
}
kret = ipconfig_server_port(&server);
if (kret != BOOTSTRAP_SUCCESS) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_server_port, %s",
mach_error_string(kret));
return (NULL);
}
config_dict = config_dict_create(getpid(), requested_ipv6_config,
mtu, perform_nud);
data = CFPropertyListCreateData(NULL,
config_dict,
kCFPropertyListBinaryFormat_v1_0,
0,
NULL);
CFRelease(config_dict);
xml_data_ptr = (void *)CFDataGetBytePtr(data);
xml_data_len = (int)CFDataGetLength(data);
my_CFStringToCStringAndLength(interface_name, if_name,
sizeof(if_name));
while (1) {
kret = ipconfig_add_service(server, if_name,
xml_data_ptr, xml_data_len,
service_id, &service_id_len,
&status);
if (kret != KERN_SUCCESS) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_add_service(%s) failed, %s",
if_name, mach_error_string(kret));
goto done;
}
if (status != ipconfig_status_duplicate_service_e) {
break;
}
if (tried_to_delete) {
break;
}
tried_to_delete = TRUE;
(void)ipconfig_remove_service(server, if_name,
xml_data_ptr, xml_data_len,
&status);
}
if (status != ipconfig_status_success_e) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_add_service(%s) failed: %s",
if_name, ipconfig_status_string(status));
goto done;
}
service = __IPConfigurationServiceAllocate(NULL);
if (service == NULL) {
goto done;
}
bcopy(if_name, service->ifname, sizeof(service->ifname));
service->serviceID_data = CFDataCreate(NULL, (const UInt8 *)service_id,
service_id_len);
service->server = server;
serviceID
= CFStringCreateWithBytes(NULL,
CFDataGetBytePtr(service->serviceID_data),
CFDataGetLength(service->serviceID_data),
kCFStringEncodingASCII, FALSE);
service->store_key = IPConfigurationServiceKey(serviceID);
CFRelease(serviceID);
server = MACH_PORT_NULL;
done:
if (server != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), server);
}
my_CFRelease(&data);
return (service);
}
CFStringRef
IPConfigurationServiceGetNotificationKey(IPConfigurationServiceRef service)
{
return (service->store_key);
}
CFDictionaryRef
IPConfigurationServiceCopyInformation(IPConfigurationServiceRef service)
{
CFDictionaryRef info;
SCDynamicStoreRef store;
store = SCDynamicStoreCreate(NULL, CFSTR("IPConfigurationService"),
NULL, NULL);
if (store == NULL) {
return (NULL);
}
info = SCDynamicStoreCopyValue(store, service->store_key);
if (info != NULL
&& isA_CFDictionary(info) == NULL) {
my_CFRelease(&info);
}
CFRelease(store);
return (info);
}
void
IPConfigurationServiceRefreshConfiguration(IPConfigurationServiceRef service)
{
kern_return_t kret;
inline_data_t service_id;
int service_id_len;
ipconfig_status_t status;
service_id_len = (int)CFDataGetLength(service->serviceID_data);
bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id,
service_id_len);
kret = ipconfig_refresh_service(service->server,
service->ifname,
service_id, service_id_len,
&status);
if (kret != KERN_SUCCESS) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_refresh_service(%s %.*s) failed, %s",
service->ifname, service_id_len, service_id,
mach_error_string(kret));
}
else if (status != ipconfig_status_success_e) {
IPConfigLogFL(LOG_NOTICE,
"ipconfig_refresh_service(%s %.*s) failed: %s",
service->ifname, service_id_len, service_id,
ipconfig_status_string(status));
}
return;
}