/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * interfaces.c * - interface related routines */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "interfaces.h" #include "ip6config_utils.h" static struct sockaddr_in6 init_sin6 = {sizeof(init_sin6), AF_INET6}; static int if_inet_count(interface_t * if_p) { return (dynarray_count(&if_p->inet)); } static inet6_addrinfo_t * if_inet_addr_at(interface_t * if_p, int i) { return (dynarray_element(&if_p->inet, i)); } static void * inet6_addrinfo_copy(void * p) { inet6_addrinfo_t * src = (inet6_addrinfo_t *)p; inet6_addrinfo_t * dest; dest = malloc(sizeof(*dest)); if (dest) { bcopy(src, dest, sizeof(*dest)); } return (dest); } static void inet6_addrinfo_free(void * p) { free(p); return; } static interface_t * if_next_entry(interface_list_t * interfaces, char * name) { interface_t * entry; if (interfaces->count >= interfaces->size) return (NULL); entry = interfaces->list + interfaces->count++; bzero(entry, sizeof(*entry)); strcpy(entry->name, name); dynarray_init(&entry->inet, inet6_addrinfo_free, inet6_addrinfo_copy); return (entry); } static boolean_t if_build_interface_list(interface_list_t * interfaces) { struct ifaddrs *ifaddrs = NULL; struct ifaddrs *ifa; int size = 0; if (getifaddrs(&ifaddrs) < 0) { return FALSE; } for (ifa = ifaddrs; ifa != NULL && ifa->ifa_addr != NULL; ifa = ifa->ifa_next) { /* count the number of AF_INET6 and AF_LINK that were returned */ if ((ifa->ifa_addr->sa_family == AF_INET6) || (ifa->ifa_addr->sa_family == AF_LINK)) { size++; } } interfaces->list = (interface_t *)malloc(size * sizeof(*(interfaces->list))); if (interfaces->list == NULL) { goto err; } interfaces->count = 0; interfaces->size = size; for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) { continue; } switch (ifa->ifa_addr->sa_family) { case AF_INET6: { inet6_addrinfo_t info; interface_t * entry; u_char name[IFNAMSIZ + 1]; if (!strcmp(ifa->ifa_name, "lo0")) { /* Skip loopback for now... */ continue; } strncpy(name, ifa->ifa_name, sizeof(name)); name[IFNAMSIZ] = '\0'; entry = ifl_find_name(interfaces, name); if (entry == NULL) { /* new entry */ entry = if_next_entry(interfaces, name); if (entry == NULL) { /* NOT REACHED */ syslog(LOG_ERR, "if_build_interface_list: if_next_entry returns NULL"); continue; } entry->flags = ifa->ifa_flags; } bzero(&info, sizeof(info)); info.addr = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; dynarray_add(&entry->inet, inet6_addrinfo_copy(&info)); break; } case AF_LINK: { struct sockaddr_dl * dl_p; interface_t * entry; struct if_data * if_data; u_char name[IFNAMSIZ + 1]; dl_p = (struct sockaddr_dl *)ifa->ifa_addr; strncpy(name, ifa->ifa_name, sizeof(name)); name[IFNAMSIZ] = '\0'; entry = ifl_find_name(interfaces, name); if (entry == NULL) { /* new entry */ entry = if_next_entry(interfaces, name); if (entry == NULL) { /* NOT REACHED */ syslog(LOG_ERR, "if_build_interface_list interfaces: if_next_entry returns NULL"); continue; } entry->flags = ifa->ifa_flags; } if (dl_p->sdl_alen > sizeof(entry->link_address.addr)) { syslog(LOG_DEBUG, "%s: link type %d address length %d > %d", name, dl_p->sdl_type, dl_p->sdl_alen, sizeof(entry->link_address.addr)); entry->link_address.alen = sizeof(entry->link_address.addr); } else { entry->link_address.alen = dl_p->sdl_alen; } bcopy(dl_p->sdl_data + dl_p->sdl_nlen, entry->link_address.addr, entry->link_address.alen); entry->link_address.type = dl_p->sdl_type; entry->link_address.index = dl_p->sdl_index; if_data = (struct if_data *)ifa->ifa_data; entry->type = if_data->ifi_type; break; } } } /* make it the "right" size (plus 1 more in case it's 0) */ interfaces->list = (interface_t *)realloc(interfaces->list, sizeof(*(interfaces->list)) * (interfaces->count + 1)); freeifaddrs(ifaddrs); return (TRUE); err: if (interfaces->list) free(interfaces->list); interfaces->list = NULL; freeifaddrs(ifaddrs); return (FALSE); } __private_extern__ int ifl_count(interface_list_t * list_p) { return (list_p->count); } __private_extern__ interface_t * ifl_at_index(interface_list_t * list_p, int i) { if (i >= list_p->count || i < 0) return (NULL); return (list_p->list + i); } int ifl_index(interface_list_t * list_p, interface_t * if_p) { return (if_p - list_p->list); } __private_extern__ interface_t * ifl_find_name(interface_list_t * list_p, const char * name) { int i; for (i = 0; i < list_p->count; i++) { if (strcmp(list_p->list[i].name, name) == 0) return (list_p->list + i); } return (NULL); } __private_extern__ interface_list_t * ifl_init() { interface_list_t * list_p = (interface_list_t *)malloc(sizeof(*list_p)); if (list_p == NULL || if_build_interface_list(list_p) == FALSE) { if (list_p) free(list_p); return (NULL); } return (list_p); } __private_extern__ void ifl_free(interface_list_t * * iflist) { if (iflist != NULL && *iflist != NULL) { interface_list_t * list_p = *iflist; int i; for (i = 0; i < list_p->count; i++) { dynarray_free(&list_p->list[i].inet); } if (list_p->list) free(list_p->list); free(list_p); *iflist = NULL; } return; } /* * Functions: if_* * Purpose: * Interface-specific routines. */ __private_extern__ char * if_name(interface_t * if_p) { return (if_p->name); } __private_extern__ short if_flags(interface_t * if_p) { return (if_p->flags); } __private_extern__ void if_setflags(interface_t * if_p, short flags) { if_p->flags = flags; return; } __private_extern__ void if_free(interface_t * * if_p_p) { interface_t * if_p; if (if_p_p == NULL) { return; } if_p = *if_p_p; if (if_p == NULL) { return; } dynarray_free(&if_p->inet); free(if_p); *if_p_p = NULL; return; } __private_extern__ interface_t * if_dup(interface_t * intface) { interface_t * new_p; new_p = (interface_t *)calloc(1, sizeof(*new_p)); if (new_p == NULL) { return (NULL); } *new_p = *intface; (void)dynarray_dup(&new_p->inet, &intface->inet); return (new_p); } static __inline__ boolean_t if_inet_addr_remove_at(interface_t * if_p, int i) { return (dynarray_free_element(&if_p->inet, i)); } void if_link_copy(interface_t * dest, const interface_t * source) { dest->link_address = source->link_address; return; } void if_link_update(interface_t * if_p) { char * buf = NULL; size_t buf_len = 0; struct sockaddr_dl * dl_p; struct if_msghdr * ifm; int mib[6]; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_LINK; mib[4] = NET_RT_IFLIST; mib[5] = if_p->link_address.index; /* ask for exactly one interface */ if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) { fprintf(stderr, "sysctl() size failed: %s", strerror(errno)); goto failed; } buf = malloc(buf_len); if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) { fprintf(stderr, "sysctl() failed: %s", strerror(errno)); goto failed; } ifm = (struct if_msghdr *)buf; switch (ifm->ifm_type) { case RTM_IFINFO: dl_p = (struct sockaddr_dl *)(ifm + 1); if (dl_p->sdl_alen > sizeof(if_p->link_address.addr)) { syslog(LOG_DEBUG, "%s: link type %d address length %d > %d", if_name(if_p), dl_p->sdl_type, dl_p->sdl_alen, sizeof(if_p->link_address.addr)); if_p->link_address.alen = sizeof(if_p->link_address.addr); } else { if_p->link_address.alen = dl_p->sdl_alen; } bcopy(dl_p->sdl_data + dl_p->sdl_nlen, if_p->link_address.addr, if_p->link_address.alen); if_p->link_address.type = dl_p->sdl_type; } failed: if (buf != NULL) { free(buf); } return; } int if_ift_type(interface_t * if_p) { return (if_p->type); }