2 * Copyright (c) 2000-2003 Apple Computer, 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>
61 #include "ev_appletalk.h"
63 static const char *inetEventName
[] = {
66 "INET address changed",
67 "INET address deleted",
68 "INET destination address changed",
69 "INET broadcast address changed",
70 "INET netmask changed",
74 static const char *dlEventName
[] = {
85 "KEV_DL_IF_DETACHING",
89 "KEV_DL_PROTO_ATTACHED",
90 "KEV_DL_PROTO_DETACHED",
93 static const char *atalkEventName
[] = {
97 "KEV_ATALK_ZONEUPDATED",
99 "KEV_ATALK_ROUTERUP_INVALID",
100 "KEV_ATALK_ROUTERDOWN",
101 "KEV_ATALK_ZONELISTCHANGED"
104 static const char *inet6EventName
[] = {
106 "KEV_INET6_NEW_USER_ADDR",
107 "KEV_INET6_CHANGED_ADDR",
108 "KEV_INET6_ADDR_DELETED",
109 "KEV_INET6_NEW_LL_ADDR",
110 "KEV_INET6_NEW_RTADV_ADDR",
111 "KEV_INET6_DEFROUTER"
116 __private_extern__ SCDynamicStoreRef store
= NULL
;
117 __private_extern__ Boolean _verbose
= FALSE
;
122 dgram_socket(int domain
)
124 return (socket(domain
, SOCK_DGRAM
, 0));
128 ifflags_set(int s
, char * name
, short flags
)
133 bzero(&ifr
, sizeof(ifr
));
134 strncpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
135 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
139 ifr
.ifr_flags
|= flags
;
140 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
144 ifflags_clear(int s
, char * name
, short flags
)
149 bzero(&ifr
, sizeof(ifr
));
150 strncpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
151 ret
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
)&ifr
);
155 ifr
.ifr_flags
&= ~flags
;
156 return (ioctl(s
, SIOCSIFFLAGS
, &ifr
));
160 mark_if_up(char * name
)
162 int s
= dgram_socket(AF_INET
);
165 ifflags_set(s
, name
, IFF_UP
);
170 mark_if_down(char * name
)
172 int s
= dgram_socket(AF_INET
);
175 ifflags_clear(s
, name
, IFF_UP
);
180 logEvent(CFStringRef evStr
, struct kern_event_msg
*ev_msg
)
185 SCLog(_verbose
, LOG_DEBUG
, CFSTR("%@ event:"), evStr
);
186 SCLog(_verbose
, LOG_DEBUG
,
187 CFSTR(" Event size=%d, id=%d, vendor=%d, class=%d, subclass=%d, code=%d"),
192 ev_msg
->kev_subclass
,
194 for (i
= 0, j
= KEV_MSG_HEADER_SIZE
; j
< ev_msg
->total_size
; i
++, j
+=4) {
195 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" Event data[%2d] = %08lx"), i
, ev_msg
->event_data
[i
]);
200 inetEventNameString(u_long event_code
)
202 if (event_code
<= KEV_INET_ARPCOLLISION
) {
203 return (inetEventName
[event_code
]);
205 return ("New Apple network INET subcode");
209 inet6EventNameString(u_long event_code
)
211 if (event_code
<= KEV_INET6_DEFROUTER
) {
212 return (inet6EventName
[event_code
]);
214 return ("New Apple network INET6 subcode");
218 dlEventNameString(u_long event_code
)
220 if (event_code
<= KEV_DL_PROTO_DETACHED
) {
221 return (dlEventName
[event_code
]);
223 return ("New Apple network DL subcode");
227 atalkEventNameString(u_long event_code
)
229 if (event_code
<= KEV_ATALK_ZONELISTCHANGED
) {
230 return (atalkEventName
[event_code
]);
232 return ("New Apple network AppleTalk subcode");
237 copy_if_name(struct net_event_data
* ev
, char * ifr_name
, int ifr_len
)
239 snprintf(ifr_name
, ifr_len
, "%s%ld", ev
->if_name
, ev
->if_unit
);
244 processEvent_Apple_Network(struct kern_event_msg
*ev_msg
)
246 const char * eventName
= NULL
;
247 int dataLen
= (ev_msg
->total_size
- KEV_MSG_HEADER_SIZE
);
248 void * event_data
= &ev_msg
->event_data
[0];
249 Boolean handled
= TRUE
;
250 char ifr_name
[IFNAMSIZ
+1];
252 switch (ev_msg
->kev_subclass
) {
253 case KEV_INET_SUBCLASS
: {
254 eventName
= inetEventNameString(ev_msg
->event_code
);
255 switch (ev_msg
->event_code
) {
256 case KEV_INET_NEW_ADDR
:
257 case KEV_INET_CHANGED_ADDR
:
258 case KEV_INET_ADDR_DELETED
:
259 case KEV_INET_SIFDSTADDR
:
260 case KEV_INET_SIFBRDADDR
:
261 case KEV_INET_SIFNETMASK
: {
262 struct kev_in_data
* ev
;
264 ev
= (struct kev_in_data
*)event_data
;
265 if (dataLen
< sizeof(*ev
)) {
269 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
270 interface_update_ipv4(NULL
, ifr_name
);
273 case KEV_INET_ARPCOLLISION
: {
274 struct kev_in_collision
* ev
;
276 ev
= (struct kev_in_collision
*)event_data
;
277 if ((dataLen
< sizeof(*ev
))
278 || (dataLen
< (sizeof(*ev
) + ev
->hw_len
))) {
282 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
283 interface_collision_ipv4(ifr_name
,
295 case KEV_INET6_SUBCLASS
: {
296 struct kev_in6_data
* ev
;
298 eventName
= inet6EventNameString(ev_msg
->event_code
);
299 ev
= (struct kev_in6_data
*)event_data
;
300 switch (ev_msg
->event_code
) {
301 case KEV_INET6_NEW_USER_ADDR
:
302 case KEV_INET6_CHANGED_ADDR
:
303 case KEV_INET6_ADDR_DELETED
:
304 case KEV_INET6_NEW_LL_ADDR
:
305 case KEV_INET6_NEW_RTADV_ADDR
:
306 case KEV_INET6_DEFROUTER
:
307 if (dataLen
< sizeof(*ev
)) {
311 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
312 interface_update_ipv6(NULL
, ifr_name
);
321 case KEV_DL_SUBCLASS
: {
322 struct net_event_data
* ev
;
324 eventName
= dlEventNameString(ev_msg
->event_code
);
325 ev
= (struct net_event_data
*)event_data
;
326 switch (ev_msg
->event_code
) {
327 case KEV_DL_IF_ATTACHED
:
329 * new interface added
331 if (dataLen
< sizeof(*ev
)) {
335 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
339 case KEV_DL_IF_DETACHED
:
343 if (dataLen
< sizeof(*ev
)) {
347 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
348 link_remove(ifr_name
);
351 case KEV_DL_IF_DETACHING
:
353 * interface detaching
355 if (dataLen
< sizeof(*ev
)) {
359 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
360 interface_detaching(ifr_name
);
363 case KEV_DL_SIFFLAGS
:
364 case KEV_DL_SIFMETRICS
:
366 case KEV_DL_SIFPHYS
:
367 case KEV_DL_SIFMEDIA
:
368 case KEV_DL_SIFGENERIC
:
369 case KEV_DL_ADDMULTI
:
370 case KEV_DL_DELMULTI
:
374 case KEV_DL_PROTO_ATTACHED
:
375 case KEV_DL_PROTO_DETACHED
: {
376 struct kev_dl_proto_data
* protoEvent
;
378 protoEvent
= (struct kev_dl_proto_data
*)event_data
;
379 if (dataLen
< sizeof(*protoEvent
)) {
383 copy_if_name(&protoEvent
->link_data
,
384 ifr_name
, sizeof(ifr_name
));
385 if (protoEvent
->proto_remaining_count
== 0) {
386 mark_if_down(ifr_name
);
388 mark_if_up(ifr_name
);
393 case KEV_DL_LINK_OFF
:
394 case KEV_DL_LINK_ON
:
396 * update the link status in the store
398 if (dataLen
< sizeof(*ev
)) {
402 copy_if_name(ev
, ifr_name
, sizeof(ifr_name
));
403 link_update_status(ifr_name
, FALSE
);
412 case KEV_ATALK_SUBCLASS
: {
413 struct kev_atalk_data
* ev
;
415 eventName
= atalkEventNameString(ev_msg
->event_code
);
416 ev
= (struct kev_atalk_data
*)event_data
;
417 if (dataLen
< sizeof(*ev
)) {
421 copy_if_name(&ev
->link_data
, ifr_name
, sizeof(ifr_name
));
422 switch (ev_msg
->event_code
) {
423 case KEV_ATALK_ENABLED
:
424 interface_update_atalk_address(ev
, ifr_name
);
427 case KEV_ATALK_DISABLED
:
428 interface_update_shutdown_atalk();
431 case KEV_ATALK_ZONEUPDATED
:
432 interface_update_atalk_zone(ev
, ifr_name
);
435 case KEV_ATALK_ROUTERUP
:
436 case KEV_ATALK_ROUTERUP_INVALID
:
437 case KEV_ATALK_ROUTERDOWN
:
438 interface_update_appletalk(NULL
, ifr_name
);
441 case KEV_ATALK_ZONELISTCHANGED
:
455 if (handled
== FALSE
) {
458 evStr
= CFStringCreateWithCString(NULL
,
459 (eventName
!= NULL
) ? eventName
: "New Apple network subclass",
460 kCFStringEncodingASCII
);
461 logEvent(evStr
, ev_msg
);
469 processEvent_Apple_IOKit(struct kern_event_msg
*ev_msg
)
471 switch (ev_msg
->kev_subclass
) {
473 logEvent(CFSTR("New Apple IOKit subclass"), ev_msg
);
482 eventCallback(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
)
484 int so
= CFSocketGetNative(s
);
487 struct kern_event_msg
*ev_msg
= (struct kern_event_msg
*)&buf
[0];
490 status
= recv(so
, &buf
, sizeof(buf
), 0);
492 SCLog(TRUE
, LOG_ERR
, CFSTR("recv() failed: %s"), strerror(errno
));
498 while (offset
< status
) {
499 if ((offset
+ ev_msg
->total_size
) > status
) {
500 SCLog(TRUE
, LOG_NOTICE
, CFSTR("missed SYSPROTO_EVENT event, buffer not big enough"));
504 switch (ev_msg
->vendor_code
) {
505 case KEV_VENDOR_APPLE
:
506 switch (ev_msg
->kev_class
) {
507 case KEV_NETWORK_CLASS
:
508 processEvent_Apple_Network(ev_msg
);
510 case KEV_IOKIT_CLASS
:
511 processEvent_Apple_IOKit(ev_msg
);
514 /* unrecognized (Apple) event class */
515 logEvent(CFSTR("New (Apple) class"), ev_msg
);
520 /* unrecognized vendor code */
521 logEvent(CFSTR("New vendor"), ev_msg
);
524 offset
+= ev_msg
->total_size
;
525 ev_msg
= (struct kern_event_msg
*)&buf
[offset
];
535 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
536 CFSocketInvalidate(s
);
544 prime_KernelEventMonitor()
546 struct ifaddrs
*ifap
= NULL
;
547 struct ifaddrs
*scan
;
550 SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called"));
554 sock
= dgram_socket(AF_INET
);
556 SCLog(TRUE
, LOG_ERR
, CFSTR("could not get interface list, socket() failed: %s"), strerror(errno
));
560 if (getifaddrs(&ifap
) < 0) {
563 CFSTR("could not get interface info, getifaddrs() failed: %s"),
568 /* update list of interfaces & link status */
569 for (scan
= ifap
; scan
!= NULL
; scan
= scan
->ifa_next
) {
570 if (scan
->ifa_addr
== NULL
571 || scan
->ifa_addr
->sa_family
!= AF_LINK
) {
574 /* get the per-interface link/media information */
575 link_add(scan
->ifa_name
);
579 * update IPv4 network addresses already assigned to
582 interface_update_ipv4(ifap
, NULL
);
585 * update IPv6 network addresses already assigned to
588 interface_update_ipv6(ifap
, NULL
);
591 * update AppleTalk network addresses already assigned
594 interface_update_appletalk(ifap
, NULL
);
611 load_KernelEventMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
615 struct kev_request kev_req
;
617 CFSocketContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
618 CFRunLoopSourceRef rls
;
624 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
625 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
627 /* open a "configd" session to allow cache updates */
628 store
= SCDynamicStoreCreate(NULL
,
629 CFSTR("Kernel Event Monitor plug-in"),
633 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDnamicStoreCreate() failed: %s"), SCErrorString(SCError()));
634 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
638 /* Open an event socket */
639 so
= socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
);
641 /* establish filter to return all events */
642 kev_req
.vendor_code
= 0;
643 kev_req
.kev_class
= 0; /* Not used if vendor_code is 0 */
644 kev_req
.kev_subclass
= 0; /* Not used if either kev_class OR vendor_code are 0 */
645 status
= ioctl(so
, SIOCSKEVFILT
, &kev_req
);
647 SCLog(TRUE
, LOG_ERR
, CFSTR("could not establish event filter, ioctl() failed: %s"), strerror(errno
));
652 SCLog(TRUE
, LOG_ERR
, CFSTR("could not open event socket, socket() failed: %s"), strerror(errno
));
658 status
= ioctl(so
, FIONBIO
, &yes
);
660 SCLog(TRUE
, LOG_ERR
, CFSTR("could not set non-blocking io, ioctl() failed: %s"), strerror(errno
));
667 SCLog(TRUE
, LOG_ERR
, CFSTR("kernel event monitor disabled."));
672 /* Create a CFSocketRef for the PF_SYSTEM kernel event socket */
673 es
= CFSocketCreateWithNative(NULL
,
675 kCFSocketReadCallBack
,
679 /* Create and add a run loop source for the event socket */
680 rls
= CFSocketCreateRunLoopSource(NULL
, es
, 0);
681 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
692 #define appendAddress appendAddress_v4
693 #define getIF getIF_v4
694 #define updateStore updateStore_v4
700 #define appendAddress appendAddress_v6
701 #define getIF getIF_v6
702 #define updateStore updateStore_v6
708 #define getIF getIF_at
709 #define updateStore updateStore_at
710 #include "ev_appletalk.c"
715 main(int argc
, char **argv
)
718 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
720 load_KernelEventMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
721 prime_KernelEventMonitor();