2 * Copyright (c) 2013-2014 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 STATIC Boolean
* S_verbose
;
50 #ifdef TEST_IPMONITOR_CONTROL
51 #define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
53 #else /* TEST_IPMONITOR_CONTROL */
54 #include "ip_plugin.h"
55 #endif /* TEST_IPMONITOR_CONTROL */
57 STATIC dispatch_queue_t S_IPMonitorControlServerQueue
;
59 typedef struct ControlSession ControlSession
, * ControlSessionRef
;
61 #define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession)
62 #define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession)
63 LIST_HEAD_ControlSession S_ControlSessions
;
65 struct ControlSession
{
66 LIST_ENTRY_ControlSession link
;
67 xpc_connection_t connection
;
68 CFMutableDictionaryRef assertions
; /* ifname<string> = rank<number> */
74 STATIC CFMutableArrayRef S_if_changes
;
75 STATIC CFRange S_if_changes_range
;
78 InterfaceChangedListAddInterface(CFStringRef ifname
)
80 if (S_if_changes
== NULL
) {
81 S_if_changes
= CFArrayCreateMutable(NULL
,
82 0, &kCFTypeArrayCallBacks
);
83 CFArrayAppendValue(S_if_changes
, ifname
);
84 S_if_changes_range
.length
= 1;
86 else if (CFArrayContainsValue(S_if_changes
, S_if_changes_range
,
88 CFArrayAppendValue(S_if_changes
, ifname
);
89 S_if_changes_range
.length
++;
94 InterfaceChangedListCopy(void)
96 CFArrayRef current_list
;
98 current_list
= S_if_changes
;
100 return (current_list
);
104 InterfaceRankAssertionAdd(const void * key
, const void * value
, void * context
)
106 CFMutableDictionaryRef
* assertions_p
;
107 CFNumberRef existing_rank
;
108 CFNumberRef rank
= (CFNumberRef
)value
;
110 assertions_p
= (CFMutableDictionaryRef
*)context
;
111 if (*assertions_p
== NULL
) {
113 = CFDictionaryCreateMutable(NULL
, 0,
114 &kCFTypeDictionaryKeyCallBacks
,
115 &kCFTypeDictionaryValueCallBacks
);
116 CFDictionarySetValue(*assertions_p
, key
, value
);
119 existing_rank
= CFDictionaryGetValue(*assertions_p
, key
);
120 if (existing_rank
== NULL
121 || (CFNumberCompare(rank
, existing_rank
, NULL
)
122 == kCFCompareGreaterThan
)) {
123 CFDictionarySetValue(*assertions_p
, key
, value
);
128 STATIC CFDictionaryRef
129 InterfaceRankAssertionsCopy(void)
131 CFMutableDictionaryRef assertions
= NULL
;
132 ControlSessionRef session
;
134 LIST_FOREACH(session
, &S_ControlSessions
, link
) {
135 if (session
->assertions
== NULL
) {
138 CFDictionaryApplyFunction(session
->assertions
,
139 InterfaceRankAssertionAdd
,
145 STATIC CFRunLoopRef S_runloop
;
146 STATIC CFRunLoopSourceRef S_signal_source
;
149 SetNotificationInfo(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
)
152 S_signal_source
= rls
;
157 GenerateNotification(void)
159 if (S_signal_source
!= NULL
) {
160 CFRunLoopSourceSignal(S_signal_source
);
161 if (S_runloop
!= NULL
) {
162 CFRunLoopWakeUp(S_runloop
);
172 AddChangedInterface(const void * key
, const void * value
, void * context
)
174 InterfaceChangedListAddInterface((CFStringRef
)key
);
179 ControlSessionInvalidate(ControlSessionRef session
)
182 my_log(LOG_NOTICE
, "Invalidating %p", session
);
184 LIST_REMOVE(session
, link
);
185 if (session
->assertions
!= NULL
) {
187 "IPMonitorControlServer: %p pid %d removing assertions %@",
189 xpc_connection_get_pid(session
->connection
),
190 session
->assertions
);
191 CFDictionaryApplyFunction(session
->assertions
, AddChangedInterface
,
193 CFRelease(session
->assertions
);
194 session
->assertions
= NULL
;
195 GenerateNotification();
201 ControlSessionRelease(void * p
)
204 my_log(LOG_NOTICE
, "Releasing %p", p
);
210 STATIC ControlSessionRef
211 ControlSessionLookup(xpc_connection_t connection
)
213 return ((ControlSessionRef
)xpc_connection_get_context(connection
));
216 STATIC ControlSessionRef
217 ControlSessionCreate(xpc_connection_t connection
)
219 ControlSessionRef session
;
221 session
= (ControlSessionRef
)malloc(sizeof(*session
));
222 bzero(session
, sizeof(*session
));
223 session
->connection
= connection
;
224 xpc_connection_set_finalizer_f(connection
, ControlSessionRelease
);
225 xpc_connection_set_context(connection
, session
);
226 LIST_INSERT_HEAD(&S_ControlSessions
, session
, link
);
228 my_log(LOG_NOTICE
, "Created %p (connection %p)", session
, connection
);
233 STATIC ControlSessionRef
234 ControlSessionGet(xpc_connection_t connection
)
236 ControlSessionRef session
;
238 session
= ControlSessionLookup(connection
);
239 if (session
!= NULL
) {
242 return (ControlSessionCreate(connection
));
246 ControlSessionSetInterfaceRank(ControlSessionRef session
,
248 SCNetworkServicePrimaryRank rank
)
250 CFStringRef ifname_cf
;
252 if (session
->assertions
== NULL
) {
253 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
254 /* no assertions, no need to store rank */
258 = CFDictionaryCreateMutable(NULL
, 0,
259 &kCFTypeDictionaryKeyCallBacks
,
260 &kCFTypeDictionaryValueCallBacks
);
262 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
263 kCFStringEncodingUTF8
);
265 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
266 CFDictionaryRemoveValue(session
->assertions
, ifname_cf
);
267 if (CFDictionaryGetCount(session
->assertions
) == 0) {
268 CFRelease(session
->assertions
);
269 session
->assertions
= NULL
;
275 rank_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank
);
276 CFDictionarySetValue(session
->assertions
, ifname_cf
, rank_cf
);
279 InterfaceChangedListAddInterface(ifname_cf
);
280 GenerateNotification();
281 CFRelease(ifname_cf
);
285 STATIC SCNetworkServicePrimaryRank
286 ControlSessionGetInterfaceRank(ControlSessionRef session
,
289 SCNetworkServicePrimaryRank rank
= kSCNetworkServicePrimaryRankDefault
;
291 if (session
->assertions
!= NULL
) {
292 CFStringRef ifname_cf
;
295 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
296 kCFStringEncodingUTF8
);
297 rank_cf
= CFDictionaryGetValue(session
->assertions
, ifname_cf
);
298 CFRelease(ifname_cf
);
299 if (rank_cf
!= NULL
) {
300 (void)CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &rank
);
307 ** IPMonitorControlServer
310 IPMonitorControlServerValidateConnection(xpc_connection_t connection
)
314 uid
= xpc_connection_get_euid(connection
);
319 IPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection
,
320 xpc_object_t request
,
324 SCNetworkServicePrimaryRank rank
;
325 ControlSessionRef session
;
327 if (IPMonitorControlServerValidateConnection(connection
) == FALSE
) {
329 "IPMonitorControlServer: %p pid %d permission denied",
330 connection
, xpc_connection_get_pid(connection
));
334 = xpc_dictionary_get_string(request
,
335 kIPMonitorControlRequestKeyInterfaceName
);
336 if (ifname
== NULL
) {
339 rank
= (SCNetworkServicePrimaryRank
)
340 xpc_dictionary_get_uint64(request
,
341 kIPMonitorControlRequestKeyPrimaryRank
);
343 case kSCNetworkServicePrimaryRankDefault
:
344 case kSCNetworkServicePrimaryRankFirst
:
345 case kSCNetworkServicePrimaryRankLast
:
346 case kSCNetworkServicePrimaryRankNever
:
347 case kSCNetworkServicePrimaryRankScoped
:
352 session
= ControlSessionGet(connection
);
353 ControlSessionSetInterfaceRank(session
, ifname
, rank
);
355 "IPMonitorControlServer: %p pid %d set %s %u",
356 connection
, xpc_connection_get_pid(connection
), ifname
, rank
);
361 IPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection
,
362 xpc_object_t request
,
366 SCNetworkServicePrimaryRank rank
;
367 ControlSessionRef session
;
370 /* no point in processing the request if we can't provide an answer */
373 session
= ControlSessionLookup(connection
);
374 if (session
== NULL
) {
375 /* no session, no rank assertion */
379 = xpc_dictionary_get_string(request
,
380 kIPMonitorControlRequestKeyInterfaceName
);
381 if (ifname
== NULL
) {
384 rank
= ControlSessionGetInterfaceRank(session
, ifname
);
385 xpc_dictionary_set_uint64(reply
, kIPMonitorControlResponseKeyPrimaryRank
,
391 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection
)
393 ControlSessionRef session
;
396 my_log(LOG_NOTICE
, "IPMonitorControlServer: client %p went away",
399 session
= ControlSessionLookup(connection
);
400 if (session
== NULL
) {
401 /* never asserted anything */
404 ControlSessionInvalidate(session
);
409 IPMonitorControlServerHandleRequest(xpc_connection_t connection
,
410 xpc_object_t request
)
414 type
= xpc_get_type(request
);
415 if (type
== XPC_TYPE_DICTIONARY
) {
417 uint64_t request_type
;
418 xpc_connection_t remote
;
422 = xpc_dictionary_get_uint64(request
,
423 kIPMonitorControlRequestKeyType
);
424 reply
= xpc_dictionary_create_reply(request
);
425 switch (request_type
) {
426 case kIPMonitorControlRequestTypeSetInterfaceRank
:
427 error
= IPMonitorControlServerHandleSetInterfaceRank(connection
,
431 case kIPMonitorControlRequestTypeGetInterfaceRank
:
432 error
= IPMonitorControlServerHandleGetInterfaceRank(connection
,
441 /* client didn't want a reply */
444 xpc_dictionary_set_int64(reply
, kIPMonitorControlResponseKeyError
,
446 remote
= xpc_dictionary_get_remote_connection(request
);
447 xpc_connection_send_message(remote
, reply
);
450 else if (type
== XPC_TYPE_ERROR
) {
451 if (request
== XPC_ERROR_CONNECTION_INVALID
) {
452 IPMonitorControlServerHandleDisconnect(connection
);
454 else if (request
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
456 "IPMonitorControlServer: connection interrupted");
460 my_log(LOG_NOTICE
, "IPMonitorControlServer: unexpected event");
466 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection
)
468 xpc_handler_t handler
;
470 handler
= ^(xpc_object_t event
) {
471 IPMonitorControlServerHandleRequest(connection
, event
);
473 xpc_connection_set_event_handler(connection
, handler
);
474 xpc_connection_resume(connection
);
478 STATIC xpc_connection_t
479 IPMonitorControlServerCreate(dispatch_queue_t queue
, const char * name
)
481 uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_LISTENER
;
482 xpc_connection_t connection
;
483 xpc_handler_t handler
;
485 connection
= xpc_connection_create_mach_service(name
, queue
, flags
);
486 if (connection
== NULL
) {
489 handler
= ^(xpc_object_t event
) {
492 type
= xpc_get_type(event
);
493 if (type
== XPC_TYPE_CONNECTION
) {
494 IPMonitorControlServerHandleNewConnection(event
);
496 else if (type
== XPC_TYPE_ERROR
) {
499 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
500 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
501 my_log(LOG_NOTICE
, "IPMonitorControlServer: %s", desc
);
502 xpc_release(connection
);
505 my_log(LOG_NOTICE
, "IPMonitorControlServer: %s", desc
);
509 my_log(LOG_NOTICE
, "IPMonitorControlServer: unknown event %p",
513 S_IPMonitorControlServerQueue
= queue
;
514 xpc_connection_set_event_handler(connection
, handler
);
515 xpc_connection_resume(connection
);
519 PRIVATE_EXTERN Boolean
520 IPMonitorControlServerStart(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
,
524 xpc_connection_t connection
;
527 SetNotificationInfo(runloop
, rls
);
528 q
= dispatch_queue_create("IPMonitorControlServer", NULL
);
529 connection
= IPMonitorControlServerCreate(q
, kIPMonitorControlServerName
);
530 if (connection
== NULL
) {
532 "IPMonitorControlServer: failed to create server");
539 PRIVATE_EXTERN CFArrayRef
540 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef
* info
)
542 __block CFArrayRef changed
;
543 __block CFDictionaryRef dict
;
545 dispatch_sync(S_IPMonitorControlServerQueue
,
547 dict
= InterfaceRankAssertionsCopy();
548 changed
= InterfaceChangedListCopy();