2 * Copyright (c) 2013-2015 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, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\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
,
86 CFArrayAppendValue(S_if_changes
, ifname
);
87 S_if_changes_range
.length
++;
92 InterfaceChangedListCopy(void)
94 CFArrayRef current_list
;
96 current_list
= S_if_changes
;
98 return (current_list
);
102 InterfaceRankAssertionAdd(const void * key
, const void * value
, void * context
)
104 CFMutableDictionaryRef
* assertions_p
;
105 CFNumberRef existing_rank
;
106 CFNumberRef rank
= (CFNumberRef
)value
;
108 assertions_p
= (CFMutableDictionaryRef
*)context
;
109 if (*assertions_p
== NULL
) {
111 = CFDictionaryCreateMutable(NULL
, 0,
112 &kCFTypeDictionaryKeyCallBacks
,
113 &kCFTypeDictionaryValueCallBacks
);
114 CFDictionarySetValue(*assertions_p
, key
, value
);
117 existing_rank
= CFDictionaryGetValue(*assertions_p
, key
);
118 if (existing_rank
== NULL
119 || (CFNumberCompare(rank
, existing_rank
, NULL
)
120 == kCFCompareGreaterThan
)) {
121 CFDictionarySetValue(*assertions_p
, key
, value
);
126 STATIC CFDictionaryRef
127 InterfaceRankAssertionsCopy(void)
129 CFMutableDictionaryRef assertions
= NULL
;
130 ControlSessionRef session
;
132 LIST_FOREACH(session
, &S_ControlSessions
, link
) {
133 if (session
->assertions
== NULL
) {
136 CFDictionaryApplyFunction(session
->assertions
,
137 InterfaceRankAssertionAdd
,
143 STATIC CFRunLoopRef S_runloop
;
144 STATIC CFRunLoopSourceRef S_signal_source
;
147 SetNotificationInfo(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
)
150 S_signal_source
= rls
;
155 GenerateNotification(void)
157 if (S_signal_source
!= NULL
) {
158 CFRunLoopSourceSignal(S_signal_source
);
159 if (S_runloop
!= NULL
) {
160 CFRunLoopWakeUp(S_runloop
);
170 AddChangedInterface(const void * key
, const void * value
, void * context
)
172 InterfaceChangedListAddInterface((CFStringRef
)key
);
177 ControlSessionInvalidate(ControlSessionRef session
)
179 my_log(LOG_DEBUG
, "Invalidating %p", session
);
180 LIST_REMOVE(session
, link
);
181 if (session
->assertions
!= NULL
) {
183 "IPMonitorControlServer: %p pid %d removing assertions %@",
185 xpc_connection_get_pid(session
->connection
),
186 session
->assertions
);
187 CFDictionaryApplyFunction(session
->assertions
, AddChangedInterface
,
189 CFRelease(session
->assertions
);
190 session
->assertions
= NULL
;
191 GenerateNotification();
197 ControlSessionRelease(void * p
)
199 my_log(LOG_DEBUG
, "Releasing %p", p
);
204 STATIC ControlSessionRef
205 ControlSessionLookup(xpc_connection_t connection
)
207 return ((ControlSessionRef
)xpc_connection_get_context(connection
));
210 STATIC ControlSessionRef
211 ControlSessionCreate(xpc_connection_t connection
)
213 ControlSessionRef session
;
215 session
= (ControlSessionRef
)malloc(sizeof(*session
));
216 bzero(session
, sizeof(*session
));
217 session
->connection
= connection
;
218 xpc_connection_set_finalizer_f(connection
, ControlSessionRelease
);
219 xpc_connection_set_context(connection
, session
);
220 LIST_INSERT_HEAD(&S_ControlSessions
, session
, link
);
221 my_log(LOG_DEBUG
, "Created %p (connection %p)", session
, connection
);
225 STATIC ControlSessionRef
226 ControlSessionGet(xpc_connection_t connection
)
228 ControlSessionRef session
;
230 session
= ControlSessionLookup(connection
);
231 if (session
!= NULL
) {
234 return (ControlSessionCreate(connection
));
238 ControlSessionSetInterfaceRank(ControlSessionRef session
,
240 SCNetworkServicePrimaryRank rank
)
242 CFStringRef ifname_cf
;
244 if (session
->assertions
== NULL
) {
245 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
246 /* no assertions, no need to store rank */
250 = CFDictionaryCreateMutable(NULL
, 0,
251 &kCFTypeDictionaryKeyCallBacks
,
252 &kCFTypeDictionaryValueCallBacks
);
254 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
255 kCFStringEncodingUTF8
);
257 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
258 CFDictionaryRemoveValue(session
->assertions
, ifname_cf
);
259 if (CFDictionaryGetCount(session
->assertions
) == 0) {
260 CFRelease(session
->assertions
);
261 session
->assertions
= NULL
;
267 rank_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank
);
268 CFDictionarySetValue(session
->assertions
, ifname_cf
, rank_cf
);
271 InterfaceChangedListAddInterface(ifname_cf
);
272 GenerateNotification();
273 CFRelease(ifname_cf
);
277 STATIC SCNetworkServicePrimaryRank
278 ControlSessionGetInterfaceRank(ControlSessionRef session
,
281 SCNetworkServicePrimaryRank rank
= kSCNetworkServicePrimaryRankDefault
;
283 if (session
->assertions
!= NULL
) {
284 CFStringRef ifname_cf
;
287 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
288 kCFStringEncodingUTF8
);
289 rank_cf
= CFDictionaryGetValue(session
->assertions
, ifname_cf
);
290 CFRelease(ifname_cf
);
291 if (rank_cf
!= NULL
) {
292 (void)CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &rank
);
299 ** IPMonitorControlServer
302 IPMonitorControlServerValidateConnection(xpc_connection_t connection
)
306 uid
= xpc_connection_get_euid(connection
);
311 IPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection
,
312 xpc_object_t request
,
316 SCNetworkServicePrimaryRank rank
;
317 ControlSessionRef session
;
319 if (IPMonitorControlServerValidateConnection(connection
) == FALSE
) {
320 my_log(LOG_INFO
, "connection %p pid %d permission denied",
321 connection
, xpc_connection_get_pid(connection
));
325 = xpc_dictionary_get_string(request
,
326 kIPMonitorControlRequestKeyInterfaceName
);
327 if (ifname
== NULL
) {
330 rank
= (SCNetworkServicePrimaryRank
)
331 xpc_dictionary_get_uint64(request
,
332 kIPMonitorControlRequestKeyPrimaryRank
);
334 case kSCNetworkServicePrimaryRankDefault
:
335 case kSCNetworkServicePrimaryRankFirst
:
336 case kSCNetworkServicePrimaryRankLast
:
337 case kSCNetworkServicePrimaryRankNever
:
338 case kSCNetworkServicePrimaryRankScoped
:
343 session
= ControlSessionGet(connection
);
344 ControlSessionSetInterfaceRank(session
, ifname
, rank
);
345 my_log(LOG_INFO
, "connection %p pid %d set %s %u",
346 connection
, xpc_connection_get_pid(connection
), ifname
, rank
);
351 IPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection
,
352 xpc_object_t request
,
356 SCNetworkServicePrimaryRank rank
;
357 ControlSessionRef session
;
360 /* no point in processing the request if we can't provide an answer */
363 session
= ControlSessionLookup(connection
);
364 if (session
== NULL
) {
365 /* no session, no rank assertion */
369 = xpc_dictionary_get_string(request
,
370 kIPMonitorControlRequestKeyInterfaceName
);
371 if (ifname
== NULL
) {
374 rank
= ControlSessionGetInterfaceRank(session
, ifname
);
375 xpc_dictionary_set_uint64(reply
, kIPMonitorControlResponseKeyPrimaryRank
,
381 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection
)
383 ControlSessionRef session
;
385 my_log(LOG_DEBUG
, "IPMonitorControlServer: client %p went away", connection
);
386 session
= ControlSessionLookup(connection
);
387 if (session
== NULL
) {
388 /* never asserted anything */
391 ControlSessionInvalidate(session
);
396 IPMonitorControlServerHandleRequest(xpc_connection_t connection
,
397 xpc_object_t request
)
401 type
= xpc_get_type(request
);
402 if (type
== XPC_TYPE_DICTIONARY
) {
404 uint64_t request_type
;
405 xpc_connection_t remote
;
409 = xpc_dictionary_get_uint64(request
,
410 kIPMonitorControlRequestKeyType
);
411 reply
= xpc_dictionary_create_reply(request
);
412 switch (request_type
) {
413 case kIPMonitorControlRequestTypeSetInterfaceRank
:
414 error
= IPMonitorControlServerHandleSetInterfaceRank(connection
,
418 case kIPMonitorControlRequestTypeGetInterfaceRank
:
419 error
= IPMonitorControlServerHandleGetInterfaceRank(connection
,
428 /* client didn't want a reply */
431 xpc_dictionary_set_int64(reply
, kIPMonitorControlResponseKeyError
,
433 remote
= xpc_dictionary_get_remote_connection(request
);
434 xpc_connection_send_message(remote
, reply
);
437 else if (type
== XPC_TYPE_ERROR
) {
438 if (request
== XPC_ERROR_CONNECTION_INVALID
) {
439 IPMonitorControlServerHandleDisconnect(connection
);
441 else if (request
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
442 my_log(LOG_INFO
, "connection interrupted");
446 my_log(LOG_NOTICE
, "unexpected event");
452 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection
)
454 xpc_handler_t handler
;
456 handler
= ^(xpc_object_t event
) {
457 os_activity_t activity_id
;
459 activity_id
= os_activity_start("processing IPMonitor [rank] request",
460 OS_ACTIVITY_FLAG_DEFAULT
);
462 IPMonitorControlServerHandleRequest(connection
, event
);
464 os_activity_end(activity_id
);
466 xpc_connection_set_event_handler(connection
, handler
);
467 xpc_connection_resume(connection
);
471 STATIC xpc_connection_t
472 IPMonitorControlServerCreate(dispatch_queue_t queue
, const char * name
)
474 uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_LISTENER
;
475 xpc_connection_t connection
;
476 xpc_handler_t handler
;
478 connection
= xpc_connection_create_mach_service(name
, queue
, flags
);
479 if (connection
== NULL
) {
482 handler
= ^(xpc_object_t event
) {
483 os_activity_t activity_id
;
486 activity_id
= os_activity_start("processing IPMonitor [rank] connection request",
487 OS_ACTIVITY_FLAG_DEFAULT
);
489 type
= xpc_get_type(event
);
490 if (type
== XPC_TYPE_CONNECTION
) {
491 IPMonitorControlServerHandleNewConnection(event
);
493 else if (type
== XPC_TYPE_ERROR
) {
496 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
497 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
498 my_log(LOG_NOTICE
, "%s", desc
);
499 xpc_release(connection
);
502 my_log(LOG_NOTICE
, "%s", desc
);
506 my_log(LOG_NOTICE
, "unknown event %p", type
);
509 os_activity_end(activity_id
);
511 S_IPMonitorControlServerQueue
= queue
;
512 xpc_connection_set_event_handler(connection
, handler
);
513 xpc_connection_resume(connection
);
517 PRIVATE_EXTERN Boolean
518 IPMonitorControlServerStart(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
,
522 xpc_connection_t connection
;
524 SetNotificationInfo(runloop
, rls
);
525 q
= dispatch_queue_create("IPMonitorControlServer", NULL
);
526 connection
= IPMonitorControlServerCreate(q
, kIPMonitorControlServerName
);
527 if (connection
== NULL
) {
529 "IPMonitorControlServer: failed to create server");
536 PRIVATE_EXTERN CFArrayRef
537 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef
* info
)
539 __block CFArrayRef changed
;
540 __block CFDictionaryRef dict
;
542 dispatch_sync(S_IPMonitorControlServerQueue
,
544 dict
= InterfaceRankAssertionsCopy();
545 changed
= InterfaceChangedListCopy();