2 * Copyright (c) 2013-2016 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 InterfaceChangedListAddInterface((CFStringRef
)key
);
176 ControlSessionInvalidate(ControlSessionRef session
)
178 my_log(LOG_DEBUG
, "Invalidating %p", session
);
179 LIST_REMOVE(session
, link
);
180 if (session
->assertions
!= NULL
) {
182 "IPMonitorControlServer: %p pid %d removing assertions %@",
184 xpc_connection_get_pid(session
->connection
),
185 session
->assertions
);
186 CFDictionaryApplyFunction(session
->assertions
, AddChangedInterface
,
188 CFRelease(session
->assertions
);
189 session
->assertions
= NULL
;
190 GenerateNotification();
196 ControlSessionRelease(void * p
)
198 my_log(LOG_DEBUG
, "Releasing %p", p
);
203 STATIC ControlSessionRef
204 ControlSessionLookup(xpc_connection_t connection
)
206 return ((ControlSessionRef
)xpc_connection_get_context(connection
));
209 STATIC ControlSessionRef
210 ControlSessionCreate(xpc_connection_t connection
)
212 ControlSessionRef session
;
214 session
= (ControlSessionRef
)malloc(sizeof(*session
));
215 bzero(session
, sizeof(*session
));
216 session
->connection
= connection
;
217 xpc_connection_set_finalizer_f(connection
, ControlSessionRelease
);
218 xpc_connection_set_context(connection
, session
);
219 LIST_INSERT_HEAD(&S_ControlSessions
, session
, link
);
220 my_log(LOG_DEBUG
, "Created %p (connection %p)", session
, connection
);
224 STATIC ControlSessionRef
225 ControlSessionGet(xpc_connection_t connection
)
227 ControlSessionRef session
;
229 session
= ControlSessionLookup(connection
);
230 if (session
!= NULL
) {
233 return (ControlSessionCreate(connection
));
237 ControlSessionSetInterfaceRank(ControlSessionRef session
,
239 SCNetworkServicePrimaryRank rank
)
241 CFStringRef ifname_cf
;
243 if (session
->assertions
== NULL
) {
244 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
245 /* no assertions, no need to store rank */
249 = CFDictionaryCreateMutable(NULL
, 0,
250 &kCFTypeDictionaryKeyCallBacks
,
251 &kCFTypeDictionaryValueCallBacks
);
253 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
254 kCFStringEncodingUTF8
);
256 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
257 CFDictionaryRemoveValue(session
->assertions
, ifname_cf
);
258 if (CFDictionaryGetCount(session
->assertions
) == 0) {
259 CFRelease(session
->assertions
);
260 session
->assertions
= NULL
;
266 rank_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank
);
267 CFDictionarySetValue(session
->assertions
, ifname_cf
, rank_cf
);
270 InterfaceChangedListAddInterface(ifname_cf
);
271 GenerateNotification();
272 CFRelease(ifname_cf
);
276 STATIC SCNetworkServicePrimaryRank
277 ControlSessionGetInterfaceRank(ControlSessionRef session
,
280 SCNetworkServicePrimaryRank rank
= kSCNetworkServicePrimaryRankDefault
;
282 if (session
->assertions
!= NULL
) {
283 CFStringRef ifname_cf
;
286 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
,
287 kCFStringEncodingUTF8
);
288 rank_cf
= CFDictionaryGetValue(session
->assertions
, ifname_cf
);
289 CFRelease(ifname_cf
);
290 if (rank_cf
!= NULL
) {
291 (void)CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &rank
);
298 ** IPMonitorControlServer
301 IPMonitorControlServerValidateConnection(xpc_connection_t connection
)
305 uid
= xpc_connection_get_euid(connection
);
310 IPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection
,
311 xpc_object_t request
,
315 SCNetworkServicePrimaryRank rank
;
316 ControlSessionRef session
;
318 if (!IPMonitorControlServerValidateConnection(connection
)) {
319 my_log(LOG_INFO
, "connection %p pid %d permission denied",
320 connection
, xpc_connection_get_pid(connection
));
324 = xpc_dictionary_get_string(request
,
325 kIPMonitorControlRequestKeyInterfaceName
);
326 if (ifname
== NULL
) {
329 rank
= (SCNetworkServicePrimaryRank
)
330 xpc_dictionary_get_uint64(request
,
331 kIPMonitorControlRequestKeyPrimaryRank
);
333 case kSCNetworkServicePrimaryRankDefault
:
334 case kSCNetworkServicePrimaryRankFirst
:
335 case kSCNetworkServicePrimaryRankLast
:
336 case kSCNetworkServicePrimaryRankNever
:
337 case kSCNetworkServicePrimaryRankScoped
:
342 session
= ControlSessionGet(connection
);
343 ControlSessionSetInterfaceRank(session
, ifname
, rank
);
344 my_log(LOG_INFO
, "connection %p pid %d set %s %u",
345 connection
, xpc_connection_get_pid(connection
), ifname
, rank
);
350 IPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection
,
351 xpc_object_t request
,
355 SCNetworkServicePrimaryRank rank
;
356 ControlSessionRef session
;
359 /* no point in processing the request if we can't provide an answer */
362 session
= ControlSessionLookup(connection
);
363 if (session
== NULL
) {
364 /* no session, no rank assertion */
368 = xpc_dictionary_get_string(request
,
369 kIPMonitorControlRequestKeyInterfaceName
);
370 if (ifname
== NULL
) {
373 rank
= ControlSessionGetInterfaceRank(session
, ifname
);
374 xpc_dictionary_set_uint64(reply
, kIPMonitorControlResponseKeyPrimaryRank
,
380 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection
)
382 ControlSessionRef session
;
384 my_log(LOG_DEBUG
, "IPMonitorControlServer: client %p went away", connection
);
385 session
= ControlSessionLookup(connection
);
386 if (session
== NULL
) {
387 /* never asserted anything */
390 ControlSessionInvalidate(session
);
395 IPMonitorControlServerHandleRequest(xpc_connection_t connection
,
396 xpc_object_t request
)
400 type
= xpc_get_type(request
);
401 if (type
== XPC_TYPE_DICTIONARY
) {
403 uint64_t request_type
;
404 xpc_connection_t remote
;
408 = xpc_dictionary_get_uint64(request
,
409 kIPMonitorControlRequestKeyType
);
410 reply
= xpc_dictionary_create_reply(request
);
411 switch (request_type
) {
412 case kIPMonitorControlRequestTypeSetInterfaceRank
:
413 error
= IPMonitorControlServerHandleSetInterfaceRank(connection
,
417 case kIPMonitorControlRequestTypeGetInterfaceRank
:
418 error
= IPMonitorControlServerHandleGetInterfaceRank(connection
,
427 /* client didn't want a reply */
430 xpc_dictionary_set_int64(reply
, kIPMonitorControlResponseKeyError
,
432 remote
= xpc_dictionary_get_remote_connection(request
);
433 xpc_connection_send_message(remote
, reply
);
436 else if (type
== XPC_TYPE_ERROR
) {
437 if (request
== XPC_ERROR_CONNECTION_INVALID
) {
438 IPMonitorControlServerHandleDisconnect(connection
);
440 else if (request
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
441 my_log(LOG_INFO
, "connection interrupted");
445 my_log(LOG_NOTICE
, "unexpected event");
451 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection
)
453 xpc_handler_t handler
;
455 handler
= ^(xpc_object_t event
) {
456 IPMonitorControlServerHandleRequest(connection
, event
);
458 xpc_connection_set_event_handler(connection
, handler
);
459 xpc_connection_set_target_queue(connection
, S_IPMonitorControlServerQueue
);
460 xpc_connection_resume(connection
);
464 STATIC xpc_connection_t
465 IPMonitorControlServerCreate(dispatch_queue_t queue
, const char * name
)
467 uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_LISTENER
;
468 xpc_connection_t connection
;
469 xpc_handler_t handler
;
471 connection
= xpc_connection_create_mach_service(name
, queue
, flags
);
472 if (connection
== NULL
) {
475 handler
= ^(xpc_object_t event
) {
478 type
= xpc_get_type(event
);
479 if (type
== XPC_TYPE_CONNECTION
) {
480 IPMonitorControlServerHandleNewConnection(event
);
482 else if (type
== XPC_TYPE_ERROR
) {
485 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
486 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
487 my_log(LOG_NOTICE
, "%s", desc
);
488 xpc_release(connection
);
491 my_log(LOG_NOTICE
, "%s", desc
);
495 my_log(LOG_NOTICE
, "unknown event %p", type
);
498 S_IPMonitorControlServerQueue
= queue
;
499 xpc_connection_set_event_handler(connection
, handler
);
500 xpc_connection_resume(connection
);
504 PRIVATE_EXTERN Boolean
505 IPMonitorControlServerStart(CFRunLoopRef runloop
, CFRunLoopSourceRef rls
,
509 xpc_connection_t connection
;
511 SetNotificationInfo(runloop
, rls
);
512 q
= dispatch_queue_create("IPMonitorControlServer", NULL
);
513 connection
= IPMonitorControlServerCreate(q
, kIPMonitorControlServerName
);
514 if (connection
== NULL
) {
516 "IPMonitorControlServer: failed to create server");
523 PRIVATE_EXTERN CFArrayRef
524 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef
* info
)
526 __block CFArrayRef changed
;
527 __block CFDictionaryRef dict
;
529 dispatch_sync(S_IPMonitorControlServerQueue
,
531 dict
= InterfaceRankAssertionsCopy();
532 changed
= InterfaceChangedListCopy();