1/*
2 * Copyright (c) 2011-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * IPConfigurationService.c
26 * - API to communicate with IPConfiguration to instantiate services
27 */
28/*
29 * Modification History
30 *
31 * April 14, 2011 	Dieter Siegmund (dieter@apple.com)
32 * - initial revision
33 */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <sys/types.h>
39#include <string.h>
40#include <CoreFoundation/CFRuntime.h>
41#include <SystemConfiguration/SystemConfiguration.h>
42#include <SystemConfiguration/SCValidation.h>
43#include <pthread.h>
44#include <mach/mach_error.h>
45#include <mach/mach_port.h>
46#include "ipconfig_types.h"
47#include "ipconfig_ext.h"
48#include "symbol_scope.h"
49#include "cfutil.h"
50#include "ipconfig.h"
51#include "IPConfigurationLog.h"
52#include "IPConfigurationServiceInternal.h"
53#include "IPConfigurationService.h"
54
55const CFStringRef
56kIPConfigurationServiceOptionMTU = _kIPConfigurationServiceOptionMTU;
57
58const CFStringRef
59kIPConfigurationServiceOptionPerformNUD = _kIPConfigurationServiceOptionPerformNUD;
60
61const CFStringRef
62kIPConfigurationServiceOptionIPv6Entity = _kIPConfigurationServiceOptionIPv6Entity;
63
64/**
65 ** IPConfigurationService
66 **/
67struct __IPConfigurationService {
68    CFRuntimeBase		cf_base;
69
70    mach_port_t			server;
71    if_name_t			ifname;
72    CFDataRef			serviceID_data;
73    CFStringRef			store_key;
74};
75
76
77/**
78 ** CF object glue code
79 **/
80STATIC CFStringRef	__IPConfigurationServiceCopyDebugDesc(CFTypeRef cf);
81STATIC void		__IPConfigurationServiceDeallocate(CFTypeRef cf);
82
83STATIC CFTypeID __kIPConfigurationServiceTypeID = _kCFRuntimeNotATypeID;
84
85STATIC const CFRuntimeClass __IPConfigurationServiceClass = {
86    0,						/* version */
87    "IPConfigurationService",			/* className */
88    NULL,					/* init */
89    NULL,					/* copy */
90    __IPConfigurationServiceDeallocate,		/* deallocate */
91    NULL,					/* equal */
92    NULL,					/* hash */
93    NULL,					/* copyFormattingDesc */
94    __IPConfigurationServiceCopyDebugDesc	/* copyDebugDesc */
95};
96
97STATIC CFStringRef
98__IPConfigurationServiceCopyDebugDesc(CFTypeRef cf)
99{
100    CFAllocatorRef		allocator = CFGetAllocator(cf);
101    CFMutableStringRef		result;
102    IPConfigurationServiceRef	service = (IPConfigurationServiceRef)cf;
103
104    result = CFStringCreateMutable(allocator, 0);
105    CFStringAppendFormat(result, NULL,
106			 CFSTR("<IPConfigurationService %p [%p]> {"),
107			 cf, allocator);
108    CFStringAppendFormat(result, NULL, CFSTR("ifname = %s, serviceID = %.*s"),
109			 service->ifname,
110			 (int)CFDataGetLength(service->serviceID_data),
111			 CFDataGetBytePtr(service->serviceID_data));
112    CFStringAppend(result, CFSTR("}"));
113    return (result);
114}
115
116STATIC void
117__IPConfigurationServiceDeallocate(CFTypeRef cf)
118{
119    kern_return_t		kret;
120    IPConfigurationServiceRef 	service = (IPConfigurationServiceRef)cf;
121    inline_data_t 		service_id;
122    int				service_id_len;
123    ipconfig_status_t		status;
124
125    service_id_len = (int)CFDataGetLength(service->serviceID_data);
126
127    bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id,
128	  service_id_len);
129    kret = ipconfig_remove_service_on_interface(service->server,
130						service->ifname,
131						service_id, service_id_len,
132						&status);
133    if (kret != KERN_SUCCESS) {
134	IPConfigLogFL(LOG_NOTICE,
135		      "ipconfig_remove_service_on_interface(%s %.*s) "
136		      "failed, %s",
137		      service->ifname, service_id_len, service_id,
138		      mach_error_string(kret));
139    }
140    else if (status != ipconfig_status_success_e) {
141	IPConfigLogFL(LOG_NOTICE,
142		      "ipconfig_remove_service_on_interface(%s %.*s)"
143		      " failed: %s",
144		      service->ifname, service_id_len, service_id,
145		      ipconfig_status_string(status));
146    }
147    mach_port_deallocate(mach_task_self(), service->server);
148    service->server = MACH_PORT_NULL;
149    my_CFRelease(&service->serviceID_data);
150    my_CFRelease(&service->store_key);
151    return;
152}
153
154STATIC void
155__IPConfigurationServiceInitialize(void)
156{
157    /* initialize runtime */
158    __kIPConfigurationServiceTypeID
159	= _CFRuntimeRegisterClass(&__IPConfigurationServiceClass);
160    return;
161}
162
163STATIC void
164__IPConfigurationServiceRegisterClass(void)
165{
166    STATIC pthread_once_t	initialized = PTHREAD_ONCE_INIT;
167
168    pthread_once(&initialized, __IPConfigurationServiceInitialize);
169    return;
170}
171
172STATIC IPConfigurationServiceRef
173__IPConfigurationServiceAllocate(CFAllocatorRef allocator)
174{
175    IPConfigurationServiceRef	service;
176    int				size;
177
178    __IPConfigurationServiceRegisterClass();
179
180    size = sizeof(*service) - sizeof(CFRuntimeBase);
181    service = (IPConfigurationServiceRef)
182	_CFRuntimeCreateInstance(allocator,
183				 __kIPConfigurationServiceTypeID, size, NULL);
184    bzero(((void *)service) + sizeof(CFRuntimeBase), size);
185    return (service);
186}
187
188STATIC CFDictionaryRef
189config_dict_create(pid_t pid, CFDictionaryRef requested_ipv6_config,
190		   CFNumberRef mtu, CFBooleanRef perform_nud)
191{
192    int			count;
193    CFDictionaryRef	config_dict;
194    CFDictionaryRef 	ipv6_dict;
195    const void *	keys[4];
196    CFDictionaryRef	options;
197    const void *	values[4];
198
199    /* monitor pid */
200    count = 0;
201    keys[count] = _kIPConfigurationServiceOptionMonitorPID;
202    values[count] = kCFBooleanTrue;
203    count++;
204
205    /* no publish */
206    keys[count] = _kIPConfigurationServiceOptionNoPublish;
207    values[count] = kCFBooleanTrue;
208    count++;
209
210    /* mtu */
211    if (mtu != NULL) {
212	keys[count] = kIPConfigurationServiceOptionMTU;
213	values[count] = mtu;
214	count++;
215    }
216
217    /* perform NUD */
218    if (perform_nud != NULL) {
219	keys[count] = kIPConfigurationServiceOptionPerformNUD;
220	values[count] = perform_nud;
221	count++;
222    }
223
224    options
225	= CFDictionaryCreate(NULL, keys, values, count,
226			     &kCFTypeDictionaryKeyCallBacks,
227			     &kCFTypeDictionaryValueCallBacks);
228
229    if (requested_ipv6_config != NULL) {
230	ipv6_dict = requested_ipv6_config;
231    }
232    else {
233	/* create the default IPv6 dictionary */
234	ipv6_dict
235	    = CFDictionaryCreate(NULL,
236				 (const void * *)&kSCPropNetIPv6ConfigMethod,
237				 (const void * *)&kSCValNetIPv6ConfigMethodAutomatic,
238				 1,
239				 &kCFTypeDictionaryKeyCallBacks,
240				 &kCFTypeDictionaryValueCallBacks);
241    }
242    keys[0] = kIPConfigurationServiceOptions;
243    values[0] = options;
244    keys[1] = kSCEntNetIPv6;
245    values[1] = ipv6_dict;
246    config_dict
247	= CFDictionaryCreate(NULL, keys, values, 2,
248			     &kCFTypeDictionaryKeyCallBacks,
249			     &kCFTypeDictionaryValueCallBacks);
250    CFRelease(options);
251    if (ipv6_dict != requested_ipv6_config) {
252	CFRelease(ipv6_dict);
253    }
254    return (config_dict);
255}
256
257static Boolean
258ipv6_config_is_valid(CFDictionaryRef config)
259{
260    CFStringRef		config_method;
261    Boolean		is_valid = FALSE;
262
263    config_method = CFDictionaryGetValue(config, kSCPropNetIPv6ConfigMethod);
264    if (isA_CFString(config_method) == NULL) {
265	goto done;
266    }
267    if (CFEqual(config_method, kSCValNetIPv6ConfigMethodManual)) {
268	CFArrayRef	addresses;
269	CFIndex		count;
270	CFArrayRef	prefix_lengths;
271
272	/* must contain Addresses, PrefixLength */
273	addresses = CFDictionaryGetValue(config, kSCPropNetIPv6Addresses);
274	prefix_lengths = CFDictionaryGetValue(config,
275					      kSCPropNetIPv6PrefixLength);
276	if (isA_CFArray(addresses) == NULL
277	    || isA_CFArray(prefix_lengths) == NULL) {
278	    goto done;
279	}
280	count = CFArrayGetCount(addresses);
281	if (count == 0 || count != CFArrayGetCount(prefix_lengths)) {
282	    goto done;
283	}
284    }
285    else if (CFEqual(config_method, kSCValNetIPv6ConfigMethodAutomatic)
286	     || CFEqual(config_method, kSCValNetIPv6ConfigMethodLinkLocal)) {
287	/* no other parameters are required */
288    }
289    else {
290	goto done;
291    }
292    is_valid = TRUE;
293
294 done:
295    return (is_valid);
296}
297
298/**
299 ** IPConfigurationService APIs
300 **/
301
302PRIVATE_EXTERN CFTypeID
303IPConfigurationServiceGetTypeID(void)
304{
305    __IPConfigurationServiceRegisterClass();
306    return (__kIPConfigurationServiceTypeID);
307}
308
309IPConfigurationServiceRef
310IPConfigurationServiceCreate(CFStringRef interface_name,
311			     CFDictionaryRef options)
312{
313    CFDictionaryRef		config_dict;
314    CFDataRef			data = NULL;
315    if_name_t			if_name;
316    kern_return_t		kret;
317    CFNumberRef			mtu = NULL;
318    CFBooleanRef		perform_nud = NULL;
319    CFDictionaryRef		requested_ipv6_config = NULL;
320    mach_port_t			server = MACH_PORT_NULL;
321    IPConfigurationServiceRef	service = NULL;
322    CFStringRef			serviceID;
323    inline_data_t 		service_id;
324    unsigned int 		service_id_len;
325    ipconfig_status_t		status = ipconfig_status_success_e;
326    boolean_t			tried_to_delete = FALSE;
327    void *			xml_data_ptr = NULL;
328    int				xml_data_len = 0;
329
330    if (options != NULL) {
331	mtu = CFDictionaryGetValue(options,
332				   kIPConfigurationServiceOptionMTU);
333	if (mtu != NULL && isA_CFNumber(mtu) == NULL) {
334	    IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
335			  kIPConfigurationServiceOptionMTU);
336	    mtu = NULL;
337	}
338	perform_nud
339	    = CFDictionaryGetValue(options,
340				   kIPConfigurationServiceOptionPerformNUD);
341	if (perform_nud != NULL && isA_CFBoolean(perform_nud) == NULL) {
342	    IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
343			  kIPConfigurationServiceOptionPerformNUD);
344	    perform_nud = NULL;
345	}
346	requested_ipv6_config
347	    = CFDictionaryGetValue(options,
348				   kIPConfigurationServiceOptionIPv6Entity);
349	if (requested_ipv6_config != NULL
350	    && ipv6_config_is_valid(requested_ipv6_config) == FALSE) {
351	    IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option",
352			  kIPConfigurationServiceOptionIPv6Entity);
353	    requested_ipv6_config = NULL;
354	}
355
356    }
357    kret = ipconfig_server_port(&server);
358    if (kret != BOOTSTRAP_SUCCESS) {
359	IPConfigLogFL(LOG_NOTICE,
360		      "ipconfig_server_port, %s",
361		      mach_error_string(kret));
362	return (NULL);
363    }
364    config_dict = config_dict_create(getpid(), requested_ipv6_config,
365				     mtu, perform_nud);
366    data = CFPropertyListCreateData(NULL,
367				    config_dict,
368				    kCFPropertyListBinaryFormat_v1_0,
369				    0,
370				    NULL);
371    CFRelease(config_dict);
372    xml_data_ptr = (void *)CFDataGetBytePtr(data);
373    xml_data_len = (int)CFDataGetLength(data);
374    my_CFStringToCStringAndLength(interface_name, if_name,
375				  sizeof(if_name));
376    while (1) {
377	kret = ipconfig_add_service(server, if_name,
378				    xml_data_ptr, xml_data_len,
379				    service_id, &service_id_len,
380				    &status);
381	if (kret != KERN_SUCCESS) {
382	    IPConfigLogFL(LOG_NOTICE,
383			  "ipconfig_add_service(%s) failed, %s",
384			  if_name, mach_error_string(kret));
385	    goto done;
386	}
387	if (status != ipconfig_status_duplicate_service_e) {
388	    break;
389	}
390	if (tried_to_delete) {
391	    /* already tried once */
392	    break;
393	}
394	tried_to_delete = TRUE;
395	(void)ipconfig_remove_service(server, if_name,
396				      xml_data_ptr, xml_data_len,
397				      &status);
398    }
399    if (status != ipconfig_status_success_e) {
400	IPConfigLogFL(LOG_NOTICE,
401		      "ipconfig_add_service(%s) failed: %s",
402		      if_name, ipconfig_status_string(status));
403	goto done;
404    }
405
406    /* allocate/return an IPConfigurationServiceRef */
407    service = __IPConfigurationServiceAllocate(NULL);
408    if (service == NULL) {
409	goto done;
410    }
411    bcopy(if_name, service->ifname, sizeof(service->ifname));
412    service->serviceID_data = CFDataCreate(NULL, (const UInt8 *)service_id,
413					   service_id_len);
414    service->server = server;
415    serviceID
416	= CFStringCreateWithBytes(NULL,
417				  CFDataGetBytePtr(service->serviceID_data),
418				  CFDataGetLength(service->serviceID_data),
419				  kCFStringEncodingASCII, FALSE);
420    service->store_key = IPConfigurationServiceKey(serviceID);
421    CFRelease(serviceID);
422    server = MACH_PORT_NULL;
423
424 done:
425    if (server != MACH_PORT_NULL) {
426	mach_port_deallocate(mach_task_self(), server);
427    }
428    my_CFRelease(&data);
429    return (service);
430}
431
432/*
433 * Function: IPConfigurationServiceGetNotificationKey
434 *
435 * Purpose:
436 *   Return the SCDynamicStoreKeyRef used to monitor the service using
437 *   SCDynamicStoreSetNotificationKeys().
438 *
439 * Parameters:
440 *   service			: the service to monitor
441 */
442CFStringRef
443IPConfigurationServiceGetNotificationKey(IPConfigurationServiceRef service)
444{
445    return (service->store_key);
446}
447
448CFDictionaryRef
449IPConfigurationServiceCopyInformation(IPConfigurationServiceRef service)
450{
451    CFDictionaryRef		info;
452    SCDynamicStoreRef		store;
453
454    store = SCDynamicStoreCreate(NULL, CFSTR("IPConfigurationService"),
455				 NULL, NULL);
456    if (store == NULL) {
457	return (NULL);
458    }
459    info = SCDynamicStoreCopyValue(store, service->store_key);
460    if (info != NULL
461	&& isA_CFDictionary(info) == NULL) {
462	my_CFRelease(&info);
463    }
464    CFRelease(store);
465    return (info);
466}
467
468void
469IPConfigurationServiceRefreshConfiguration(IPConfigurationServiceRef service)
470{
471    kern_return_t		kret;
472    inline_data_t 		service_id;
473    int				service_id_len;
474    ipconfig_status_t		status;
475
476    service_id_len = (int)CFDataGetLength(service->serviceID_data);
477    bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id,
478	  service_id_len);
479    kret = ipconfig_refresh_service(service->server,
480				    service->ifname,
481				    service_id, service_id_len,
482				    &status);
483    if (kret != KERN_SUCCESS) {
484	IPConfigLogFL(LOG_NOTICE,
485		      "ipconfig_refresh_service(%s %.*s) failed, %s",
486		      service->ifname, service_id_len, service_id,
487		      mach_error_string(kret));
488    }
489    else if (status != ipconfig_status_success_e) {
490	IPConfigLogFL(LOG_NOTICE,
491		      "ipconfig_refresh_service(%s %.*s) failed: %s",
492		      service->ifname, service_id_len, service_id,
493		      ipconfig_status_string(status));
494    }
495    return;
496}
497