]> git.saurik.com Git - apple/configd.git/blob - IPMonitorControl/IPMonitorControlServer.c
configd-1061.80.3.tar.gz
[apple/configd.git] / IPMonitorControl / IPMonitorControlServer.c
1 /*
2 * Copyright (c) 2013-2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * IPMonitorControlServer.c
26 * - IPC channel to IPMonitor
27 * - used to create interface rank assertions
28 */
29
30 /*
31 * Modification History
32 *
33 * December 16, 2013 Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 */
36
37 #include <CoreFoundation/CoreFoundation.h>
38 #include <xpc/xpc.h>
39 #include <xpc/private.h>
40 #include <sys/queue.h>
41 #include <CoreFoundation/CFRunLoop.h>
42 #include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
43 #include <SystemConfiguration/SCPrivate.h>
44 #include "IPMonitorControlServer.h"
45 #include "symbol_scope.h"
46 #include "IPMonitorControlPrivate.h"
47 #include "IPMonitorAWDReport.h"
48
49 #ifdef TEST_IPMONITOR_CONTROL
50 #define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__)
51
52 #else /* TEST_IPMONITOR_CONTROL */
53 #include "ip_plugin.h"
54 #endif /* TEST_IPMONITOR_CONTROL */
55
56 STATIC dispatch_queue_t S_IPMonitorControlServerQueue;
57
58 typedef struct ControlSession ControlSession, * ControlSessionRef;
59
60 #define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession)
61 #define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession)
62 LIST_HEAD_ControlSession S_ControlSessions;
63
64 struct ControlSession {
65 LIST_ENTRY_ControlSession link;
66 xpc_connection_t connection;
67
68 CFMutableDictionaryRef assertions; /* ifname<string> = rank<number> */
69 CFMutableDictionaryRef advisories; /* ifname<string> = advisory<number> */
70 };
71
72 /**
73 ** Support Functions
74 **/
75 STATIC CFMutableArrayRef S_if_changes;
76 STATIC CFRange S_if_changes_range;
77
78 STATIC CFNumberRef
79 RankLastNumberGet(void)
80 {
81 STATIC CFNumberRef rank_last;
82
83 if (rank_last == NULL) {
84 SCNetworkServicePrimaryRank rank;
85
86 rank = kSCNetworkServicePrimaryRankLast;
87 rank_last = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank);
88 }
89 return (rank_last);
90 }
91
92 STATIC void
93 InterfaceChangedListAddInterface(CFStringRef ifname)
94 {
95 if (S_if_changes == NULL) {
96 S_if_changes = CFArrayCreateMutable(NULL,
97 0, &kCFTypeArrayCallBacks);
98 CFArrayAppendValue(S_if_changes, ifname);
99 S_if_changes_range.length = 1;
100 }
101 else if (!CFArrayContainsValue(S_if_changes, S_if_changes_range, ifname)) {
102 CFArrayAppendValue(S_if_changes, ifname);
103 S_if_changes_range.length++;
104 }
105 }
106
107 STATIC CFArrayRef
108 InterfaceChangedListCopy(void)
109 {
110 CFArrayRef current_list;
111
112 current_list = S_if_changes;
113 S_if_changes = NULL;
114 return (current_list);
115 }
116
117 STATIC void
118 InterfaceRankAssertionAdd(const void * key, const void * value, void * context)
119 {
120 CFMutableDictionaryRef * assertions_p;
121 CFNumberRef existing_rank;
122 CFNumberRef rank = (CFNumberRef)value;
123
124 assertions_p = (CFMutableDictionaryRef *)context;
125 if (*assertions_p == NULL) {
126 *assertions_p
127 = CFDictionaryCreateMutable(NULL, 0,
128 &kCFTypeDictionaryKeyCallBacks,
129 &kCFTypeDictionaryValueCallBacks);
130 CFDictionarySetValue(*assertions_p, key, rank);
131 return;
132 }
133 existing_rank = CFDictionaryGetValue(*assertions_p, key);
134 if (existing_rank == NULL
135 || (CFNumberCompare(rank, existing_rank, NULL)
136 == kCFCompareGreaterThan)) {
137 CFDictionarySetValue(*assertions_p, key, rank);
138 }
139 return;
140 }
141
142 STATIC void
143 InterfaceAdvisoryAdd(const void * key, const void * value, void * context)
144 {
145 #pragma unused(value)
146 CFMutableDictionaryRef * assertions_p;
147 CFNumberRef existing_rank;
148 CFNumberRef rank;
149
150 /* an interface advisory implies RankLast */
151 rank = RankLastNumberGet();
152 assertions_p = (CFMutableDictionaryRef *)context;
153 if (*assertions_p == NULL) {
154 *assertions_p
155 = CFDictionaryCreateMutable(NULL, 0,
156 &kCFTypeDictionaryKeyCallBacks,
157 &kCFTypeDictionaryValueCallBacks);
158 CFDictionarySetValue(*assertions_p, key, rank);
159 return;
160 }
161 existing_rank = CFDictionaryGetValue(*assertions_p, key);
162 if (existing_rank == NULL
163 || (CFNumberCompare(rank, existing_rank, NULL)
164 == kCFCompareGreaterThan)) {
165 CFDictionarySetValue(*assertions_p, key, rank);
166 }
167 return;
168 }
169
170 STATIC CFDictionaryRef
171 InterfaceRankAssertionsCopy(void)
172 {
173 CFMutableDictionaryRef assertions = NULL;
174 ControlSessionRef session;
175
176 LIST_FOREACH(session, &S_ControlSessions, link) {
177 if (session->advisories != NULL) {
178 CFDictionaryApplyFunction(session->advisories,
179 InterfaceAdvisoryAdd,
180 &assertions);
181 }
182 if (session->assertions != NULL) {
183 CFDictionaryApplyFunction(session->assertions,
184 InterfaceRankAssertionAdd,
185 &assertions);
186 }
187 }
188 return (assertions);
189 }
190
191 STATIC Boolean
192 InterfaceHasAdvisories(CFStringRef ifname)
193 {
194 ControlSessionRef session;
195
196 LIST_FOREACH(session, &S_ControlSessions, link) {
197 if (session->advisories != NULL
198 && CFDictionaryContainsKey(session->advisories, ifname)) {
199 return (TRUE);
200 }
201 }
202 return (FALSE);
203 }
204
205
206 STATIC AWDIPMonitorInterfaceAdvisoryReport_Flags
207 advisory_to_flags(SCNetworkInterfaceAdvisory advisory)
208 {
209 AWDIPMonitorInterfaceAdvisoryReport_Flags flags;
210
211 switch (advisory) {
212 case kSCNetworkInterfaceAdvisoryNone:
213 default:
214 flags = 0;
215 break;
216 case kSCNetworkInterfaceAdvisoryLinkLayerIssue:
217 flags = AWDIPMonitorInterfaceAdvisoryReport_Flags_LINK_LAYER_ISSUE;
218 break;
219 case kSCNetworkInterfaceAdvisoryUplinkIssue:
220 flags = AWDIPMonitorInterfaceAdvisoryReport_Flags_UPLINK_ISSUE;
221 break;
222 }
223 return (flags);
224 }
225
226 STATIC AWDIPMonitorInterfaceAdvisoryReport_Flags
227 InterfaceGetAdvisoryFlags(CFStringRef ifname,
228 ControlSessionRef exclude_session,
229 uint32_t * ret_count)
230 {
231 uint32_t count;
232 AWDIPMonitorInterfaceAdvisoryReport_Flags flags = 0;
233 ControlSessionRef session;
234
235 count = 0;
236 LIST_FOREACH(session, &S_ControlSessions, link) {
237 SCNetworkInterfaceAdvisory advisory = 0;
238 CFNumberRef advisory_cf;
239
240 if (session->advisories == NULL) {
241 continue;
242 }
243 if (exclude_session != NULL && exclude_session == session) {
244 continue;
245 }
246 advisory_cf = CFDictionaryGetValue(session->advisories, ifname);
247 if (advisory_cf == NULL) {
248 /* session has no advisories for this interface */
249 continue;
250 }
251 (void)CFNumberGetValue(advisory_cf, kCFNumberSInt32Type, &advisory);
252 flags |= advisory_to_flags(advisory);
253 count++;
254 }
255 *ret_count = count;
256 return (flags);
257 }
258
259 STATIC Boolean
260 AnyInterfaceHasAdvisories(void)
261 {
262 ControlSessionRef session;
263
264 LIST_FOREACH(session, &S_ControlSessions, link) {
265 if (session->advisories != NULL) {
266 return (TRUE);
267 }
268 }
269 return (FALSE);
270 }
271
272 STATIC CFRunLoopRef S_runloop;
273 STATIC CFRunLoopSourceRef S_signal_source;
274
275 STATIC void
276 SetNotificationInfo(CFRunLoopRef runloop, CFRunLoopSourceRef rls)
277 {
278 S_runloop = runloop;
279 S_signal_source = rls;
280 return;
281 }
282
283 STATIC void
284 NotifyIPMonitor(void)
285 {
286 if (S_signal_source != NULL) {
287 CFRunLoopSourceSignal(S_signal_source);
288 if (S_runloop != NULL) {
289 CFRunLoopWakeUp(S_runloop);
290 }
291 }
292 return;
293 }
294
295 STATIC void
296 NotifyInterfaceAdvisory(CFStringRef ifname)
297 {
298 CFStringRef key;
299
300 key = _IPMonitorControlCopyInterfaceAdvisoryNotificationKey(ifname);
301 SCDynamicStoreNotifyValue(NULL, key);
302 CFRelease(key);
303 return;
304 }
305
306 STATIC void
307 SubmitInterfaceAdvisoryMetric(CFStringRef ifname,
308 AWDIPMonitorInterfaceAdvisoryReport_Flags flags,
309 uint32_t count)
310 {
311 InterfaceAdvisoryReportRef report;
312 AWDIPMonitorInterfaceType type;
313
314 /* XXX need to actually figure out what the interface type is */
315 if (CFStringHasPrefix(ifname, CFSTR("pdp"))) {
316 type = AWDIPMonitorInterfaceType_IPMONITOR_INTERFACE_TYPE_CELLULAR;
317 }
318 else {
319 type = AWDIPMonitorInterfaceType_IPMONITOR_INTERFACE_TYPE_WIFI;
320 }
321 report = InterfaceAdvisoryReportCreate(type);
322 if (report == NULL) {
323 return;
324 }
325 InterfaceAdvisoryReportSetFlags(report, flags);
326 InterfaceAdvisoryReportSetAdvisoryCount(report, count);
327 InterfaceAdvisoryReportSubmit(report);
328 my_log(LOG_NOTICE, "%@: submitted AWD report %@", ifname, report);
329 CFRelease(report);
330 }
331
332 /**
333 ** ControlSession
334 **/
335 STATIC void
336 AddChangedInterface(const void * key, const void * value, void * context)
337 {
338 #pragma unused(value)
339 #pragma unused(context)
340 InterfaceChangedListAddInterface((CFStringRef)key);
341 return;
342 }
343
344 STATIC void
345 AddChangedInterfaceNotify(const void * key, const void * value, void * context)
346 {
347 #pragma unused(value)
348 #pragma unused(context)
349 InterfaceChangedListAddInterface((CFStringRef)key);
350 NotifyInterfaceAdvisory((CFStringRef)key);
351 return;
352 }
353
354 STATIC void
355 GenerateMetricForInterfaceAtSessionClose(const void * key, const void * value,
356 void * context)
357 {
358 uint32_t count_after;
359 uint32_t count_before;
360 AWDIPMonitorInterfaceAdvisoryReport_Flags flags_after;
361 AWDIPMonitorInterfaceAdvisoryReport_Flags flags_before;
362 CFStringRef ifname = (CFStringRef)key;
363 ControlSessionRef session = (ControlSessionRef)context;
364
365 #pragma unused(value)
366 /*
367 * Get the flags and count including this session, then again
368 * excluding this session. If either flags or count are different,
369 * generate the metric.
370 */
371 flags_before = InterfaceGetAdvisoryFlags(ifname, NULL, &count_before);
372 flags_after = InterfaceGetAdvisoryFlags(ifname, session, &count_after);
373 if (flags_before != flags_after || count_before != count_after) {
374 SubmitInterfaceAdvisoryMetric(ifname, flags_after, count_after);
375 }
376 return;
377 }
378
379 STATIC void
380 ControlSessionGenerateMetricsAtClose(ControlSessionRef session)
381 {
382 if (session->advisories == NULL) {
383 return;
384 }
385 CFDictionaryApplyFunction(session->advisories,
386 GenerateMetricForInterfaceAtSessionClose,
387 session);
388 }
389
390 STATIC void
391 ControlSessionInvalidate(ControlSessionRef session)
392 {
393 my_log(LOG_DEBUG, "Invalidating %p", session);
394 ControlSessionGenerateMetricsAtClose(session);
395 LIST_REMOVE(session, link);
396 if (session->assertions != NULL || session->advisories != NULL) {
397 if (session->advisories != NULL) {
398 my_log(LOG_NOTICE,
399 "pid %d removing advisories %@",
400 xpc_connection_get_pid(session->connection),
401 session->advisories);
402 CFDictionaryApplyFunction(session->advisories,
403 AddChangedInterfaceNotify,
404 NULL);
405 my_CFRelease(&session->advisories);
406 }
407 if (session->assertions != NULL) {
408 my_log(LOG_NOTICE,
409 "pid %d removing assertions %@",
410 xpc_connection_get_pid(session->connection),
411 session->assertions);
412 CFDictionaryApplyFunction(session->assertions, AddChangedInterface,
413 NULL);
414 my_CFRelease(&session->assertions);
415 }
416 NotifyIPMonitor();
417 }
418 return;
419 }
420
421 STATIC void
422 ControlSessionRelease(void * p)
423 {
424 my_log(LOG_DEBUG, "Releasing %p", p);
425 free(p);
426 return;
427 }
428
429 STATIC ControlSessionRef
430 ControlSessionLookup(xpc_connection_t connection)
431 {
432 return ((ControlSessionRef)xpc_connection_get_context(connection));
433 }
434
435 STATIC ControlSessionRef
436 ControlSessionCreate(xpc_connection_t connection)
437 {
438 ControlSessionRef session;
439
440 session = (ControlSessionRef)malloc(sizeof(*session));
441 memset(session, 0, sizeof(*session));
442 session->connection = connection;
443 xpc_connection_set_finalizer_f(connection, ControlSessionRelease);
444 xpc_connection_set_context(connection, session);
445 LIST_INSERT_HEAD(&S_ControlSessions, session, link);
446 my_log(LOG_DEBUG, "Created %p (connection %p)", session, connection);
447 return (session);
448 }
449
450 STATIC ControlSessionRef
451 ControlSessionForConnection(xpc_connection_t connection)
452 {
453 ControlSessionRef session;
454
455 session = ControlSessionLookup(connection);
456 if (session != NULL) {
457 return (session);
458 }
459 return (ControlSessionCreate(connection));
460 }
461
462 STATIC void
463 ControlSessionSetInterfaceRank(ControlSessionRef session,
464 const char * ifname,
465 SCNetworkServicePrimaryRank rank)
466 {
467 CFStringRef ifname_cf;
468
469 if (session->assertions == NULL) {
470 if (rank == kSCNetworkServicePrimaryRankDefault) {
471 /* no assertions, no need to store rank */
472 return;
473 }
474 session->assertions
475 = CFDictionaryCreateMutable(NULL, 0,
476 &kCFTypeDictionaryKeyCallBacks,
477 &kCFTypeDictionaryValueCallBacks);
478 }
479 ifname_cf = CFStringCreateWithCString(NULL, ifname,
480 kCFStringEncodingUTF8);
481
482 if (rank == kSCNetworkServicePrimaryRankDefault) {
483 CFDictionaryRemoveValue(session->assertions, ifname_cf);
484 if (CFDictionaryGetCount(session->assertions) == 0) {
485 CFRelease(session->assertions);
486 session->assertions = NULL;
487 }
488 }
489 else {
490 CFNumberRef rank_cf;
491
492 rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank);
493 CFDictionarySetValue(session->assertions, ifname_cf, rank_cf);
494 CFRelease(rank_cf);
495 }
496 InterfaceChangedListAddInterface(ifname_cf);
497 NotifyIPMonitor();
498 CFRelease(ifname_cf);
499 return;
500 }
501
502 STATIC SCNetworkServicePrimaryRank
503 ControlSessionGetInterfaceRank(ControlSessionRef session,
504 const char * ifname)
505 {
506 SCNetworkServicePrimaryRank rank = kSCNetworkServicePrimaryRankDefault;
507
508 if (session->assertions != NULL) {
509 CFStringRef ifname_cf;
510 CFNumberRef rank_cf;
511
512 ifname_cf = CFStringCreateWithCString(NULL, ifname,
513 kCFStringEncodingUTF8);
514 rank_cf = CFDictionaryGetValue(session->assertions, ifname_cf);
515 CFRelease(ifname_cf);
516 if (rank_cf != NULL) {
517 (void)CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &rank);
518 }
519 }
520 return (rank);
521 }
522
523 STATIC void
524 ControlSessionSetInterfaceAdvisory(ControlSessionRef session,
525 const char * ifname,
526 SCNetworkInterfaceAdvisory advisory)
527 {
528 uint32_t count_after;
529 uint32_t count_before;
530 AWDIPMonitorInterfaceAdvisoryReport_Flags flags_after;
531 AWDIPMonitorInterfaceAdvisoryReport_Flags flags_before;
532 CFStringRef ifname_cf;
533
534 if (session->advisories == NULL) {
535 if (advisory == kSCNetworkInterfaceAdvisoryNone) {
536 /* no advisories, no need to store advisory */
537 return;
538 }
539 session->advisories
540 = CFDictionaryCreateMutable(NULL, 0,
541 &kCFTypeDictionaryKeyCallBacks,
542 &kCFTypeDictionaryValueCallBacks);
543 }
544 ifname_cf = CFStringCreateWithCString(NULL, ifname,
545 kCFStringEncodingUTF8);
546 flags_before = InterfaceGetAdvisoryFlags(ifname_cf, NULL, &count_before);
547 if (advisory == kSCNetworkInterfaceAdvisoryNone) {
548 CFDictionaryRemoveValue(session->advisories, ifname_cf);
549 if (CFDictionaryGetCount(session->advisories) == 0) {
550 CFRelease(session->advisories);
551 session->advisories = NULL;
552 }
553 }
554 else {
555 CFNumberRef advisory_cf;
556
557 advisory_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &advisory);
558 CFDictionarySetValue(session->advisories, ifname_cf, advisory_cf);
559 CFRelease(advisory_cf);
560 }
561 flags_after = InterfaceGetAdvisoryFlags(ifname_cf, NULL, &count_after);
562 if (flags_before != flags_after || count_before != count_after) {
563 SubmitInterfaceAdvisoryMetric(ifname_cf, flags_after, count_after);
564 }
565 InterfaceChangedListAddInterface(ifname_cf);
566 NotifyInterfaceAdvisory(ifname_cf);
567 NotifyIPMonitor();
568 CFRelease(ifname_cf);
569 return;
570 }
571
572 /**
573 ** IPMonitorControlServer
574 **/
575
576 STATIC const char *
577 get_process_name(xpc_object_t request)
578 {
579 const char * process_name;
580
581 process_name
582 = xpc_dictionary_get_string(request,
583 kIPMonitorControlRequestKeyProcessName);
584 if (process_name == NULL) {
585 process_name = "<unknown>";
586 }
587 return (process_name);
588 }
589
590 STATIC Boolean
591 IPMonitorControlServerConnectionIsRoot(xpc_connection_t connection)
592 {
593 uid_t uid;
594
595 uid = xpc_connection_get_euid(connection);
596 return (uid == 0);
597 }
598
599 STATIC Boolean
600 IPMonitorControlServerConnectionHasEntitlement(xpc_connection_t connection,
601 const char * entitlement)
602 {
603 Boolean entitled = FALSE;
604 xpc_object_t val;
605
606 val = xpc_connection_copy_entitlement_value(connection, entitlement);
607 if (val != NULL) {
608 if (xpc_get_type(val) == XPC_TYPE_BOOL) {
609 entitled = xpc_bool_get_value(val);
610 }
611 xpc_release(val);
612 }
613 return (entitled);
614 }
615
616 STATIC const char *
617 get_rank_str(SCNetworkServicePrimaryRank rank)
618 {
619 const char * str = NULL;
620
621 switch (rank) {
622 case kSCNetworkServicePrimaryRankDefault:
623 str = "Default";
624 break;
625 case kSCNetworkServicePrimaryRankFirst:
626 str = "First";
627 break;
628 case kSCNetworkServicePrimaryRankLast:
629 str = "Last";
630 break;
631 case kSCNetworkServicePrimaryRankNever:
632 str = "Never";
633 break;
634 case kSCNetworkServicePrimaryRankScoped:
635 str = "Scoped";
636 break;
637 default:
638 break;
639 }
640 return (str);
641 }
642
643 STATIC int
644 HandleSetInterfaceRank(xpc_connection_t connection,
645 xpc_object_t request,
646 xpc_object_t reply)
647 {
648 #pragma unused(reply)
649 const char * ifname;
650 SCNetworkServicePrimaryRank rank;
651 const char * rank_str;
652 ControlSessionRef session;
653
654 if (!IPMonitorControlServerConnectionIsRoot(connection)) {
655 my_log(LOG_INFO, "connection %p pid %d permission denied",
656 connection, xpc_connection_get_pid(connection));
657 return (EPERM);
658 }
659 ifname
660 = xpc_dictionary_get_string(request,
661 kIPMonitorControlRequestKeyInterfaceName);
662 if (ifname == NULL) {
663 return (EINVAL);
664 }
665 rank = (SCNetworkServicePrimaryRank)
666 xpc_dictionary_get_uint64(request,
667 kIPMonitorControlRequestKeyPrimaryRank);
668 rank_str = get_rank_str(rank);
669 if (rank_str == NULL) {
670 return (EINVAL);
671 }
672 session = ControlSessionForConnection(connection);
673 ControlSessionSetInterfaceRank(session, ifname, rank);
674 my_log(LOG_NOTICE, "%s[%d] SetInterfaceRank(%s) = %s (%u)",
675 get_process_name(request),
676 xpc_connection_get_pid(connection), ifname, rank_str, rank);
677 return (0);
678 }
679
680 STATIC int
681 HandleGetInterfaceRank(xpc_connection_t connection,
682 xpc_object_t request,
683 xpc_object_t reply)
684 {
685 const char * ifname;
686 SCNetworkServicePrimaryRank rank;
687 ControlSessionRef session;
688
689 if (reply == NULL) {
690 /* no point in processing the request if we can't provide an answer */
691 return (EINVAL);
692 }
693 session = ControlSessionLookup(connection);
694 if (session == NULL) {
695 /* no session, no rank assertion */
696 return (ENOENT);
697 }
698 ifname
699 = xpc_dictionary_get_string(request,
700 kIPMonitorControlRequestKeyInterfaceName);
701 if (ifname == NULL) {
702 return (EINVAL);
703 }
704 rank = ControlSessionGetInterfaceRank(session, ifname);
705 xpc_dictionary_set_uint64(reply, kIPMonitorControlResponseKeyPrimaryRank,
706 rank);
707 return (0);
708 }
709
710 STATIC const char *
711 get_advisory_str(SCNetworkInterfaceAdvisory advisory)
712 {
713 const char * str = NULL;
714
715 switch (advisory) {
716 case kSCNetworkInterfaceAdvisoryNone:
717 str = "None";
718 break;
719 case kSCNetworkInterfaceAdvisoryLinkLayerIssue:
720 str = "LinkLayerIssue";
721 break;
722 case kSCNetworkInterfaceAdvisoryUplinkIssue:
723 str = "UplinkIssue";
724 break;
725 default:
726 break;
727 }
728 return (str);
729 }
730
731 STATIC int
732 HandleSetInterfaceAdvisory(xpc_connection_t connection,
733 xpc_object_t request,
734 xpc_object_t reply)
735 {
736 #pragma unused(reply)
737 SCNetworkInterfaceAdvisory advisory;
738 const char * advisory_str;
739 const char * ifname;
740 const char * reason;
741 ControlSessionRef session;
742
743 #define ENTITLEMENT "com.apple.SystemConfiguration.SCNetworkInterfaceSetAdvisory"
744 if (!IPMonitorControlServerConnectionIsRoot(connection)
745 && !IPMonitorControlServerConnectionHasEntitlement(connection,
746 ENTITLEMENT)) {
747 my_log(LOG_INFO, "connection %p pid %d permission denied",
748 connection, xpc_connection_get_pid(connection));
749 return (EPERM);
750 }
751 ifname
752 = xpc_dictionary_get_string(request,
753 kIPMonitorControlRequestKeyInterfaceName);
754 if (ifname == NULL) {
755 return (EINVAL);
756 }
757 reason
758 = xpc_dictionary_get_string(request,
759 kIPMonitorControlRequestKeyReason);
760 advisory = (SCNetworkInterfaceAdvisory)
761 xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyAdvisory);
762
763 /* validate the advisory code */
764 advisory_str = get_advisory_str(advisory);
765 if (advisory_str == NULL) {
766 return (EINVAL);
767 }
768 session = ControlSessionForConnection(connection);
769 ControlSessionSetInterfaceAdvisory(session, ifname, advisory);
770 my_log(LOG_NOTICE, "%s[%d] SetInterfaceAdvisory(%s) = %s (%u) reason='%s'",
771 get_process_name(request),
772 xpc_connection_get_pid(connection), ifname, advisory_str, advisory,
773 reason != NULL ? reason : "" );
774 return (0);
775 }
776
777 STATIC int
778 HandleInterfaceAdvisoryIsSet(xpc_connection_t connection,
779 xpc_object_t request,
780 xpc_object_t reply)
781 {
782 #pragma unused(connection)
783 const char * ifname;
784 CFStringRef ifname_cf;
785
786 if (reply == NULL) {
787 /* no point in processing the request if we can't provide an answer */
788 return (EINVAL);
789 }
790 ifname
791 = xpc_dictionary_get_string(request,
792 kIPMonitorControlRequestKeyInterfaceName);
793 if (ifname == NULL) {
794 return (EINVAL);
795 }
796 ifname_cf = CFStringCreateWithCString(NULL, ifname,
797 kCFStringEncodingUTF8);
798 xpc_dictionary_set_bool(reply,
799 kIPMonitorControlResponseKeyAdvisoryIsSet,
800 InterfaceHasAdvisories(ifname_cf));
801 CFRelease(ifname_cf);
802 return (0);
803 }
804
805 STATIC int
806 HandleAnyInterfaceAdvisoryIsSet(xpc_connection_t connection,
807 xpc_object_t request,
808 xpc_object_t reply)
809 {
810 #pragma unused(connection)
811 #pragma unused(request)
812 if (reply == NULL) {
813 /* no point in processing the request if we can't provide an answer */
814 return (EINVAL);
815 }
816 xpc_dictionary_set_bool(reply,
817 kIPMonitorControlResponseKeyAdvisoryIsSet,
818 AnyInterfaceHasAdvisories());
819 return (0);
820 }
821
822 STATIC void
823 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection)
824 {
825 ControlSessionRef session;
826
827 my_log(LOG_DEBUG, "IPMonitorControlServer: client %p went away",
828 connection);
829 session = ControlSessionLookup(connection);
830 if (session == NULL) {
831 /* never asserted anything */
832 return;
833 }
834 ControlSessionInvalidate(session);
835 return;
836 }
837
838 STATIC void
839 IPMonitorControlServerHandleRequest(xpc_connection_t connection,
840 xpc_object_t request)
841 {
842 xpc_type_t type;
843
844 type = xpc_get_type(request);
845 if (type == XPC_TYPE_DICTIONARY) {
846 int error = 0;
847 uint64_t request_type;
848 xpc_connection_t remote;
849 xpc_object_t reply;
850
851 request_type
852 = xpc_dictionary_get_uint64(request,
853 kIPMonitorControlRequestKeyType);
854 reply = xpc_dictionary_create_reply(request);
855 switch (request_type) {
856 case kIPMonitorControlRequestTypeSetInterfaceRank:
857 error = HandleSetInterfaceRank(connection, request, reply);
858 break;
859 case kIPMonitorControlRequestTypeGetInterfaceRank:
860 error = HandleGetInterfaceRank(connection, request, reply);
861 break;
862 case kIPMonitorControlRequestTypeSetInterfaceAdvisory:
863 error = HandleSetInterfaceAdvisory(connection, request, reply);
864 break;
865 case kIPMonitorControlRequestTypeInterfaceAdvisoryIsSet:
866 error = HandleInterfaceAdvisoryIsSet(connection, request, reply);
867 break;
868 case kIPMonitorControlRequestTypeAnyInterfaceAdvisoryIsSet:
869 error = HandleAnyInterfaceAdvisoryIsSet(connection, request, reply);
870 break;
871 default:
872 error = EINVAL;
873 break;
874 }
875 if (reply == NULL) {
876 /* client didn't want a reply */
877 return;
878 }
879 xpc_dictionary_set_int64(reply, kIPMonitorControlResponseKeyError,
880 error);
881 remote = xpc_dictionary_get_remote_connection(request);
882 xpc_connection_send_message(remote, reply);
883 xpc_release(reply);
884 }
885 else if (type == XPC_TYPE_ERROR) {
886 if (request == XPC_ERROR_CONNECTION_INVALID) {
887 IPMonitorControlServerHandleDisconnect(connection);
888 }
889 else if (request == XPC_ERROR_CONNECTION_INTERRUPTED) {
890 my_log(LOG_INFO, "connection interrupted");
891 }
892 }
893 else {
894 my_log(LOG_NOTICE, "unexpected event");
895 }
896 return;
897 }
898
899 STATIC void
900 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection)
901 {
902 xpc_handler_t handler;
903
904 handler = ^(xpc_object_t event) {
905 IPMonitorControlServerHandleRequest(connection, event);
906 };
907 xpc_connection_set_event_handler(connection, handler);
908 xpc_connection_set_target_queue(connection, S_IPMonitorControlServerQueue);
909 xpc_connection_resume(connection);
910 return;
911 }
912
913 STATIC xpc_connection_t
914 IPMonitorControlServerCreate(dispatch_queue_t queue, const char * name)
915 {
916 uint64_t flags = XPC_CONNECTION_MACH_SERVICE_LISTENER;
917 xpc_connection_t connection;
918 xpc_handler_t handler;
919
920 connection = xpc_connection_create_mach_service(name, queue, flags);
921 if (connection == NULL) {
922 return (NULL);
923 }
924 handler = ^(xpc_object_t event) {
925 xpc_type_t type;
926
927 type = xpc_get_type(event);
928 if (type == XPC_TYPE_CONNECTION) {
929 IPMonitorControlServerHandleNewConnection(event);
930 }
931 else if (type == XPC_TYPE_ERROR) {
932 const char * desc;
933
934 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
935 if (event == XPC_ERROR_CONNECTION_INVALID) {
936 my_log(LOG_NOTICE, "%s", desc);
937 xpc_release(connection);
938 }
939 else {
940 my_log(LOG_NOTICE, "%s", desc);
941 }
942 }
943 else {
944 my_log(LOG_NOTICE, "unknown event %p", type);
945 }
946 };
947 S_IPMonitorControlServerQueue = queue;
948 xpc_connection_set_event_handler(connection, handler);
949 xpc_connection_resume(connection);
950 return (connection);
951 }
952
953 PRIVATE_EXTERN Boolean
954 IPMonitorControlServerStart(CFRunLoopRef runloop, CFRunLoopSourceRef rls,
955 Boolean * verbose)
956 {
957 #pragma unused(verbose)
958 dispatch_queue_t q;
959 xpc_connection_t connection;
960
961 SetNotificationInfo(runloop, rls);
962 q = dispatch_queue_create("IPMonitorControlServer", NULL);
963 connection = IPMonitorControlServerCreate(q, kIPMonitorControlServerName);
964 if (connection == NULL) {
965 my_log(LOG_ERR,
966 "IPMonitorControlServer: failed to create server");
967 dispatch_release(q);
968 return (FALSE);
969 }
970 return (TRUE);
971 }
972
973 PRIVATE_EXTERN CFArrayRef
974 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef * info)
975 {
976 __block CFArrayRef changed;
977 __block CFDictionaryRef dict;
978
979 dispatch_sync(S_IPMonitorControlServerQueue,
980 ^{
981 dict = InterfaceRankAssertionsCopy();
982 changed = InterfaceChangedListCopy();
983 });
984 *info = dict;
985 return (changed);
986 }