/* * Copyright (c) 2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include "network_information_priv.h" #include sa_family_t nwi_af_list[] = {AF_INET, AF_INET6}; static __inline__ unsigned int nwi_state_compute_size(unsigned int n) { return (offsetof(nwi_state, nwi_ifstates[n])); } __private_extern__ nwi_state_t nwi_state_copy_priv(nwi_state_t src) { nwi_state_t dest = NULL; if (src == NULL) { return dest; } dest = malloc(src->size); if (dest != NULL) { bcopy(src, dest, src->size); dest->ref = 1; } return dest; } __private_extern__ nwi_state_t nwi_state_new(nwi_state_t old_state, int elems) { nwi_state_t state = NULL; int new_size; if (old_state == NULL && elems == 0) { return NULL; } /* Need to insert a last node for each of the v4/v6 list */ new_size = (elems != 0)? (sizeof(nwi_state) + nwi_state_compute_size((elems+1) * 2)):0; /* Should we reallocate? */ if (old_state != NULL) { if (old_state->size >= new_size) { return (old_state); } } state = malloc(new_size); if (state == NULL) { return NULL; } state->size = new_size; /* * v4 list is stored 0 to elems, * v6 list is stored elems + 1 to 2 * elems + 2 */ state->ipv6_start = elems + 1; if (old_state != NULL) { state->ipv6_count = old_state->ipv6_count; if (state->ipv6_count > 0) { bcopy((void*) &old_state->nwi_ifstates[old_state->ipv6_start], (void*) &state->nwi_ifstates[state->ipv6_start], old_state->ipv6_count * sizeof(nwi_ifstate)); } state->ipv4_count = old_state->ipv4_count; if (state->ipv4_count > 0) { bcopy((void*) old_state->nwi_ifstates, (void*) state->nwi_ifstates, old_state->ipv4_count * sizeof(nwi_ifstate)); } free(old_state); } else { state->ipv4_count = 0; state->ipv6_count = 0; } nwi_state_set_last(state, AF_INET); nwi_state_set_last(state, AF_INET6); state->ref = 1; return state; } static inline nwi_ifstate_t nwi_ifstate_get_last(nwi_state_t state, int af, uint32_t** last) { uint32_t* count; int idx; count = (af == AF_INET) ?&state->ipv4_count:&state->ipv6_count; idx = (af == AF_INET) ?state->ipv4_count:(state->ipv6_start + state->ipv6_count); *last = count; return &state->nwi_ifstates[idx]; } __private_extern__ void nwi_insert_ifstate(nwi_state_t state, const char* ifname, int af, uint64_t flags, Rank rank, void* ifa) { nwi_ifstate_t ifstate; /* Will only insert unique elements in the list */ ifstate = nwi_state_get_ifstate_with_name(state, af, ifname); /* Already present, just ignore it */ if (ifstate != NULL) { if (ifstate->rank < rank) { return; } } if (ifstate == NULL) { uint32_t *last; /* We need to append it as the last element */ ifstate = nwi_ifstate_get_last(state, af, &last); strcpy(ifstate->ifname, ifname); ifstate->af_alias = NULL; ifstate->af = af; ifstate->diff_ch = NULL; (*last)++; } /* We need to update the address/rank/flag fields for the existing/new * element */ if (ifa != NULL) { switch (af) { case AF_INET: ifstate->iaddr = *((struct in_addr *) ifa); break; case AF_INET6: ifstate->iaddr6 = *((struct in6_addr *) ifa); break; default: break; } } ifstate->rank = rank; ifstate->flags = flags; return; } __private_extern__ void nwi_state_clear(nwi_state_t state, int af) { uint32_t* count; count = (af == AF_INET) ?&state->ipv4_count:&state->ipv6_count; *count = 0; nwi_state_set_last(state, af); return; } __private_extern__ void nwi_state_set_last(nwi_state_t state, int af) { int last_elem_idx; if (state == NULL) { return; } /* The last element is an element with the flags set as * NWI_IFSTATE_FLAGS_NOT_IN_LIST */ last_elem_idx = (af == AF_INET) ?state->ipv4_count :(state->ipv6_start + state->ipv6_count); state->nwi_ifstates[last_elem_idx].ifname[0] = '\0'; state->nwi_ifstates[last_elem_idx].flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST; } __private_extern__ void _nwi_state_dump(int level, nwi_state_t state) { const char * addr_str; void * address; int i; char ntopbuf[INET6_ADDRSTRLEN]; nwi_ifstate_t scan; if (state == NULL) { syslog(level, ""); return; } syslog(level, "nwi_state = { gen = %llu size = %u #ipv4 = %u #ipv6 = %u }", state->generation_count, state->size, state->ipv4_count, state->ipv6_count); if (state->ipv4_count) { syslog(level, "IPv4:"); for (i = 0, scan = state->nwi_ifstates; i < state->ipv4_count; i++, scan++) { bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0; bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0; address = nwi_ifstate_get_address(scan); addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf)); syslog(level, " [%d]: %s%s%s%s rank %u iaddr: %s " , i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"", has_dns ? " dns" : "", never ? " never" : "", scan->rank, addr_str); } } if (state->ipv6_count) { syslog(level, "IPv6:"); for (i = 0, scan = state->nwi_ifstates + state->ipv6_start; i < state->ipv6_count; i++, scan++) { bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0; bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0; address = nwi_ifstate_get_address(scan); addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf)); syslog(level, " [%d]: %s%s%s%s rank %u iaddr6: %s ", i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"", has_dns ? " dns" : "", never ? " never" : "", scan->rank, addr_str); } } return; } #define unchanged "" #define added "+" #define deleted "-" #define changed "!" __private_extern__ void * nwi_ifstate_get_address(nwi_ifstate_t ifstate) { return (void *)&ifstate->iaddr; } __private_extern__ const char * nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate) { return ifstate->diff_ch; } static inline boolean_t nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2) { if (ifstate1->rank != ifstate2->rank) { return TRUE; } if (ifstate1->flags != ifstate2->flags) { return TRUE; } if (ifstate1->af == AF_INET) { if (memcmp(&ifstate1->iaddr, &ifstate2->iaddr, sizeof(struct in_addr)) != 0) { return TRUE; } } else { if (memcmp(&ifstate1->iaddr6, &ifstate2->iaddr6, sizeof(struct in6_addr)) != 0) { return TRUE; } } return FALSE; } static inline nwi_ifstate_t nwi_ifstate_append(nwi_state_t state, nwi_ifstate_t scan) { nwi_ifstate_t new_ifstate = NULL; uint32_t *last; new_ifstate = nwi_ifstate_get_last(state, scan->af, &last); memcpy(new_ifstate, scan, sizeof(*scan)); (*last)++; return new_ifstate; } static inline void nwi_ifstate_set_diff_str(nwi_ifstate_t ifstate, const char * ch) { ifstate->diff_ch = ch; } static void nwi_state_merge_added(nwi_state_t state, nwi_state_t old_state, nwi_state_t new_state) { int idx; nwi_ifstate_t scan; /* Iterate through v4 and v6 list and annotate the diff flags */ for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) { scan = nwi_state_get_first_ifstate(new_state, nwi_af_list[idx]); while (scan != NULL) { nwi_ifstate_t existing_ifstate, new_ifstate; const char* ifname; ifname = nwi_ifstate_get_ifname(scan); existing_ifstate = nwi_state_get_ifstate_with_name(old_state, scan->af, ifname); /* Add the element that is not in the store */ new_ifstate = nwi_ifstate_append(state, scan); /* These are potentially "added" elements unless they are * in the old list */ nwi_ifstate_set_diff_str(new_ifstate, added); if (existing_ifstate != NULL) { if (nwi_ifstate_has_changed(existing_ifstate, new_ifstate) == TRUE) { nwi_ifstate_set_diff_str(new_ifstate, changed); } else { nwi_ifstate_set_diff_str(new_ifstate, unchanged); } } scan = nwi_ifstate_get_next(scan, scan->af); } nwi_state_set_last(state, nwi_af_list[idx]); } return; } static void nwi_state_merge_removed(nwi_state_t state, nwi_state_t old_state) { int idx; nwi_ifstate_t scan; /* Iterate through v4 and v6 list and annotate the diff flags */ for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) { scan = nwi_state_get_first_ifstate(old_state, nwi_af_list[idx]); while (scan != NULL) { nwi_ifstate_t existing_ifstate; const char* ifname; ifname = nwi_ifstate_get_ifname(scan); existing_ifstate = nwi_state_get_ifstate_with_name(state, scan->af, ifname); /* Any elements that has not been added means that they are removed */ if (existing_ifstate == NULL) { nwi_ifstate_t new_ifstate = nwi_ifstate_append(state, scan); nwi_ifstate_set_diff_str(new_ifstate, deleted); } scan = nwi_ifstate_get_next(scan, scan->af); } nwi_state_set_last(state, nwi_af_list[idx]); } } __private_extern__ nwi_state_t nwi_state_diff(nwi_state_t old_state, nwi_state_t new_state) { nwi_state_t diff; int total_count = 0; if (old_state != NULL) { total_count = old_state->ipv4_count + old_state->ipv6_count; } if (new_state != NULL) { total_count += new_state->ipv4_count + new_state->ipv6_count; } if (total_count == 0) { return NULL; } diff = nwi_state_new(NULL, total_count); nwi_state_merge_added(diff, old_state, new_state); nwi_state_merge_removed(diff, old_state); /* Diff consists of a nwi_state_t with annotated diff_ch's */ return diff; }