2 * Copyright (c) 2000-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 * Modification History
27 * December 3, 2002 Dieter Siegmund <dieter@apple.com>
28 * - handle the new KEV_INET_ARPCOLLISION event
29 * - format the event into a DynamicStore key
30 * State:/Network/Interface/ifname/IPv4Collision/ip_addr/hw_addr
31 * and send a notification on the key
33 * August 8, 2002 Allan Nathanson <ajn@apple.com>
34 * - added support for KEV_INET6_xxx events
36 * January 6, 2002 Jessica Vazquez <vazquez@apple.com>
37 * - added handling for KEV_ATALK_xxx events
39 * July 2, 2001 Dieter Siegmund <dieter@apple.com>
40 * - added handling for KEV_DL_PROTO_{ATTACHED, DETACHED}
41 * - mark an interface up if the number of protocols remaining is not 0,
42 * mark an interface down if the number is zero
43 * - allocate socket on demand instead of keeping it open all the time
45 * June 23, 2001 Allan Nathanson <ajn@apple.com>
46 * - update to public SystemConfiguration.framework APIs
48 * May 17, 2001 Allan Nathanson <ajn@apple.com>
49 * - add/maintain per-interface address/netmask/destaddr information
50 * in the dynamic store.
52 * June 30, 2000 Allan Nathanson <ajn@apple.com>
62 #include <sys/sysctl.h>
63 #include <sys/kern_event.h>
66 #define KEV_LOG_SUBCLASS 10
68 static dispatch_queue_t S_kev_queue
;
69 static dispatch_source_t S_kev_source
;
70 __private_extern__ Boolean network_changed
= FALSE
;
71 __private_extern__ SCDynamicStoreRef store
= NULL
;
72 __private_extern__ Boolean _verbose
= FALSE
;
75 #define MESSAGES_MAX 100
76 static CFMutableArrayRef S_messages
;
77 static Boolean S_messages_modified
;
82 S_messages
= CFArrayCreateMutable(NULL
,
84 &kCFTypeArrayCallBacks
);
91 if (S_messages
!= NULL
) {
92 CFRelease(S_messages
);
99 messages_should_add_message(void)
101 if (S_messages
== NULL
102 || CFArrayGetCount(S_messages
) >= MESSAGES_MAX
) {
109 messages_add_message(CFStringRef message
)
111 if (messages_should_add_message()) {
112 CFArrayAppendValue(S_messages
, message
);
113 S_messages_modified
= TRUE
;
118 __private_extern__
void
119 messages_add_msg_with_arg(const char * msg
, const char * arg
)
121 if (messages_should_add_message()) {
124 str
= CFStringCreateWithFormat(NULL
, NULL
,
125 CFSTR("%12.8f: %s %s"),
126 CFAbsoluteTimeGetCurrent(),
128 messages_add_message(str
);
137 if (S_messages
!= NULL
&& S_messages_modified
) {
138 SCDynamicStoreSetValue(NULL
,
139 CFSTR("Plugin:KernelEventMonitor"),
141 S_messages_modified
= FALSE
;
147 check_interface_link_status(const char * if_name
)
149 if (S_messages
== NULL
) {
150 return; /* we're not in early boot of system */
152 link_update_status_if_missing(if_name
);
158 dgram_socket(int domain
)
162 s
= socket(domain
, SOCK_DGRAM
, 0);
164 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
171 ifflags_set(int s
, char * name
, short flags
)
176 bzero(&ifr
, sizeof(ifr
));
177 strlcpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
178 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
182 ifr
.ifr_flags
|= flags
;
183 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
187 ifflags_clear(int s
, char * name
, short flags
)
192 bzero(&ifr
, sizeof(ifr
));
193 strlcpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
194 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
198 ifr
.ifr_flags
&= ~flags
;
199 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
203 mark_if_up(char * name
)
205 int s
= dgram_socket(AF_INET
);
208 ifflags_set(s
, name
, IFF_UP
);
213 mark_if_down(char * name
)
215 int s
= dgram_socket(AF_INET
);
218 ifflags_clear(s
, name
, IFF_UP
);
223 post_network_changed(void)
225 if (network_changed
) {
228 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
229 if (status
!= NOTIFY_STATUS_OK
) {
230 SC_log(LOG_NOTICE
, "notify_post() failed: error=%u", status
);
233 network_changed
= FALSE
;
240 logEvent(CFStringRef evStr
, struct kern_event_msg
*ev_msg
)
249 SC_log(LOG_DEBUG
, "%@ event:", evStr
);
250 SC_log(LOG_DEBUG
, " Event size=%d, id=%d, vendor=%d, class=%d, subclass=%d, code=%d",
255 ev_msg
->kev_subclass
,
257 for (i
= 0, j
= KEV_MSG_HEADER_SIZE
; j
< ev_msg
->total_size
; i
++, j
+=4) {
258 SC_log(LOG_DEBUG
, " Event data[%2d] = %08x", i
, ev_msg
->event_data
[i
]);
263 copy_if_name(const struct net_event_data
* ev
, char * ifr_name
, int ifr_len
)
265 snprintf(ifr_name
, ifr_len
, "%s%d", ev
->if_name
, ev
->if_unit
);
269 static uint8_t info_zero
[DLIL_MODARGLEN
];
272 processEvent_Apple_Network(struct kern_event_msg
*ev_msg
)
274 int dataLen
= (ev_msg
->total_size
- KEV_MSG_HEADER_SIZE
);
275 void * event_data
= &ev_msg
->event_data
[0];
276 Boolean handled
= TRUE
;
277 char ifr_name
[IFNAMSIZ
];
279 switch (ev_msg
->kev_subclass
) {
280 case KEV_INET_SUBCLASS
: {
281 switch (ev_msg
->event_code
) {
282 case KEV_INET_NEW_ADDR
:
283 case KEV_INET_CHANGED_ADDR
:
284 case KEV_INET_ADDR_DELETED
:
285 case KEV_INET_SIFDSTADDR
:
286 case KEV_INET_SIFBRDADDR
:
287 case KEV_INET_SIFNETMASK
: {
288 struct kev_in_data
* ev
;
290 ev
= (struct kev_in_data
*)event_data
;
291 if (dataLen
< sizeof(*ev
)) {
295 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
296 SC_log(LOG_INFO
, "Process IPv4 address change: %s: %d", (char *)ifr_name
, ev_msg
->event_code
);
297 ipv4_interface_update(NULL
, ifr_name
);
298 if (ev_msg
->event_code
299 != KEV_INET_ADDR_DELETED
) {
300 check_interface_link_status(ifr_name
);
304 case KEV_INET_ARPCOLLISION
: {
305 struct kev_in_collision
* ev
;
307 ev
= (struct kev_in_collision
*)event_data
;
308 if ((dataLen
< sizeof(*ev
))
309 || (dataLen
< (sizeof(*ev
) + ev
->hw_len
))) {
313 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
314 SC_log(LOG_INFO
, "Process ARP collision: %s", (char *)ifr_name
);
315 ipv4_arp_collision(ifr_name
,
321 #if !TARGET_OS_IPHONE
322 case KEV_INET_PORTINUSE
: {
323 struct kev_in_portinuse
* ev
;
324 ev
= (struct kev_in_portinuse
*)event_data
;
325 if (dataLen
< sizeof(*ev
)) {
329 SC_log(LOG_INFO
, "Process port-in-use: %hu, %u", ev
->port
, ev
->req_pid
);
330 ipv4_port_in_use(ev
->port
, ev
->req_pid
);
333 #endif /* !TARGET_OS_IPHONE */
334 case KEV_INET_ARPRTRFAILURE
: {
335 const struct kev_in_arpfailure
* ev
;
337 ev
= (const struct kev_in_arpfailure
*)event_data
;
338 if (dataLen
< sizeof(*ev
)) {
342 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
343 SC_log(LOG_INFO
, "Process router ARP failure: %s", (char *)ifr_name
);
344 ipv4_router_arp_failure(ifr_name
);
347 case KEV_INET_ARPRTRALIVE
: {
348 const struct kev_in_arpalive
* ev
;
350 ev
= (const struct kev_in_arpalive
*)event_data
;
351 if (dataLen
< sizeof(*ev
)) {
355 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
356 SC_log(LOG_INFO
, "Process router ARP alive: %s", (char *)ifr_name
);
357 ipv4_router_arp_alive(ifr_name
);
366 case KEV_INET6_SUBCLASS
: {
367 struct kev_in6_data
* ev
;
369 ev
= (struct kev_in6_data
*)event_data
;
370 switch (ev_msg
->event_code
) {
371 case KEV_INET6_NEW_USER_ADDR
:
372 case KEV_INET6_CHANGED_ADDR
:
373 case KEV_INET6_ADDR_DELETED
:
374 case KEV_INET6_NEW_LL_ADDR
:
375 case KEV_INET6_NEW_RTADV_ADDR
:
376 case KEV_INET6_DEFROUTER
:
377 if (dataLen
< sizeof(*ev
)) {
381 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
382 SC_log(LOG_INFO
, "Process IPv6 address change: %s: %d", (char *)ifr_name
, ev_msg
->event_code
);
383 interface_update_ipv6(NULL
, ifr_name
);
384 if (ev_msg
->event_code
== KEV_INET6_NEW_USER_ADDR
385 && (ev
->ia6_flags
& IN6_IFF_DUPLICATED
) != 0) {
386 ipv6_duplicated_address(ifr_name
,
387 &ev
->ia_addr
.sin6_addr
,
391 if (ev_msg
->event_code
392 != KEV_INET6_ADDR_DELETED
) {
393 check_interface_link_status(ifr_name
);
403 case KEV_DL_SUBCLASS
: {
404 struct net_event_data
* ev
;
406 ev
= (struct net_event_data
*)event_data
;
407 switch (ev_msg
->event_code
) {
408 case KEV_DL_IF_ATTACHED
:
410 * new interface added
412 if (dataLen
< sizeof(*ev
)) {
416 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
417 SC_log(LOG_INFO
, "Process interface attach: %s", (char *)ifr_name
);
421 case KEV_DL_IF_DETACHED
:
425 if (dataLen
< sizeof(*ev
)) {
429 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
430 SC_log(LOG_INFO
, "Process interface detach: %s", (char *)ifr_name
);
431 link_remove(ifr_name
);
434 case KEV_DL_IF_DETACHING
:
436 * interface detaching
438 if (dataLen
< sizeof(*ev
)) {
442 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
443 SC_log(LOG_INFO
, "Process interface detaching: %s", (char *)ifr_name
);
444 interface_detaching(ifr_name
);
447 case KEV_DL_PROTO_ATTACHED
:
448 case KEV_DL_PROTO_DETACHED
: {
449 struct kev_dl_proto_data
* protoEvent
;
451 protoEvent
= (struct kev_dl_proto_data
*)event_data
;
452 if (dataLen
< sizeof(*protoEvent
)) {
456 copy_if_name(&protoEvent
->link_data
,
457 ifr_name
, sizeof(ifr_name
));
458 SC_log(LOG_INFO
, "Process protocol %s: %s (n=%d)",
459 (ev_msg
->event_code
== KEV_DL_PROTO_ATTACHED
) ? "attach" : "detach",
461 protoEvent
->proto_remaining_count
);
462 if (protoEvent
->proto_remaining_count
== 0) {
463 mark_if_down(ifr_name
);
465 mark_if_up(ifr_name
);
470 #ifdef KEV_DL_IF_IDLE_ROUTE_REFCNT
471 case KEV_DL_IF_IDLE_ROUTE_REFCNT
: {
473 * interface route refcnt idle
475 if (dataLen
< sizeof(*ev
)) {
479 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
480 SC_log(LOG_INFO
, "Process interface idle: %s", (char *)ifr_name
);
481 interface_update_idle_state(ifr_name
);
484 #endif // KEV_DL_IF_IDLE_ROUTE_REFCNT
486 case KEV_DL_LINK_OFF
:
487 case KEV_DL_LINK_ON
:
489 * update the link status in the store
491 if (dataLen
< sizeof(*ev
)) {
495 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
496 SC_log(LOG_INFO
, "Process interface link %s: %s",
497 (ev_msg
->event_code
== KEV_DL_LINK_ON
) ? "up" : "down",
499 link_update_status(ifr_name
, FALSE
, FALSE
);
502 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
503 case KEV_DL_LINK_QUALITY_METRIC_CHANGED
: {
504 struct kev_dl_link_quality_metric_data
* lqm_data
;
505 lqm_data
= (struct kev_dl_link_quality_metric_data
*) event_data
;
507 if (dataLen
< sizeof(*ev
)) {
511 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
512 SC_log(LOG_INFO
, "Process interface quality: %s (q=%d)",
514 lqm_data
->link_quality_metric
);
515 interface_update_quality_metric(ifr_name
,
516 lqm_data
->link_quality_metric
);
519 #endif // KEV_DL_LINK_QUALITY_METRIC_CHANGED
522 case KEV_DL_ISSUES
: {
523 struct kev_dl_issues
*issues
;
525 issues
= (struct kev_dl_issues
*)event_data
;
526 if (dataLen
< sizeof(*ev
)) {
530 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
531 SC_log(LOG_INFO
, "Process interface link issues: %s",
533 interface_update_link_issues(ifr_name
,
538 (bcmp(issues
->info
, info_zero
, DLIL_MODIDLEN
) != 0)
543 #endif // KEV_DL_ISSUES
545 case KEV_DL_SIFFLAGS
:
546 case KEV_DL_SIFMETRICS
:
548 case KEV_DL_SIFPHYS
:
549 case KEV_DL_SIFMEDIA
:
550 case KEV_DL_SIFGENERIC
:
551 case KEV_DL_ADDMULTI
:
552 case KEV_DL_DELMULTI
:
553 case KEV_DL_LINK_ADDRESS_CHANGED
:
554 case KEV_DL_WAKEFLAGS_CHANGED
:
555 #ifdef KEV_DL_IFCAP_CHANGED
556 case KEV_DL_IFCAP_CHANGED
:
557 #endif // KEV_DL_IFCAP_CHANGED
566 #ifdef KEV_ND6_SUBCLASS
567 case KEV_ND6_SUBCLASS
: {
568 switch (ev_msg
->event_code
) {
569 case KEV_KEV_ND6_RA
:
578 #endif // KEV_ND6_SUBCLASS
579 case KEV_LOG_SUBCLASS
: {
588 logEvent(CFSTR("New Apple network subclass"), ev_msg
);
594 eventCallback(int so
)
599 struct kern_event_msg ev_msg1
; // first kernel event
601 struct kern_event_msg
*ev_msg
= &buf
.ev_msg1
;
604 status
= recv(so
, &buf
, sizeof(buf
), 0);
606 SC_log(LOG_NOTICE
, "recv() failed: %s", strerror(errno
));
612 while (offset
< status
) {
613 if ((offset
+ ev_msg
->total_size
) > status
) {
614 SC_log(LOG_NOTICE
, "missed SYSPROTO_EVENT event, buffer not big enough");
618 switch (ev_msg
->vendor_code
) {
619 case KEV_VENDOR_APPLE
:
620 switch (ev_msg
->kev_class
) {
621 case KEV_NETWORK_CLASS
:
622 processEvent_Apple_Network(ev_msg
);
624 case KEV_IOKIT_CLASS
:
625 case KEV_SYSTEM_CLASS
:
626 case KEV_APPLESHARE_CLASS
:
627 case KEV_FIREWALL_CLASS
:
628 case KEV_IEEE80211_CLASS
:
631 /* unrecognized (Apple) event class */
632 logEvent(CFSTR("New (Apple) class"), ev_msg
);
637 /* unrecognized vendor code */
638 logEvent(CFSTR("New vendor"), ev_msg
);
641 offset
+= ev_msg
->total_size
;
642 ev_msg
= (struct kern_event_msg
*)(void *)&buf
.bytes
[offset
];
647 post_network_changed();
655 update_interfaces(const char * msg
, Boolean ipv4_ipv6_too
)
657 Boolean added
= FALSE
;
658 struct ifaddrs
* ifap
= NULL
;
659 CFMutableArrayRef ifList
= NULL
;
660 struct ifaddrs
* scan
;
662 if (getifaddrs(&ifap
) == -1) {
663 messages_add_msg_with_arg("getifaddrs", strerror(errno
));
664 SC_log(LOG_NOTICE
, "getifaddrs() failed: %s", strerror(errno
));
668 /* update list of interfaces & link status */
669 ifList
= interfaceListCopy();
670 for (scan
= ifap
; scan
!= NULL
; scan
= scan
->ifa_next
) {
671 if (scan
->ifa_addr
== NULL
672 || scan
->ifa_addr
->sa_family
!= AF_LINK
) {
675 /* get the per-interface link/media information */
676 if (interfaceListAddInterface(ifList
, scan
->ifa_name
)) {
677 messages_add_msg_with_arg(msg
, scan
->ifa_name
);
682 /* update the global list if an interface was added */
684 interfaceListUpdate(ifList
);
688 /* update IPv4/IPv6 addresses that are already assigned */
690 ipv4_interface_update(ifap
, NULL
);
691 interface_update_ipv6(ifap
, NULL
);
700 #define TIMER_INTERVAL (6LL * NSEC_PER_SEC)
701 #define MAX_TIMER_COUNT 20
704 check_for_new_interfaces(void * context
);
709 dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW
, TIMER_INTERVAL
),
712 check_for_new_interfaces
);
717 kernel_events_lost(void)
719 boolean_t events_lost
= FALSE
;
720 struct kevtstat kevtstat
;
721 size_t len
= sizeof(kevtstat
);
722 const char * mibvar
= "net.systm.kevt.stats";
723 static u_int64_t old_kes_nomem
;
725 if (sysctlbyname(mibvar
, &kevtstat
, &len
, 0, 0) < 0) {
726 SC_log(LOG_NOTICE
, "sysctl(%s) failed, %s",
727 mibvar
, strerror(errno
));
729 else if (old_kes_nomem
!= kevtstat
.kes_nomem
) {
730 SC_log(LOG_NOTICE
, "KernelEventMonitor: lost kernel event");
731 old_kes_nomem
= kevtstat
.kes_nomem
;
734 return (events_lost
);
738 check_for_new_interfaces(void * context
)
743 if (kernel_events_lost()) {
746 snprintf(msg
, sizeof(msg
), "timeout %d (of %d)", count
, MAX_TIMER_COUNT
);
748 update_interfaces(msg
, FALSE
);
754 /* schedule the next timer, if needed */
755 if (count
< MAX_TIMER_COUNT
) {
768 SC_log(LOG_DEBUG
, "prime() called");
772 update_interfaces("prime", TRUE
);
776 network_changed
= TRUE
;
777 post_network_changed();
780 /* start handling kernel events */
781 dispatch_resume(S_kev_source
);
783 /* schedule polling timer */
792 prime_KernelEventMonitor()
794 dispatch_async(S_kev_queue
, ^{ prime(); });
799 initialize_store(void)
801 store
= SCDynamicStoreCreate(NULL
,
802 CFSTR("Kernel Event Monitor plug-in"),
806 SC_log(LOG_ERR
, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError()));
815 load_KernelEventMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
817 struct kev_request kev_req
;
825 SC_log(LOG_DEBUG
, "load() called");
826 SC_log(LOG_DEBUG
, " bundle ID = %@", CFBundleGetIdentifier(bundle
));
828 if (!initialize_store()) {
829 SC_log(LOG_ERR
, "kernel event monitor disabled");
833 /* Open an event socket */
834 so
= socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
);
836 /* establish filter to return events of interest */
837 kev_req
.vendor_code
= KEV_VENDOR_APPLE
;
838 kev_req
.kev_class
= KEV_NETWORK_CLASS
;
839 kev_req
.kev_subclass
= KEV_ANY_SUBCLASS
;
840 status
= ioctl(so
, SIOCSKEVFILT
, &kev_req
);
842 SC_log(LOG_ERR
, "could not establish event filter, ioctl() failed: %s", strerror(errno
));
847 SC_log(LOG_ERR
, "could not open event socket, socket() failed: %s", strerror(errno
));
853 status
= ioctl(so
, FIONBIO
, &yes
);
855 SC_log(LOG_ERR
, "could not set non-blocking io, ioctl() failed: %s", strerror(errno
));
862 SC_log(LOG_ERR
, "kernel event monitor disabled");
867 S_kev_queue
= dispatch_queue_create("com.apple.SystemConfiguration.KernelEventMonitor", NULL
);
869 = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, so
, 0, S_kev_queue
);
870 dispatch_source_set_cancel_handler(S_kev_source
, ^{
873 dispatch_source_set_event_handler(S_kev_source
, ^{
874 os_activity_t activity_id
;
877 activity_id
= os_activity_start("processing network kernel events",
878 OS_ACTIVITY_FLAG_DEFAULT
);
880 ok
= eventCallback(so
);
882 SC_log(LOG_ERR
, "kernel event monitor disabled");
883 dispatch_source_cancel(S_kev_source
);
886 os_activity_end(activity_id
);
888 // NOTE: dispatch_resume() will be called in prime()
897 #define appendAddress appendAddress_v4
898 #define getIF getIF_v4
899 #define updateStore updateStore_v4
905 #define appendAddress appendAddress_v6
906 #define getIF getIF_v6
907 #define updateStore updateStore_v6
914 main(int argc
, char **argv
)
917 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
919 load_KernelEventMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
920 prime_KernelEventMonitor();