1/*
2 * Copyright (c) 2013-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 * IPMonitorControl.c
26 * - IPC channel to IPMonitor
27 * - used to create interface rank assertions
28 */
29
30/*
31 * Modification History
32 *
33 * December 16, 2013	Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 */
36
37#include "IPMonitorControl.h"
38#include "IPMonitorControlPrivate.h"
39#include "symbol_scope.h"
40#include <CoreFoundation/CFRuntime.h>
41#include <net/if.h>
42#include <xpc/xpc.h>
43#include <xpc/private.h>
44#include <SystemConfiguration/SCPrivate.h>
45
46#ifdef TEST_IPMONITOR_CONTROL
47#define my_log(__level, fmt, ...)	SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
48
49#else /* TEST_IPMONITOR_CONTROL */
50
51#define my_log(__level, fmt, ...)	SCLog(TRUE, __level, CFSTR(fmt), ## __VA_ARGS__)
52#endif /* TEST_IPMONITOR_CONTROL */
53
54/**
55 ** IPMonitorControl CF object glue
56 **/
57
58struct IPMonitorControl {
59    CFRuntimeBase		cf_base;
60
61    dispatch_queue_t		queue;
62    xpc_connection_t		connection;
63    CFMutableDictionaryRef	assertions; /* ifname<string> = rank<number> */
64};
65
66STATIC CFStringRef	__IPMonitorControlCopyDebugDesc(CFTypeRef cf);
67STATIC void		__IPMonitorControlDeallocate(CFTypeRef cf);
68
69STATIC CFTypeID __kIPMonitorControlTypeID = _kCFRuntimeNotATypeID;
70
71STATIC const CFRuntimeClass __IPMonitorControlClass = {
72    0,					/* version */
73    "IPMonitorControl",			/* className */
74    NULL,				/* init */
75    NULL,				/* copy */
76    __IPMonitorControlDeallocate,	/* deallocate */
77    NULL,				/* equal */
78    NULL,				/* hash */
79    NULL,				/* copyFormattingDesc */
80    __IPMonitorControlCopyDebugDesc	/* copyDebugDesc */
81};
82
83STATIC CFStringRef
84__IPMonitorControlCopyDebugDesc(CFTypeRef cf)
85{
86    CFAllocatorRef		allocator = CFGetAllocator(cf);
87    IPMonitorControlRef		control = (IPMonitorControlRef)cf;
88
89    return (CFStringCreateWithFormat(allocator, NULL,
90				     CFSTR("<IPMonitorControl %p>"),
91				     control));
92}
93
94STATIC void
95__IPMonitorControlDeallocate(CFTypeRef cf)
96{
97    IPMonitorControlRef control = (IPMonitorControlRef)cf;
98
99    if (control->connection != NULL) {
100	xpc_release(control->connection);
101    }
102    if (control->queue != NULL) {
103	xpc_release(control->queue);
104    }
105    return;
106}
107
108/**
109 ** IPMonitorControl support functions
110 **/
111STATIC void
112__IPMonitorControlRegisterClass(void)
113{
114    STATIC dispatch_once_t	once;
115
116    dispatch_once(&once, ^{
117	    __kIPMonitorControlTypeID
118		= _CFRuntimeRegisterClass(&__IPMonitorControlClass);
119	});
120    return;
121}
122
123STATIC IPMonitorControlRef
124__IPMonitorControlAllocate(CFAllocatorRef allocator)
125{
126    IPMonitorControlRef		control;
127    int				size;
128
129    __IPMonitorControlRegisterClass();
130    size = sizeof(*control) - sizeof(CFRuntimeBase);
131    control = (IPMonitorControlRef)
132	_CFRuntimeCreateInstance(allocator,
133				 __kIPMonitorControlTypeID, size, NULL);
134    bzero(((void *)control) + sizeof(CFRuntimeBase), size);
135    return (control);
136}
137
138STATIC Boolean
139IPMonitorControlHandleResponse(xpc_object_t event, Boolean async,
140			       Boolean * retry_p)
141{
142    Boolean	retry = FALSE;
143    Boolean	success = FALSE;
144    xpc_type_t	type;
145
146    type = xpc_get_type(event);
147    if (type == XPC_TYPE_DICTIONARY) {
148	if (async) {
149	    /* we don't expect async responses messages */
150	    my_log(LOG_NOTICE, "IPMonitorControl: unexpected message");
151	}
152	else {
153	    int64_t	error;
154
155	    error = xpc_dictionary_get_int64(event,
156					     kIPMonitorControlResponseKeyError);
157	    if (error != 0) {
158		success = FALSE;
159#ifdef TEST_IPMONITOR_CONTROL
160		my_log(LOG_NOTICE,
161		       "IPMonitorControl: failure code %lld", error);
162#endif /* TEST_IPMONITOR_CONTROL */
163	    }
164	    else {
165		success = TRUE;
166	    }
167	}
168    }
169    else if (type == XPC_TYPE_ERROR) {
170	if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
171#ifdef TEST_IPMONITOR_CONTROL
172	    my_log(LOG_NOTICE, "IPMonitorControl: can retry");
173#endif /* TEST_IPMONITOR_CONTROL */
174	    retry = TRUE;
175	}
176	else {
177	    const char *	desc;
178
179	    desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
180	    my_log(LOG_NOTICE, "IPMonitorControl: %s", desc);
181	}
182    }
183    else {
184	my_log(LOG_NOTICE, "IPMonitorControl: unknown event type : %p", type);
185    }
186    if (retry_p != NULL) {
187	*retry_p = retry;
188    }
189    return (success);
190}
191
192
193STATIC void
194IPMonitorControlSetInterfaceRank(IPMonitorControlRef control,
195				 CFStringRef ifname_cf,
196				 SCNetworkServicePrimaryRank rank)
197{
198    if (control->assertions == NULL) {
199	if (rank == kSCNetworkServicePrimaryRankDefault) {
200	    /* no assertions, no need to store rank */
201	    return;
202	}
203	control->assertions
204	    = CFDictionaryCreateMutable(NULL, 0,
205					&kCFTypeDictionaryKeyCallBacks,
206					&kCFTypeDictionaryValueCallBacks);
207    }
208    if (rank == kSCNetworkServicePrimaryRankDefault) {
209	CFDictionaryRemoveValue(control->assertions, ifname_cf);
210	if (CFDictionaryGetCount(control->assertions) == 0) {
211	    CFRelease(control->assertions);
212	    control->assertions = NULL;
213	}
214    }
215    else {
216	CFNumberRef	rank_cf;
217
218	rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank);
219	CFDictionarySetValue(control->assertions, ifname_cf, rank_cf);
220	CFRelease(rank_cf);
221    }
222    return;
223}
224
225STATIC void
226ApplyInterfaceRank(const void * key, const void * value, void * context)
227{
228    xpc_connection_t		connection = (xpc_connection_t)context;
229    char			ifname[IF_NAMESIZE];
230    SCNetworkServicePrimaryRank	rank;
231    xpc_object_t		request;
232
233    if (CFStringGetCString(key, ifname, sizeof(ifname),
234			   kCFStringEncodingUTF8) == FALSE) {
235	return;
236    }
237    if (CFNumberGetValue(value, kCFNumberSInt32Type, &rank) == FALSE) {
238	return;
239    }
240    request = xpc_dictionary_create(NULL, NULL, 0);
241    xpc_dictionary_set_uint64(request,
242			      kIPMonitorControlRequestKeyType,
243			      kIPMonitorControlRequestTypeSetInterfaceRank);
244    xpc_dictionary_set_string(request,
245			      kIPMonitorControlRequestKeyInterfaceName,
246			      ifname);
247    xpc_dictionary_set_uint64(request,
248			      kIPMonitorControlRequestKeyPrimaryRank,
249			      rank);
250    xpc_connection_send_message(connection, request);
251    xpc_release(request);
252    return;
253}
254
255
256/**
257 ** IPMonitorControl SPI
258 **/
259PRIVATE_EXTERN IPMonitorControlRef
260IPMonitorControlCreate(void)
261{
262    xpc_connection_t	connection;
263    IPMonitorControlRef	control;
264    uint64_t		flags = XPC_CONNECTION_MACH_SERVICE_PRIVILEGED;
265    xpc_handler_t	handler;
266    dispatch_queue_t	queue;
267
268    control = __IPMonitorControlAllocate(NULL);
269    queue = dispatch_queue_create("IPMonitorControl", NULL);
270    connection
271	= xpc_connection_create_mach_service(kIPMonitorControlServerName,
272					     queue, flags);
273    handler = ^(xpc_object_t event) {
274	Boolean			retry;
275
276	(void)IPMonitorControlHandleResponse(event, TRUE, &retry);
277	if (retry && control->assertions != NULL) {
278	    CFDictionaryApplyFunction(control->assertions,
279				      ApplyInterfaceRank,
280				      control->connection);
281	}
282    };
283    xpc_connection_set_event_handler(connection, handler);
284    control->connection = connection;
285    control->queue = queue;
286    xpc_connection_resume(connection);
287    return (control);
288}
289
290PRIVATE_EXTERN Boolean
291IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control,
292					CFStringRef ifname_cf,
293					SCNetworkServicePrimaryRank rank)
294{
295    char		ifname[IF_NAMESIZE];
296    xpc_object_t	request;
297    Boolean		success = FALSE;
298
299    if (CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
300			   kCFStringEncodingUTF8) == FALSE) {
301	return (FALSE);
302    }
303    request = xpc_dictionary_create(NULL, NULL, 0);
304    xpc_dictionary_set_uint64(request,
305			      kIPMonitorControlRequestKeyType,
306			      kIPMonitorControlRequestTypeSetInterfaceRank);
307    xpc_dictionary_set_string(request,
308			      kIPMonitorControlRequestKeyInterfaceName,
309			      ifname);
310    xpc_dictionary_set_uint64(request,
311			      kIPMonitorControlRequestKeyPrimaryRank,
312			      rank);
313    while (TRUE) {
314	xpc_object_t	reply;
315	Boolean		retry_on_error = FALSE;
316
317	reply = xpc_connection_send_message_with_reply_sync(control->connection,
318							    request);
319	if (reply == NULL) {
320	    my_log(LOG_NOTICE, "IPMonitorControl: failed to send message");
321	    break;
322	}
323	success = IPMonitorControlHandleResponse(reply, FALSE,
324						 &retry_on_error);
325	xpc_release(reply);
326	if (success) {
327	    break;
328	}
329	if (retry_on_error) {
330	    continue;
331	}
332	my_log(LOG_NOTICE, "IPMonitorControl: fatal error");
333	break;
334    }
335    xpc_release(request);
336    if (success) {
337	/* sync our state */
338	CFRetain(ifname_cf);
339	CFRetain(control);
340	dispatch_async(control->queue,
341		       ^{
342			   IPMonitorControlSetInterfaceRank(control,
343							    ifname_cf,
344							    rank);
345			   CFRelease(ifname_cf);
346			   CFRelease(control);
347		       });
348    }
349    return (success);
350}
351
352SCNetworkServicePrimaryRank
353IPMonitorControlGetInterfacePrimaryRank(IPMonitorControlRef control,
354					CFStringRef ifname_cf)
355{
356    char			ifname[IF_NAMESIZE];
357    SCNetworkServicePrimaryRank rank;
358    xpc_object_t		request;
359
360    rank = kSCNetworkServicePrimaryRankDefault;
361    if (CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
362			   kCFStringEncodingUTF8) == FALSE) {
363	goto done;
364    }
365    request = xpc_dictionary_create(NULL, NULL, 0);
366    xpc_dictionary_set_uint64(request,
367			      kIPMonitorControlRequestKeyType,
368			      kIPMonitorControlRequestTypeGetInterfaceRank);
369    xpc_dictionary_set_string(request,
370			      kIPMonitorControlRequestKeyInterfaceName,
371			      ifname);
372    while (TRUE) {
373	xpc_object_t	reply;
374	Boolean		retry_on_error = FALSE;
375	Boolean		success;
376
377	reply = xpc_connection_send_message_with_reply_sync(control->connection,
378							    request);
379	if (reply == NULL) {
380	    my_log(LOG_NOTICE, "IPMonitorControl: failed to send message");
381	    break;
382	}
383	success = IPMonitorControlHandleResponse(reply, FALSE, &retry_on_error);
384	if (success) {
385	    rank = (SCNetworkServicePrimaryRank)
386		xpc_dictionary_get_uint64(reply,
387					  kIPMonitorControlResponseKeyPrimaryRank);
388	}
389	xpc_release(reply);
390	if (success) {
391	    break;
392	}
393	if (retry_on_error) {
394	    continue;
395	}
396	break;
397    }
398    xpc_release(request);
399
400 done:
401    return (rank);
402}
403
404