2 * Copyright (c) 2000-2008 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>
63 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
64 #include "ev_appletalk.h"
65 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
67 static const char *inetEventName
[] = {
70 "INET address changed",
71 "INET address deleted",
72 "INET destination address changed",
73 "INET broadcast address changed",
74 "INET netmask changed",
78 static const char *dlEventName
[] = {
89 "KEV_DL_IF_DETACHING",
93 "KEV_DL_PROTO_ATTACHED",
94 "KEV_DL_PROTO_DETACHED",
97 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
98 static const char *atalkEventName
[] = {
101 "KEV_ATALK_DISABLED",
102 "KEV_ATALK_ZONEUPDATED",
103 "KEV_ATALK_ROUTERUP",
104 "KEV_ATALK_ROUTERUP_INVALID",
105 "KEV_ATALK_ROUTERDOWN",
106 "KEV_ATALK_ZONELISTCHANGED"
108 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
110 static const char *inet6EventName
[] = {
112 "KEV_INET6_NEW_USER_ADDR",
113 "KEV_INET6_CHANGED_ADDR",
114 "KEV_INET6_ADDR_DELETED",
115 "KEV_INET6_NEW_LL_ADDR",
116 "KEV_INET6_NEW_RTADV_ADDR",
117 "KEV_INET6_DEFROUTER"
120 __private_extern__ Boolean network_changed
= FALSE
;
121 __private_extern__ SCDynamicStoreRef store
= NULL
;
122 __private_extern__ Boolean _verbose
= FALSE
;
126 dgram_socket(int domain
)
128 return (socket(domain
, SOCK_DGRAM
, 0));
132 ifflags_set(int s
, char * name
, short flags
)
137 bzero(&ifr
, sizeof(ifr
));
138 strncpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
139 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
143 ifr
.ifr_flags
|= flags
;
144 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
148 ifflags_clear(int s
, char * name
, short flags
)
153 bzero(&ifr
, sizeof(ifr
));
154 strncpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
155 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
159 ifr
.ifr_flags
&= ~flags
;
160 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
164 mark_if_up(char * name
)
166 int s
= dgram_socket(AF_INET
);
169 ifflags_set(s
, name
, IFF_UP
);
174 mark_if_down(char * name
)
176 int s
= dgram_socket(AF_INET
);
179 ifflags_clear(s
, name
, IFF_UP
);
184 post_network_changed(void)
186 if (network_changed
) {
189 status
= notify_post("com.apple.system.config.network_change");
190 if (status
!= NOTIFY_STATUS_OK
) {
191 SCLog(TRUE
, LOG_ERR
, CFSTR("notify_post() failed: error=%ld"), status
);
194 network_changed
= FALSE
;
201 logEvent(CFStringRef evStr
, struct kern_event_msg
*ev_msg
)
206 SCLog(_verbose
, LOG_DEBUG
, CFSTR("%@ event:"), evStr
);
207 SCLog(_verbose
, LOG_DEBUG
,
208 CFSTR(" Event size=%d, id=%d, vendor=%d, class=%d, subclass=%d, code=%d"),
213 ev_msg
->kev_subclass
,
215 for (i
= 0, j
= KEV_MSG_HEADER_SIZE
; j
< ev_msg
->total_size
; i
++, j
+=4) {
216 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" Event data[%2d] = %08lx"), i
, ev_msg
->event_data
[i
]);
221 inetEventNameString(uint32_t event_code
)
223 if (event_code
<= KEV_INET_ARPCOLLISION
) {
224 return (inetEventName
[event_code
]);
226 return ("New Apple network INET subcode");
230 inet6EventNameString(uint32_t event_code
)
232 if (event_code
<= KEV_INET6_DEFROUTER
) {
233 return (inet6EventName
[event_code
]);
235 return ("New Apple network INET6 subcode");
239 dlEventNameString(uint32_t event_code
)
241 if (event_code
<= KEV_DL_PROTO_DETACHED
) {
242 return (dlEventName
[event_code
]);
244 return ("New Apple network DL subcode");
247 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
249 atalkEventNameString(uint32_t event_code
)
251 if (event_code
<= KEV_ATALK_ZONELISTCHANGED
) {
252 return (atalkEventName
[event_code
]);
254 return ("New Apple network AppleTalk subcode");
256 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
259 copy_if_name(struct net_event_data
* ev
, char * ifr_name
, int ifr_len
)
261 snprintf(ifr_name
, ifr_len
, "%s%d", ev
->if_name
, ev
->if_unit
);
266 processEvent_Apple_Network(struct kern_event_msg
*ev_msg
)
268 const char * eventName
= NULL
;
269 int dataLen
= (ev_msg
->total_size
- KEV_MSG_HEADER_SIZE
);
270 void * event_data
= &ev_msg
->event_data
[0];
271 Boolean handled
= TRUE
;
272 char ifr_name
[IFNAMSIZ
+ 1];
274 switch (ev_msg
->kev_subclass
) {
275 case KEV_INET_SUBCLASS
: {
276 eventName
= inetEventNameString(ev_msg
->event_code
);
277 switch (ev_msg
->event_code
) {
278 case KEV_INET_NEW_ADDR
:
279 case KEV_INET_CHANGED_ADDR
:
280 case KEV_INET_ADDR_DELETED
:
281 case KEV_INET_SIFDSTADDR
:
282 case KEV_INET_SIFBRDADDR
:
283 case KEV_INET_SIFNETMASK
: {
284 struct kev_in_data
* ev
;
286 ev
= (struct kev_in_data
*)event_data
;
287 if (dataLen
< sizeof(*ev
)) {
291 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
292 interface_update_ipv4(NULL
, ifr_name
);
295 case KEV_INET_ARPCOLLISION
: {
296 struct kev_in_collision
* ev
;
298 ev
= (struct kev_in_collision
*)event_data
;
299 if ((dataLen
< sizeof(*ev
))
300 || (dataLen
< (sizeof(*ev
) + ev
->hw_len
))) {
304 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
305 interface_collision_ipv4(ifr_name
,
311 #if !TARGET_OS_IPHONE
312 case KEV_INET_PORTINUSE
: {
313 struct kev_in_portinuse
* ev
;
314 ev
= (struct kev_in_portinuse
*)event_data
;
315 if (dataLen
< sizeof(*ev
)) {
319 port_in_use_ipv4(ev
->port
, ev
->req_pid
);
322 #endif /* !TARGET_OS_IPHONE */
329 case KEV_INET6_SUBCLASS
: {
330 struct kev_in6_data
* ev
;
332 eventName
= inet6EventNameString(ev_msg
->event_code
);
333 ev
= (struct kev_in6_data
*)event_data
;
334 switch (ev_msg
->event_code
) {
335 case KEV_INET6_NEW_USER_ADDR
:
336 case KEV_INET6_CHANGED_ADDR
:
337 case KEV_INET6_ADDR_DELETED
:
338 case KEV_INET6_NEW_LL_ADDR
:
339 case KEV_INET6_NEW_RTADV_ADDR
:
340 case KEV_INET6_DEFROUTER
:
341 if (dataLen
< sizeof(*ev
)) {
345 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
346 interface_update_ipv6(NULL
, ifr_name
);
355 case KEV_DL_SUBCLASS
: {
356 struct net_event_data
* ev
;
358 eventName
= dlEventNameString(ev_msg
->event_code
);
359 ev
= (struct net_event_data
*)event_data
;
360 switch (ev_msg
->event_code
) {
361 case KEV_DL_IF_ATTACHED
:
363 * new interface added
365 if (dataLen
< sizeof(*ev
)) {
369 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
373 case KEV_DL_IF_DETACHED
:
377 if (dataLen
< sizeof(*ev
)) {
381 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
382 link_remove(ifr_name
);
385 case KEV_DL_IF_DETACHING
:
387 * interface detaching
389 if (dataLen
< sizeof(*ev
)) {
393 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
394 interface_detaching(ifr_name
);
397 case KEV_DL_SIFFLAGS
:
398 case KEV_DL_SIFMETRICS
:
400 case KEV_DL_SIFPHYS
:
401 case KEV_DL_SIFMEDIA
:
402 case KEV_DL_SIFGENERIC
:
403 case KEV_DL_ADDMULTI
:
404 case KEV_DL_DELMULTI
:
408 case KEV_DL_PROTO_ATTACHED
:
409 case KEV_DL_PROTO_DETACHED
: {
410 struct kev_dl_proto_data
* protoEvent
;
412 protoEvent
= (struct kev_dl_proto_data
*)event_data
;
413 if (dataLen
< sizeof(*protoEvent
)) {
417 copy_if_name(&protoEvent
->link_data
,
418 ifr_name
, sizeof(ifr_name
));
419 if (protoEvent
->proto_remaining_count
== 0) {
420 mark_if_down(ifr_name
);
422 mark_if_up(ifr_name
);
427 case KEV_DL_LINK_OFF
:
428 case KEV_DL_LINK_ON
:
430 * update the link status in the store
432 if (dataLen
< sizeof(*ev
)) {
436 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
437 link_update_status(ifr_name
, FALSE
);
446 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
447 case KEV_ATALK_SUBCLASS
: {
448 struct kev_atalk_data
* ev
;
450 eventName
= atalkEventNameString(ev_msg
->event_code
);
451 ev
= (struct kev_atalk_data
*)event_data
;
452 if (dataLen
< sizeof(*ev
)) {
456 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
457 switch (ev_msg
->event_code
) {
458 case KEV_ATALK_ENABLED
:
459 interface_update_atalk_address(ev
, ifr_name
);
462 case KEV_ATALK_DISABLED
:
463 interface_update_shutdown_atalk();
466 case KEV_ATALK_ZONEUPDATED
:
467 interface_update_atalk_zone(ev
, ifr_name
);
470 case KEV_ATALK_ROUTERUP
:
471 case KEV_ATALK_ROUTERUP_INVALID
:
472 case KEV_ATALK_ROUTERDOWN
:
473 interface_update_appletalk(NULL
, ifr_name
);
476 case KEV_ATALK_ZONELISTCHANGED
:
485 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
491 if (handled
== FALSE
) {
494 evStr
= CFStringCreateWithCString(NULL
,
495 (eventName
!= NULL
) ? eventName
: "New Apple network subclass",
496 kCFStringEncodingASCII
);
497 logEvent(evStr
, ev_msg
);
504 processEvent_Apple_IOKit(struct kern_event_msg
*ev_msg
)
506 switch (ev_msg
->kev_subclass
) {
508 logEvent(CFSTR("New Apple IOKit subclass"), ev_msg
);
516 processEvent_Apple_System(struct kern_event_msg
*ev_msg
)
518 switch (ev_msg
->kev_subclass
) {
520 logEvent(CFSTR("New Apple System subclass"), ev_msg
);
528 eventCallback(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
)
530 int so
= CFSocketGetNative(s
);
533 struct kern_event_msg
*ev_msg
= (struct kern_event_msg
*)&buf
[0];
536 status
= recv(so
, &buf
, sizeof(buf
), 0);
538 SCLog(TRUE
, LOG_ERR
, CFSTR("recv() failed: %s"), strerror(errno
));
544 while (offset
< status
) {
545 if ((offset
+ ev_msg
->total_size
) > status
) {
546 SCLog(TRUE
, LOG_NOTICE
, CFSTR("missed SYSPROTO_EVENT event, buffer not big enough"));
550 switch (ev_msg
->vendor_code
) {
551 case KEV_VENDOR_APPLE
:
552 switch (ev_msg
->kev_class
) {
553 case KEV_NETWORK_CLASS
:
554 processEvent_Apple_Network(ev_msg
);
556 case KEV_IOKIT_CLASS
:
557 processEvent_Apple_IOKit(ev_msg
);
559 case KEV_SYSTEM_CLASS
:
560 processEvent_Apple_System(ev_msg
);
563 /* unrecognized (Apple) event class */
564 logEvent(CFSTR("New (Apple) class"), ev_msg
);
569 /* unrecognized vendor code */
570 logEvent(CFSTR("New vendor"), ev_msg
);
573 offset
+= ev_msg
->total_size
;
574 ev_msg
= (struct kern_event_msg
*)&buf
[offset
];
579 post_network_changed();
585 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
586 CFSocketInvalidate(s
);
593 prime_KernelEventMonitor()
595 struct ifaddrs
*ifap
= NULL
;
596 struct ifaddrs
*scan
;
599 SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called"));
603 sock
= dgram_socket(AF_INET
);
605 SCLog(TRUE
, LOG_ERR
, CFSTR("could not get interface list, socket() failed: %s"), strerror(errno
));
609 if (getifaddrs(&ifap
) == -1) {
612 CFSTR("could not get interface info, getifaddrs() failed: %s"),
617 /* update list of interfaces & link status */
618 for (scan
= ifap
; scan
!= NULL
; scan
= scan
->ifa_next
) {
619 if (scan
->ifa_addr
== NULL
620 || scan
->ifa_addr
->sa_family
!= AF_LINK
) {
623 /* get the per-interface link/media information */
624 link_add(scan
->ifa_name
);
628 * update IPv4 network addresses already assigned to
631 interface_update_ipv4(ifap
, NULL
);
634 * update IPv6 network addresses already assigned to
637 interface_update_ipv6(ifap
, NULL
);
639 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
641 * update AppleTalk network addresses already assigned
644 interface_update_appletalk(ifap
, NULL
);
645 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
656 network_changed
= TRUE
;
657 post_network_changed();
663 kevSocketCopyDescription(const void *info
)
665 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<kernel event socket>"));
670 load_KernelEventMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
672 CFSocketContext context
= { 0
676 , kevSocketCopyDescription
679 struct kev_request kev_req
;
680 CFRunLoopSourceRef rls
;
688 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
689 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
691 /* open a "configd" session to allow cache updates */
692 store
= SCDynamicStoreCreate(NULL
,
693 CFSTR("Kernel Event Monitor plug-in"),
697 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDnamicStoreCreate() failed: %s"), SCErrorString(SCError()));
698 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
702 /* Open an event socket */
703 so
= socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
);
705 /* establish filter to return all events */
706 kev_req
.vendor_code
= 0;
707 kev_req
.kev_class
= 0; /* Not used if vendor_code is 0 */
708 kev_req
.kev_subclass
= 0; /* Not used if either kev_class OR vendor_code are 0 */
709 status
= ioctl(so
, SIOCSKEVFILT
, &kev_req
);
711 SCLog(TRUE
, LOG_ERR
, CFSTR("could not establish event filter, ioctl() failed: %s"), strerror(errno
));
716 SCLog(TRUE
, LOG_ERR
, CFSTR("could not open event socket, socket() failed: %s"), strerror(errno
));
722 status
= ioctl(so
, FIONBIO
, &yes
);
724 SCLog(TRUE
, LOG_ERR
, CFSTR("could not set non-blocking io, ioctl() failed: %s"), strerror(errno
));
731 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
736 /* Create a CFSocketRef for the PF_SYSTEM kernel event socket */
737 es
= CFSocketCreateWithNative(NULL
,
739 kCFSocketReadCallBack
,
743 /* Create and add a run loop source for the event socket */
744 rls
= CFSocketCreateRunLoopSource(NULL
, es
, 0);
745 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
756 #define appendAddress appendAddress_v4
757 #define getIF getIF_v4
758 #define updateStore updateStore_v4
764 #define appendAddress appendAddress_v6
765 #define getIF getIF_v6
766 #define updateStore updateStore_v6
772 #if !TARGET_OS_IPHONE && INCLUDE_APPLETALK
773 #define getIF getIF_at
774 #define updateStore updateStore_at
775 #include "ev_appletalk.c"
778 #endif /* !TARGET_OS_IPHONE && INCLUDE_APPLETALK */
781 main(int argc
, char **argv
)
784 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
786 load_KernelEventMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
787 prime_KernelEventMonitor();