__private_extern__
void
link_add(const char *if_name)
{
	CFStringRef		interface;
	CFStringRef		cacheKey;
	CFDictionaryRef		dict;
	CFMutableDictionaryRef	newDict		= NULL;
	CFArrayRef		ifList;
	CFMutableArrayRef	newIFList	= NULL;

	interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman);
	cacheKey  = SCDynamicStoreKeyCreateNetworkInterface(NULL,
							    kSCDynamicStoreDomainState);

	dict = cache_SCDynamicStoreCopyValue(store, cacheKey);
	if (dict) {
		if (isA_CFDictionary(dict)) {
			newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
			ifList  = CFDictionaryGetValue(newDict, kSCPropNetInterfaces);
			if (isA_CFArray(ifList)) {
				newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList);
			}
		}
		CFRelease(dict);
	}

	if (!newDict) {
		newDict = CFDictionaryCreateMutable(NULL,
						    0,
						    &kCFTypeDictionaryKeyCallBacks,
						    &kCFTypeDictionaryValueCallBacks);
	}

	if (!newIFList) {
		newIFList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	}

	if (CFArrayContainsValue(newIFList,
				 CFRangeMake(0, CFArrayGetCount(newIFList)),
				 interface) == FALSE) {
		CFArrayAppendValue(newIFList, interface);
		CFDictionarySetValue(newDict, kSCPropNetInterfaces, newIFList);
	}
	cache_SCDynamicStoreSetValue(store, cacheKey, newDict);
	link_update_status(if_name, TRUE);
#ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
	link_update_quality_metric(if_name);
#endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
	CFRelease(cacheKey);
	CFRelease(interface);
	if (newDict)	CFRelease(newDict);
	if (newIFList)	CFRelease(newIFList);

	return;
}
static void
processEvent_Apple_Network(struct kern_event_msg *ev_msg)
{
	const char *			eventName = NULL;
	int				dataLen = (ev_msg->total_size - KEV_MSG_HEADER_SIZE);
	void *				event_data = &ev_msg->event_data[0];
	Boolean				handled = TRUE;
	char				ifr_name[IFNAMSIZ+1];

	switch (ev_msg->kev_subclass) {
		case KEV_INET_SUBCLASS : {
			eventName = inetEventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_INET_NEW_ADDR :
				case KEV_INET_CHANGED_ADDR :
				case KEV_INET_ADDR_DELETED :
				case KEV_INET_SIFDSTADDR :
				case KEV_INET_SIFBRDADDR :
				case KEV_INET_SIFNETMASK : {
					struct kev_in_data * ev;

					ev = (struct kev_in_data *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv4(NULL, ifr_name);
					break;
				}
				case KEV_INET_ARPCOLLISION : {
					struct kev_in_collision * ev;

					ev = (struct kev_in_collision *)event_data;
					if ((dataLen < sizeof(*ev))
					    || (dataLen < (sizeof(*ev) + ev->hw_len))) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_collision_ipv4(ifr_name,
								 ev->ia_ipaddr,
								 ev->hw_len,
								 ev->hw_addr);
					break;
				}
				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_INET6_SUBCLASS : {
			struct kev_in6_data * ev;

			eventName = inet6EventNameString(ev_msg->event_code);
			ev = (struct kev_in6_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_INET6_NEW_USER_ADDR :
				case KEV_INET6_CHANGED_ADDR :
				case KEV_INET6_ADDR_DELETED :
				case KEV_INET6_NEW_LL_ADDR :
				case KEV_INET6_NEW_RTADV_ADDR :
				case KEV_INET6_DEFROUTER :
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv6(NULL, ifr_name);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_DL_SUBCLASS : {
			struct net_event_data * ev;

			eventName = dlEventNameString(ev_msg->event_code);
			ev = (struct net_event_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_DL_IF_ATTACHED :
					/*
					 * new interface added
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_add(ifr_name);
					break;

				case KEV_DL_IF_DETACHED :
					/*
					 * interface removed
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_remove(ifr_name);
					break;

				case KEV_DL_IF_DETACHING :
					/*
					 * interface detaching
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_detaching(ifr_name);
					break;

				case KEV_DL_SIFFLAGS :
				case KEV_DL_SIFMETRICS :
				case KEV_DL_SIFMTU :
				case KEV_DL_SIFPHYS :
				case KEV_DL_SIFMEDIA :
				case KEV_DL_SIFGENERIC :
				case KEV_DL_ADDMULTI :
				case KEV_DL_DELMULTI :
					handled = FALSE;
					break;

				case KEV_DL_PROTO_ATTACHED :
				case KEV_DL_PROTO_DETACHED : {
					struct kev_dl_proto_data * protoEvent;

					protoEvent = (struct kev_dl_proto_data *)event_data;
					if (dataLen < sizeof(*protoEvent)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&protoEvent->link_data,
						     ifr_name, sizeof(ifr_name));
					if (protoEvent->proto_remaining_count == 0) {
						mark_if_down(ifr_name);
					} else {
						mark_if_up(ifr_name);
					}
					break;
				}

				case KEV_DL_LINK_OFF :
				case KEV_DL_LINK_ON :
					/*
					 * update the link status in the store
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_update_status(ifr_name, FALSE);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_ATALK_SUBCLASS: {
			struct kev_atalk_data * ev;

			eventName = atalkEventNameString(ev_msg->event_code);
			ev = (struct kev_atalk_data *)event_data;
			if (dataLen < sizeof(*ev)) {
				handled = FALSE;
				break;
			}
			copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
			switch (ev_msg->event_code) {
				case KEV_ATALK_ENABLED:
					interface_update_atalk_address(ev, ifr_name);
					break;

				case KEV_ATALK_DISABLED:
					interface_update_shutdown_atalk();
					break;

				case KEV_ATALK_ZONEUPDATED:
					interface_update_atalk_zone(ev, ifr_name);
					break;

				case KEV_ATALK_ROUTERUP:
				case KEV_ATALK_ROUTERUP_INVALID:
				case KEV_ATALK_ROUTERDOWN:
					interface_update_appletalk(NULL, ifr_name);
					break;

				case KEV_ATALK_ZONELISTCHANGED:
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		default :
			handled = FALSE;
			break;
	}

	if (handled == FALSE) {
		CFStringRef	evStr;

		evStr = CFStringCreateWithCString(NULL,
						  (eventName != NULL) ? eventName : "New Apple network subclass",
						  kCFStringEncodingASCII);
		logEvent(evStr, ev_msg);
		CFRelease(evStr);
	}
	return;
}
static void
processEvent_Apple_Network(struct kern_event_msg *ev_msg)
{
	const char *			eventName = NULL;
	int				dataLen = (ev_msg->total_size - KEV_MSG_HEADER_SIZE);
	void *				event_data = &ev_msg->event_data[0];
	Boolean				handled = TRUE;
	char				ifr_name[IFNAMSIZ];

	switch (ev_msg->kev_subclass) {
		case KEV_INET_SUBCLASS : {
			eventName = inetEventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_INET_NEW_ADDR :
				case KEV_INET_CHANGED_ADDR :
				case KEV_INET_ADDR_DELETED :
				case KEV_INET_SIFDSTADDR :
				case KEV_INET_SIFBRDADDR :
				case KEV_INET_SIFNETMASK : {
					struct kev_in_data * ev;

					ev = (struct kev_in_data *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv4(NULL, ifr_name);
					break;
				}
				case KEV_INET_ARPCOLLISION : {
					struct kev_in_collision * ev;

					ev = (struct kev_in_collision *)event_data;
					if ((dataLen < sizeof(*ev))
					    || (dataLen < (sizeof(*ev) + ev->hw_len))) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_collision_ipv4(ifr_name,
								 ev->ia_ipaddr,
								 ev->hw_len,
								 ev->hw_addr);
					break;
				}
#if	!TARGET_OS_IPHONE
				case KEV_INET_PORTINUSE : {
					struct kev_in_portinuse	* ev;
					ev = (struct kev_in_portinuse *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					port_in_use_ipv4(ev->port, ev->req_pid);
					break;
				}
#endif	/* !TARGET_OS_IPHONE */
				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_INET6_SUBCLASS : {
			struct kev_in6_data * ev;

			eventName = inet6EventNameString(ev_msg->event_code);
			ev = (struct kev_in6_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_INET6_NEW_USER_ADDR :
				case KEV_INET6_CHANGED_ADDR :
				case KEV_INET6_ADDR_DELETED :
				case KEV_INET6_NEW_LL_ADDR :
				case KEV_INET6_NEW_RTADV_ADDR :
				case KEV_INET6_DEFROUTER :
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv6(NULL, ifr_name);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_DL_SUBCLASS : {
			struct net_event_data * ev;

			eventName = dlEventNameString(ev_msg->event_code);
			ev = (struct net_event_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_DL_IF_ATTACHED :
					/*
					 * new interface added
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_add(ifr_name);
					break;

				case KEV_DL_IF_DETACHED :
					/*
					 * interface removed
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_remove(ifr_name);
					break;

				case KEV_DL_IF_DETACHING :
					/*
					 * interface detaching
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_detaching(ifr_name);
					break;

				case KEV_DL_PROTO_ATTACHED :
				case KEV_DL_PROTO_DETACHED : {
					struct kev_dl_proto_data * protoEvent;

					protoEvent = (struct kev_dl_proto_data *)event_data;
					if (dataLen < sizeof(*protoEvent)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&protoEvent->link_data,
						     ifr_name, sizeof(ifr_name));
					if (protoEvent->proto_remaining_count == 0) {
						mark_if_down(ifr_name);
					} else {
						mark_if_up(ifr_name);
					}
					break;
				}

#ifdef	KEV_DL_IF_IDLE_ROUTE_REFCNT
				case KEV_DL_IF_IDLE_ROUTE_REFCNT: {
					/*
					 * interface route refcnt idle
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_idle_state(ifr_name);
					break;
				}
#endif	// KEV_DL_IF_IDLE_ROUTE_REFCNT

				case KEV_DL_LINK_OFF :
				case KEV_DL_LINK_ON :
					/*
					 * update the link status in the store
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_update_status(ifr_name, FALSE);
					break;

#ifdef  KEV_DL_LINK_QUALITY_METRIC_CHANGED
				case KEV_DL_LINK_QUALITY_METRIC_CHANGED: {
					struct kev_dl_link_quality_metric_data * lqm_data;
					lqm_data = (struct kev_dl_link_quality_metric_data *) event_data;

					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_quality_metric(ifr_name,
								   lqm_data->link_quality_metric);
					break;
				}
#endif  // KEV_DL_LINK_QUALITY_METRIC_CHANGED

#ifdef	KEV_DL_ISSUES
				case KEV_DL_ISSUES: {
					struct kev_dl_issues *issues;

					issues = (struct kev_dl_issues *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_link_issues(ifr_name,
								     issues->timestamp,
								     issues->modid,
								     DLIL_MODIDLEN,
								     issues->info,
								     (bcmp(issues->info, info_zero, DLIL_MODIDLEN) != 0)
									?DLIL_MODARGLEN
									:0);
					break;
				}
#endif	// KEV_DL_ISSUES

				case KEV_DL_SIFFLAGS :
				case KEV_DL_SIFMETRICS :
				case KEV_DL_SIFMTU :
				case KEV_DL_SIFPHYS :
				case KEV_DL_SIFMEDIA :
				case KEV_DL_SIFGENERIC :
				case KEV_DL_ADDMULTI :
				case KEV_DL_DELMULTI :
				case KEV_DL_LINK_ADDRESS_CHANGED :
				case KEV_DL_WAKEFLAGS_CHANGED :
#ifdef  KEV_DL_IFCAP_CHANGED
				case KEV_DL_IFCAP_CHANGED :
#endif	// KEV_DL_IFCAP_CHANGED
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
#ifdef	KEV_ND6_SUBCLASS
		case KEV_ND6_SUBCLASS : {
			eventName = nd6EventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_KEV_ND6_RA :
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
#endif	// KEV_ND6_SUBCLASS
		case KEV_LOG_SUBCLASS : {
			break;
		}
		default :
			handled = FALSE;
			break;
	}

	if (handled == FALSE) {
		CFStringRef	evStr;

		evStr = CFStringCreateWithCString(NULL,
						  (eventName != NULL) ? eventName : "New Apple network subclass",
						  kCFStringEncodingASCII);
		logEvent(evStr, ev_msg);
		CFRelease(evStr);
	}
	return;
}