2 * Copyright (c) 2013-2018 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@
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 "IPMonitorControl.h"
38 #include "IPMonitorControlPrivate.h"
39 #include "symbol_scope.h"
40 #include <CoreFoundation/CFRuntime.h>
43 #include <xpc/private.h>
44 #include <SystemConfiguration/SCPrivate.h>
46 #ifdef TEST_IPMONITOR_CONTROL
48 #define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__)
50 #else /* TEST_IPMONITOR_CONTROL */
52 #define my_log(__level, __format, ...) SC_log(__level, __format, ## __VA_ARGS__)
54 #endif /* TEST_IPMONITOR_CONTROL */
57 ** IPMonitorControl CF object glue
60 struct IPMonitorControl
{
61 CFRuntimeBase cf_base
;
63 dispatch_queue_t queue
;
64 xpc_connection_t connection
;
66 CFMutableDictionaryRef assertions
; /* ifname<string> = rank<number> */
67 CFMutableDictionaryRef advisories
; /* ifname<string> = adv<number> */
70 STATIC CFStringRef
__IPMonitorControlCopyDebugDesc(CFTypeRef cf
);
71 STATIC
void __IPMonitorControlDeallocate(CFTypeRef cf
);
73 STATIC CFTypeID __kIPMonitorControlTypeID
= _kCFRuntimeNotATypeID
;
75 STATIC
const CFRuntimeClass __IPMonitorControlClass
= {
77 "IPMonitorControl", /* className */
80 __IPMonitorControlDeallocate
, /* deallocate */
83 NULL
, /* copyFormattingDesc */
84 __IPMonitorControlCopyDebugDesc
/* copyDebugDesc */
88 __IPMonitorControlCopyDebugDesc(CFTypeRef cf
)
90 CFAllocatorRef allocator
= CFGetAllocator(cf
);
91 IPMonitorControlRef control
= (IPMonitorControlRef
)cf
;
93 return (CFStringCreateWithFormat(allocator
, NULL
,
94 CFSTR("<IPMonitorControl %p>"),
99 __IPMonitorControlDeallocate(CFTypeRef cf
)
101 IPMonitorControlRef control
= (IPMonitorControlRef
)cf
;
103 if (control
->connection
!= NULL
) {
104 xpc_release(control
->connection
);
106 if (control
->queue
!= NULL
) {
107 dispatch_release(control
->queue
);
109 my_CFRelease(&control
->advisories
);
110 my_CFRelease(&control
->assertions
);
115 ** IPMonitorControl support functions
118 __IPMonitorControlRegisterClass(void)
120 STATIC dispatch_once_t once
;
122 dispatch_once(&once
, ^{
123 __kIPMonitorControlTypeID
124 = _CFRuntimeRegisterClass(&__IPMonitorControlClass
);
129 STATIC IPMonitorControlRef
130 __IPMonitorControlAllocate(CFAllocatorRef allocator
)
132 IPMonitorControlRef control
;
135 __IPMonitorControlRegisterClass();
136 size
= sizeof(*control
) - sizeof(CFRuntimeBase
);
137 control
= (IPMonitorControlRef
)
138 _CFRuntimeCreateInstance(allocator
,
139 __kIPMonitorControlTypeID
, size
, NULL
);
144 create_request_dictionary(void)
146 const char * progname
;
147 xpc_object_t request
;
149 request
= xpc_dictionary_create(NULL
, NULL
, 0);
150 progname
= getprogname();
151 if (progname
!= NULL
) {
152 xpc_dictionary_set_string(request
,
153 kIPMonitorControlRequestKeyProcessName
,
160 IPMonitorControlHandleResponse(xpc_object_t event
, Boolean async
,
163 Boolean retry
= FALSE
;
164 Boolean success
= FALSE
;
167 type
= xpc_get_type(event
);
168 if (type
== XPC_TYPE_DICTIONARY
) {
170 /* we don't expect async responses messages */
171 my_log(LOG_NOTICE
, "unexpected message");
176 error
= xpc_dictionary_get_int64(event
,
177 kIPMonitorControlResponseKeyError
);
180 #ifdef TEST_IPMONITOR_CONTROL
181 my_log(LOG_NOTICE
, "failure code %lld", error
);
182 #endif /* TEST_IPMONITOR_CONTROL */
189 else if (type
== XPC_TYPE_ERROR
) {
190 if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
191 #ifdef TEST_IPMONITOR_CONTROL
192 my_log(LOG_NOTICE
, "can retry");
193 #endif /* TEST_IPMONITOR_CONTROL */
199 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
200 my_log(LOG_NOTICE
, "%s", desc
);
204 my_log(LOG_NOTICE
, "unknown event type : %p", type
);
206 if (retry_p
!= NULL
) {
214 _IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control
,
215 CFStringRef ifname_cf
,
216 SCNetworkServicePrimaryRank rank
)
218 if (control
->assertions
== NULL
) {
219 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
220 /* no assertions, no need to store rank */
224 = CFDictionaryCreateMutable(NULL
, 0,
225 &kCFTypeDictionaryKeyCallBacks
,
226 &kCFTypeDictionaryValueCallBacks
);
228 if (rank
== kSCNetworkServicePrimaryRankDefault
) {
229 CFDictionaryRemoveValue(control
->assertions
, ifname_cf
);
230 if (CFDictionaryGetCount(control
->assertions
) == 0) {
231 my_CFRelease(&control
->assertions
);
237 rank_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank
);
238 CFDictionarySetValue(control
->assertions
, ifname_cf
, rank_cf
);
245 ApplyInterfaceRank(const void * key
, const void * value
, void * context
)
247 xpc_connection_t connection
= (xpc_connection_t
)context
;
248 char ifname
[IF_NAMESIZE
];
249 SCNetworkServicePrimaryRank rank
;
250 xpc_object_t request
;
252 if (!CFStringGetCString(key
, ifname
, sizeof(ifname
),
253 kCFStringEncodingUTF8
)) {
256 if (!CFNumberGetValue(value
, kCFNumberSInt32Type
, &rank
)) {
259 request
= create_request_dictionary();
260 xpc_dictionary_set_uint64(request
,
261 kIPMonitorControlRequestKeyType
,
262 kIPMonitorControlRequestTypeSetInterfaceRank
);
263 xpc_dictionary_set_string(request
,
264 kIPMonitorControlRequestKeyInterfaceName
,
266 xpc_dictionary_set_uint64(request
,
267 kIPMonitorControlRequestKeyPrimaryRank
,
269 xpc_connection_send_message(connection
, request
);
270 xpc_release(request
);
276 _IPMonitorControlSetInterfaceAdvisory(IPMonitorControlRef control
,
277 CFStringRef ifname_cf
,
278 SCNetworkInterfaceAdvisory advisory
)
280 if (control
->advisories
== NULL
) {
281 if (advisory
== kSCNetworkInterfaceAdvisoryNone
) {
282 /* no advisories, no need to store advisory */
286 = CFDictionaryCreateMutable(NULL
, 0,
287 &kCFTypeDictionaryKeyCallBacks
,
288 &kCFTypeDictionaryValueCallBacks
);
290 if (advisory
== kSCNetworkInterfaceAdvisoryNone
) {
291 CFDictionaryRemoveValue(control
->advisories
, ifname_cf
);
292 if (CFDictionaryGetCount(control
->advisories
) == 0) {
293 my_CFRelease(&control
->advisories
);
297 CFNumberRef advisory_cf
;
299 advisory_cf
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &advisory
);
300 CFDictionarySetValue(control
->advisories
, ifname_cf
, advisory_cf
);
301 CFRelease(advisory_cf
);
307 ApplyInterfaceAdvisory(const void * key
, const void * value
, void * context
)
309 xpc_connection_t connection
= (xpc_connection_t
)context
;
310 char ifname
[IF_NAMESIZE
];
311 SCNetworkInterfaceAdvisory advisory
;
312 xpc_object_t request
;
314 if (!CFStringGetCString(key
, ifname
, sizeof(ifname
),
315 kCFStringEncodingUTF8
)) {
318 if (!CFNumberGetValue(value
, kCFNumberSInt32Type
, &advisory
)) {
321 request
= create_request_dictionary();
322 xpc_dictionary_set_uint64(request
,
323 kIPMonitorControlRequestKeyType
,
324 kIPMonitorControlRequestTypeSetInterfaceAdvisory
);
325 xpc_dictionary_set_string(request
,
326 kIPMonitorControlRequestKeyInterfaceName
,
328 xpc_dictionary_set_uint64(request
,
329 kIPMonitorControlRequestKeyAdvisory
,
331 xpc_connection_send_message(connection
, request
);
332 xpc_release(request
);
338 ** IPMonitorControl SPI
340 PRIVATE_EXTERN IPMonitorControlRef
341 IPMonitorControlCreate(void)
343 xpc_connection_t connection
;
344 IPMonitorControlRef control
;
345 uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
;
346 xpc_handler_t handler
;
347 dispatch_queue_t queue
;
349 control
= __IPMonitorControlAllocate(NULL
);
350 queue
= dispatch_queue_create("IPMonitorControl", NULL
);
352 = xpc_connection_create_mach_service(kIPMonitorControlServerName
,
354 handler
= ^(xpc_object_t event
) {
357 (void)IPMonitorControlHandleResponse(event
, TRUE
, &retry
);
359 if (control
->assertions
!= NULL
) {
360 CFDictionaryApplyFunction(control
->assertions
,
362 control
->connection
);
364 if (control
->advisories
!= NULL
) {
365 CFDictionaryApplyFunction(control
->advisories
,
366 ApplyInterfaceAdvisory
,
367 control
->connection
);
371 xpc_connection_set_event_handler(connection
, handler
);
372 control
->connection
= connection
;
373 control
->queue
= queue
;
374 xpc_connection_resume(connection
);
379 IPMonitorControlSendRequest(IPMonitorControlRef control
,
380 xpc_object_t request
)
385 Boolean retry_on_error
= FALSE
;
388 reply
= xpc_connection_send_message_with_reply_sync(control
->connection
,
391 my_log(LOG_NOTICE
, "failed to send message");
394 success
= IPMonitorControlHandleResponse(reply
, FALSE
,
401 if (retry_on_error
) {
404 my_log(LOG_NOTICE
, "fatal error");
410 PRIVATE_EXTERN Boolean
411 IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control
,
412 CFStringRef ifname_cf
,
413 SCNetworkServicePrimaryRank rank
)
415 char ifname
[IF_NAMESIZE
];
417 xpc_object_t request
;
418 Boolean success
= FALSE
;
420 if (!CFStringGetCString(ifname_cf
, ifname
, sizeof(ifname
),
421 kCFStringEncodingUTF8
)) {
425 request
= create_request_dictionary();
426 xpc_dictionary_set_uint64(request
,
427 kIPMonitorControlRequestKeyType
,
428 kIPMonitorControlRequestTypeSetInterfaceRank
);
429 xpc_dictionary_set_string(request
,
430 kIPMonitorControlRequestKeyInterfaceName
,
432 xpc_dictionary_set_uint64(request
,
433 kIPMonitorControlRequestKeyPrimaryRank
,
435 reply
= IPMonitorControlSendRequest(control
, request
);
436 xpc_release(request
);
444 dispatch_async(control
->queue
,
446 _IPMonitorControlSetInterfacePrimaryRank(control
,
449 CFRelease(ifname_cf
);
456 PRIVATE_EXTERN SCNetworkServicePrimaryRank
457 IPMonitorControlGetInterfacePrimaryRank(IPMonitorControlRef control
,
458 CFStringRef ifname_cf
)
460 char ifname
[IF_NAMESIZE
];
461 SCNetworkServicePrimaryRank rank
;
463 xpc_object_t request
;
465 rank
= kSCNetworkServicePrimaryRankDefault
;
466 if (!CFStringGetCString(ifname_cf
, ifname
, sizeof(ifname
),
467 kCFStringEncodingUTF8
)) {
471 request
= create_request_dictionary();
472 xpc_dictionary_set_uint64(request
,
473 kIPMonitorControlRequestKeyType
,
474 kIPMonitorControlRequestTypeGetInterfaceRank
);
475 xpc_dictionary_set_string(request
,
476 kIPMonitorControlRequestKeyInterfaceName
,
478 reply
= IPMonitorControlSendRequest(control
, request
);
480 rank
= (SCNetworkServicePrimaryRank
)
481 xpc_dictionary_get_uint64(reply
,
482 kIPMonitorControlResponseKeyPrimaryRank
);
485 xpc_release(request
);
489 PRIVATE_EXTERN Boolean
490 IPMonitorControlSetInterfaceAdvisory(IPMonitorControlRef control
,
491 CFStringRef ifname_cf
,
492 SCNetworkInterfaceAdvisory advisory
,
495 char ifname
[IF_NAMESIZE
];
496 char * reason_str
= NULL
;
498 xpc_object_t request
;
499 Boolean success
= FALSE
;
501 if (!CFStringGetCString(ifname_cf
, ifname
, sizeof(ifname
),
502 kCFStringEncodingUTF8
)) {
505 if (reason
!= NULL
) {
507 = _SC_cfstring_to_cstring(reason
, NULL
, 0, kCFStringEncodingUTF8
);
509 request
= create_request_dictionary();
510 xpc_dictionary_set_uint64(request
,
511 kIPMonitorControlRequestKeyType
,
512 kIPMonitorControlRequestTypeSetInterfaceAdvisory
);
513 xpc_dictionary_set_string(request
,
514 kIPMonitorControlRequestKeyInterfaceName
,
516 xpc_dictionary_set_uint64(request
,
517 kIPMonitorControlRequestKeyAdvisory
,
519 if (reason_str
!= NULL
) {
520 xpc_dictionary_set_string(request
,
521 kIPMonitorControlRequestKeyReason
,
523 CFAllocatorDeallocate(NULL
, reason_str
);
525 reply
= IPMonitorControlSendRequest(control
, request
);
526 xpc_release(request
);
534 dispatch_async(control
->queue
,
536 _IPMonitorControlSetInterfaceAdvisory(control
,
539 CFRelease(ifname_cf
);
546 PRIVATE_EXTERN Boolean
547 IPMonitorControlInterfaceAdvisoryIsSet(IPMonitorControlRef control
,
548 CFStringRef ifname_cf
)
550 char ifname
[IF_NAMESIZE
];
552 xpc_object_t request
;
553 Boolean is_set
= FALSE
;
555 if (!CFStringGetCString(ifname_cf
, ifname
, sizeof(ifname
),
556 kCFStringEncodingUTF8
)) {
559 request
= create_request_dictionary();
560 xpc_dictionary_set_uint64(request
,
561 kIPMonitorControlRequestKeyType
,
562 kIPMonitorControlRequestTypeInterfaceAdvisoryIsSet
);
563 xpc_dictionary_set_string(request
,
564 kIPMonitorControlRequestKeyInterfaceName
,
566 reply
= IPMonitorControlSendRequest(control
, request
);
567 xpc_release(request
);
569 if (xpc_dictionary_get_bool(reply
,
570 kIPMonitorControlResponseKeyAdvisoryIsSet
)) {
578 PRIVATE_EXTERN Boolean
579 IPMonitorControlAnyInterfaceAdvisoryIsSet(IPMonitorControlRef control
)
582 xpc_object_t request
;
583 Boolean is_set
= FALSE
;
585 request
= create_request_dictionary();
586 xpc_dictionary_set_uint64(request
,
587 kIPMonitorControlRequestKeyType
,
588 kIPMonitorControlRequestTypeAnyInterfaceAdvisoryIsSet
);
589 reply
= IPMonitorControlSendRequest(control
, request
);
590 xpc_release(request
);
592 if (xpc_dictionary_get_bool(reply
,
593 kIPMonitorControlResponseKeyAdvisoryIsSet
)) {
601 PRIVATE_EXTERN CFStringRef
602 IPMonitorControlCopyInterfaceAdvisoryNotificationKey(CFStringRef ifname
)
604 return (_IPMonitorControlCopyInterfaceAdvisoryNotificationKey(ifname
));