2 * Copyright (c) 2013-2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * IPMonitorControlServer.c
26 * - IPC channel to IPMonitor
27 * - used to create interface rank assertions
31 * Modification History
33 * December 16, 2013 Dieter Siegmund (dieter@apple.com)
37 #include <CoreFoundation/CoreFoundation.h>
39 #include <xpc/private.h>
40 #include <sys/queue.h>
41 #include <CoreFoundation/CFRunLoop.h>
42 #include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
43 #include "IPMonitorControlServer.h"
44 #include "symbol_scope.h"
45 #include "IPMonitorControlPrivate.h"
46 #include <SystemConfiguration/SCPrivate.h>
48 #ifdef TEST_IPMONITOR_CONTROL
49 #define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__)
51 #else /* TEST_IPMONITOR_CONTROL */
52 #include "ip_plugin.h"
53 #endif /* TEST_IPMONITOR_CONTROL */
55 STATIC dispatch_queue_t S_IPMonitorControlServerQueue
;
57 typedef struct ControlSession ControlSession
, * ControlSessionRef
;
59 #define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession)
60 #define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession)
61 LIST_HEAD_ControlSession S_ControlSessions
;
63 struct ControlSession
{
64 LIST_ENTRY_ControlSession link
;
65 xpc_connection_t connection
;
66 CFMutableDictionaryRef assertions
; /* ifname<string> = rank<number> */
72 STATIC CFMutableArrayRef S_if_changes
;
73 STATIC CFRange S_if_changes_range
;
76 InterfaceChangedListAddInterface(CFStringRef ifname
)
78 if (S_if_changes
== NULL
) {
79 S_if_changes
= CFArrayCreateMutable(NULL
,
80 0, &kCFTypeArrayCallBacks
);
81 CFArrayAppendValue(S_if_changes
, ifname
);
82 S_if_changes_range
.length
= 1;
84 else if (!CFArrayContainsValue(S_if_changes
, S_if_changes_range
, ifname
)) {
85 CFArrayAppendValue(S_if_changes
, ifname
);
86 S_if_changes_range
.length
++;
91 InterfaceChangedListCopy(void)
93 CFArrayRef current_list
;
95 current_list
= S_if_changes
;
97 return (current_list
);
101 InterfaceRankAssertionAdd(const void * key
, const void * value
, void * context
)
103 CFMutableDictionaryRef
* assertions_p
;
104 CFNumberRef existing_rank
;
105 CFNumberRef rank
= (CFNumberRef
)value
;
107 assertions_p
= (CFMutableDictionaryRef
*)context
;
108 if (*assertions_p
== NULL
) {
110 = CFDictionaryCreateMutable(NULL
, 0,
111 &kCFTypeDictionaryKeyCallBacks
,
112 &kCFTypeDictionaryValueCallBacks
);
113 CFDictionarySetValue(*assertions_p
, key
, rank
);
116 existing_rank
= CFDictionaryGetValue(*assertions_p
, key
);
117 if (existing_rank
== NULL
118 || (CFNumberCompare(rank
, existing_rank
, NULL
)
119 == kCFCompareGreaterThan
)) {
120 CFDictionarySetValue(*assertions_p
, key
, rank
);
125 STATIC CFDictionaryRef
126 InterfaceRankAssertionsCopy(void)
128 CFMutableDictionaryRef assertions
= NULL
;
129 ControlSessionRef session
;
131 LIST_FOREACH(session
, &S_ControlSessions
, link
) {
132 if (session
->assertions
== NULL
) {
135 CFDictionaryApplyFunction(session
->assertions
,
136 InterfaceRankAssertionAdd
,
142 STATIC CFRunLoopRef S_runloop
;
143 STATIC CFRunLoopSourceRef S_signal_source
;
146 SetNotificationInfo(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
)
149 S_signal_source
= rls
;
154 GenerateNotification(void)
156 if (S_signal_source
!= NULL
) {
157 CFRunLoopSourceSignal(S_signal_source
);
158 if (S_runloop
!= NULL
) {
159 CFRunLoopWakeUp(S_runloop
);
169 AddChangedInterface(const void * key
, const void * value
, void * context
)
171 #pragma unused(value)
172 #pragma unused(context)
173 InterfaceChangedListAddInterface((CFStringRef
)key
);
178 ControlSessionInvalidate(ControlSessionRef session
)
180 my_log(LOG_DEBUG
, "Invalidating %p", session
);
181 LIST_REMOVE(session
, link
);
182 if (session
->assertions
!= NULL
) {
184 "IPMonitorControlServer: %p pid %d removing assertions %@",
186 xpc_connection_get_pid(session
->connection
),
187 session
->assertions
);
188 CFDictionaryApplyFunction(session
->assertions
, AddChangedInterface
,
190 CFRelease(session
->assertions
);
191 session
->assertions
= NULL
;
192 GenerateNotification();
198 ControlSessionRelease(void * p
)
200 my_log(LOG_DEBUG
, "Releasing %p", p
);
205 STATIC ControlSessionRef
206 ControlSessionLookup(xpc_connection_t connection
)
208 return ((ControlSessionRef
)xpc_connection_get_context(connection
));
211 STATIC ControlSessionRef
212 ControlSessionCreate(xpc_connection_t connection
)
214 ControlSessionRef session
;
216 session
= (ControlSessionRef
)malloc(sizeof(*session
));
217 bzero(session
, sizeof(*session
));
218 session
->connection
= connection
;
219 xpc_connection_set_finalizer_f(connection
, ControlSessionRelease
);
220 xpc_connection_set_context(connection
, session
);
221 LIST_INSERT_HEAD(&S_ControlSessions
, session
, link
);
222 my_log(LOG_DEBUG
, "Created %p (connection %p)", session
, connection
);
226 STATIC ControlSessionRef
227 ControlSessionGet(xpc_connection_t connection
)
229 ControlSessionRef session
;
231 session
= ControlSessionLookup(connection
);
232 if (session
!= NULL
) {
235 return (ControlSessionCreate(connection
));
239 ControlSessionSetInterfaceRank(ControlSessionRef session
,
241 SCNetworkServicePrimaryRank rank
)
243 CFStringRef ifname_cf
;
245 if (session
->assertions
== NULL
) {
246 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
247 /* no assertions, no need to store rank */
251 = CFDictionaryCreateMutable(NULL
, 0,
252 &kCFTypeDictionaryKeyCallBacks
,
253 &kCFTypeDictionaryValueCallBacks
);
255 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
256 kCFStringEncodingUTF8
);
258 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
259 CFDictionaryRemoveValue(session
->assertions
, ifname_cf
);
260 if (CFDictionaryGetCount(session
->assertions
) == 0) {
261 CFRelease(session
->assertions
);
262 session
->assertions
= NULL
;
268 rank_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank
);
269 CFDictionarySetValue(session
->assertions
, ifname_cf
, rank_cf
);
272 InterfaceChangedListAddInterface(ifname_cf
);
273 GenerateNotification();
274 CFRelease(ifname_cf
);
278 STATIC SCNetworkServicePrimaryRank
279 ControlSessionGetInterfaceRank(ControlSessionRef session
,
282 SCNetworkServicePrimaryRank rank
= kSCNetworkServicePrimaryRankDefault
;
284 if (session
->assertions
!= NULL
) {
285 CFStringRef ifname_cf
;
288 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
289 kCFStringEncodingUTF8
);
290 rank_cf
= CFDictionaryGetValue(session
->assertions
, ifname_cf
);
291 CFRelease(ifname_cf
);
292 if (rank_cf
!= NULL
) {
293 (void)CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &rank
);
300 ** IPMonitorControlServer
303 IPMonitorControlServerValidateConnection(xpc_connection_t connection
)
307 uid
= xpc_connection_get_euid(connection
);
312 IPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection
,
313 xpc_object_t request
,
316 #pragma unused(reply)
318 SCNetworkServicePrimaryRank rank
;
319 ControlSessionRef session
;
321 if (!IPMonitorControlServerValidateConnection(connection
)) {
322 my_log(LOG_INFO
, "connection %p pid %d permission denied",
323 connection
, xpc_connection_get_pid(connection
));
327 = xpc_dictionary_get_string(request
,
328 kIPMonitorControlRequestKeyInterfaceName
);
329 if (ifname
== NULL
) {
332 rank
= (SCNetworkServicePrimaryRank
)
333 xpc_dictionary_get_uint64(request
,
334 kIPMonitorControlRequestKeyPrimaryRank
);
336 case kSCNetworkServicePrimaryRankDefault
:
337 case kSCNetworkServicePrimaryRankFirst
:
338 case kSCNetworkServicePrimaryRankLast
:
339 case kSCNetworkServicePrimaryRankNever
:
340 case kSCNetworkServicePrimaryRankScoped
:
345 session
= ControlSessionGet(connection
);
346 ControlSessionSetInterfaceRank(session
, ifname
, rank
);
347 my_log(LOG_INFO
, "connection %p pid %d set %s %u",
348 connection
, xpc_connection_get_pid(connection
), ifname
, rank
);
353 IPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection
,
354 xpc_object_t request
,
358 SCNetworkServicePrimaryRank rank
;
359 ControlSessionRef session
;
362 /* no point in processing the request if we can't provide an answer */
365 session
= ControlSessionLookup(connection
);
366 if (session
== NULL
) {
367 /* no session, no rank assertion */
371 = xpc_dictionary_get_string(request
,
372 kIPMonitorControlRequestKeyInterfaceName
);
373 if (ifname
== NULL
) {
376 rank
= ControlSessionGetInterfaceRank(session
, ifname
);
377 xpc_dictionary_set_uint64(reply
, kIPMonitorControlResponseKeyPrimaryRank
,
383 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection
)
385 ControlSessionRef session
;
387 my_log(LOG_DEBUG
, "IPMonitorControlServer: client %p went away", connection
);
388 session
= ControlSessionLookup(connection
);
389 if (session
== NULL
) {
390 /* never asserted anything */
393 ControlSessionInvalidate(session
);
398 IPMonitorControlServerHandleRequest(xpc_connection_t connection
,
399 xpc_object_t request
)
403 type
= xpc_get_type(request
);
404 if (type
== XPC_TYPE_DICTIONARY
) {
406 uint64_t request_type
;
407 xpc_connection_t remote
;
411 = xpc_dictionary_get_uint64(request
,
412 kIPMonitorControlRequestKeyType
);
413 reply
= xpc_dictionary_create_reply(request
);
414 switch (request_type
) {
415 case kIPMonitorControlRequestTypeSetInterfaceRank
:
416 error
= IPMonitorControlServerHandleSetInterfaceRank(connection
,
420 case kIPMonitorControlRequestTypeGetInterfaceRank
:
421 error
= IPMonitorControlServerHandleGetInterfaceRank(connection
,
430 /* client didn't want a reply */
433 xpc_dictionary_set_int64(reply
, kIPMonitorControlResponseKeyError
,
435 remote
= xpc_dictionary_get_remote_connection(request
);
436 xpc_connection_send_message(remote
, reply
);
439 else if (type
== XPC_TYPE_ERROR
) {
440 if (request
== XPC_ERROR_CONNECTION_INVALID
) {
441 IPMonitorControlServerHandleDisconnect(connection
);
443 else if (request
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
444 my_log(LOG_INFO
, "connection interrupted");
448 my_log(LOG_NOTICE
, "unexpected event");
454 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection
)
456 xpc_handler_t handler
;
458 handler
= ^(xpc_object_t event
) {
459 IPMonitorControlServerHandleRequest(connection
, event
);
461 xpc_connection_set_event_handler(connection
, handler
);
462 xpc_connection_set_target_queue(connection
, S_IPMonitorControlServerQueue
);
463 xpc_connection_resume(connection
);
467 STATIC xpc_connection_t
468 IPMonitorControlServerCreate(dispatch_queue_t queue
, const char * name
)
470 uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_LISTENER
;
471 xpc_connection_t connection
;
472 xpc_handler_t handler
;
474 connection
= xpc_connection_create_mach_service(name
, queue
, flags
);
475 if (connection
== NULL
) {
478 handler
= ^(xpc_object_t event
) {
481 type
= xpc_get_type(event
);
482 if (type
== XPC_TYPE_CONNECTION
) {
483 IPMonitorControlServerHandleNewConnection(event
);
485 else if (type
== XPC_TYPE_ERROR
) {
488 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
489 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
490 my_log(LOG_NOTICE
, "%s", desc
);
491 xpc_release(connection
);
494 my_log(LOG_NOTICE
, "%s", desc
);
498 my_log(LOG_NOTICE
, "unknown event %p", type
);
501 S_IPMonitorControlServerQueue
= queue
;
502 xpc_connection_set_event_handler(connection
, handler
);
503 xpc_connection_resume(connection
);
507 PRIVATE_EXTERN Boolean
508 IPMonitorControlServerStart(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
,
511 #pragma unused(verbose)
513 xpc_connection_t connection
;
515 SetNotificationInfo(runloop
, rls
);
516 q
= dispatch_queue_create("IPMonitorControlServer", NULL
);
517 connection
= IPMonitorControlServerCreate(q
, kIPMonitorControlServerName
);
518 if (connection
== NULL
) {
520 "IPMonitorControlServer: failed to create server");
527 PRIVATE_EXTERN CFArrayRef
528 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef
* info
)
530 __block CFArrayRef changed
;
531 __block CFDictionaryRef dict
;
533 dispatch_sync(S_IPMonitorControlServerQueue
,
535 dict
= InterfaceRankAssertionsCopy();
536 changed
= InterfaceChangedListCopy();