#include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "common.h" #include "nwstatus.h" typedef struct { List *interfaces; List *ipv4; List *ipv6; } netconfig_t; static netconfig_t *nstatus = NULL; static void sort_addresses(List *list); void netstatus_init(void) { if (nstatus == NULL) { nstatus = malloc(sizeof(netconfig_t)); nstatus->interfaces = new_list(); nstatus->ipv4 = new_list(); nstatus->ipv6 = new_list(); } } static int find_interface(void *data, void *user_data) { return ((interface_t *)data)->index == *((int *)user_data); } static interface_t * new_interface(int index) { interface_t *result; result = malloc(sizeof(interface_t)); result->name = NULL; result->index = index; result->has_link = -1; list_append(nstatus->interfaces, result); return result; } static interface_t * get_interface(index) { interface_t *result; result = list_search(nstatus->interfaces, find_interface, &index); if (result == NULL) { result = new_interface(index); } return result; } void interface_set_name(int index, char *name) { interface_t *interface; interface = get_interface(index); free(interface->name); interface->name = strdup(name); } void interface_set_link(int index, int has_link) { interface_t *interface; interface = get_interface(index); interface->has_link = has_link; } void interface_set_flags(int index, unsigned int flags) { interface_t *interface; interface = get_interface(index); interface->flags = flags; } int interface_get_link(int index) { return get_interface(index)->has_link; } char *interface_get_name(int index) { return get_interface(index)->name; } static int do_del_address(void *data, void *user_data) { address_t *a; a = (address_t *)data; if (a->interface == user_data) { switch (a->family) { case AF_INET: list_del(nstatus->ipv4, a); break; case AF_INET6: list_del(nstatus->ipv6, a); break; } free(a); } return TRUE; } void del_interface(int index) { interface_t *interface; interface = get_interface(index); list_foreach(nstatus->ipv4, do_del_address, interface); list_foreach(nstatus->ipv6, do_del_address, interface); list_del(nstatus->interfaces, interface); sort_addresses(nstatus->ipv4); sort_addresses(nstatus->ipv6); } static int find_v4(void *data, void *user_data) { address_t *a; a = (address_t *)data; return a->a.v4.s_addr == ((struct in_addr *) user_data)->s_addr; } static int find_v6(void *data, void *user_data) { address_t *a; struct in6_addr *addr; int i; a = (address_t *)data; addr = (struct in6_addr *)user_data; for (i = 0 ; i < 4 ; i++) { if (a->a.v6.s6_addr32[i] != addr->s6_addr32[i]) { return FALSE; } } return TRUE; } void add_address(int index, uint8_t scope, uint8_t family, void *addr) { address_t *a = NULL; switch (family) { case AF_INET: a = list_search(nstatus->ipv4, find_v4, addr); break; case AF_INET6: a = list_search(nstatus->ipv6, find_v6, addr); break; default: return; } if (a == NULL) { a = malloc(sizeof(address_t)); a->family = family; a->interface = get_interface(index); a->scope = scope; a->current = 0; switch (family) { case AF_INET: a->a.v4 = *(struct in_addr*)addr; list_append(nstatus->ipv4, a); sort_addresses(nstatus->ipv4); break; case AF_INET6: a->a.v6 = *(struct in6_addr *)addr; list_append(nstatus->ipv6, a); sort_addresses(nstatus->ipv6); break; } } } void del_address(int index, uint8_t family, void *addr) { address_t *a = NULL; switch (family) { case AF_INET: a = list_search(nstatus->ipv4, find_v4, addr); list_del(nstatus->ipv4, a); sort_addresses(nstatus->ipv4); break; case AF_INET6: a = list_search(nstatus->ipv6, find_v6, addr); list_del(nstatus->ipv6, a); sort_addresses(nstatus->ipv6); break; } free(a); } static int v4address_type(address_t *addr) { #define IN_SUBNET(i0,i1,i2,i3, mask) \ !(((ntohl(addr->a.v4.s_addr)) - \ ((i0 << 24) + (i1 << 16) + (i2 << 8) + (i3))) \ & (0xffffffff << (32 - mask))) #define NORMAL 0 #define PRIVATE 1 #define ZEROCONF 2 if (IN_SUBNET(168,254,0,0, 16)) { return ZEROCONF; } if (IN_SUBNET(10,0,0,0, 8) || IN_SUBNET(172,16,0,0, 12) || IN_SUBNET(192,168,0,0, 16)) { return PRIVATE; } return NORMAL; } static int print_address(void *data, void *user_data); int address_cmp(void *data0, void *data1, void *user_data) { address_t *a0 = (address_t *)data0; address_t *a1 = (address_t *)data1; /* We want scope global/universe in front */ if (a0->scope != RT_SCOPE_UNIVERSE || a1->scope != RT_SCOPE_UNIVERSE) { return (a1->scope == RT_SCOPE_UNIVERSE ? 1 : 0) - (a0->scope == RT_SCOPE_UNIVERSE ? 1 : 0); } /* Address with link up before addresses without a link */ if (!a0->interface->has_link || !a1->interface->has_link) { return (a1->interface->has_link ? 1 : 0) - (a0->interface->has_link ? 1 : 0); } /* Prefer to first have normal, then private then zeroconf addresses */ if (a0->family == AF_INET) { int a0_type = v4address_type(a0); int a1_type = v4address_type(a1); if (a0_type != a1_type) { return a0_type - a1_type; } } return a1->current - a0->current; } static void sort_addresses(List *list) { list_sort(list, address_cmp, NULL); } static address_t *get_best_address(List *list) { List_ptr *p; address_t *result = NULL; p = new_list_ptr(list); if (list_ptr_first(p)) { result = (address_t *)list_ptr_get_data(p); if (result->scope != RT_SCOPE_UNIVERSE || !result->interface->has_link) { result = NULL; } } del_list_ptr(p); return result; } address_t *get_best_v4_address(void) { return get_best_address(nstatus->ipv4); } address_t *get_best_v6_address(void) { return get_best_address(nstatus->ipv6); } static int unset_current_address(void *data, void *user_data) { ((address_t *)data)->current = FALSE; return TRUE; } void address_toggle_current(address_t *address) { switch (address->family) { case AF_INET: list_foreach(nstatus->ipv4, unset_current_address, NULL); break; case AF_INET6: list_foreach(nstatus->ipv6, unset_current_address, NULL); break; } address->current = TRUE; } static int print_interfaces(void *data, void *user_data) { interface_t *i; i = (interface_t *)data; printf("%s (%s, %x)\n", i->name, i->has_link ? "UP" : "DOWN", i->flags); return TRUE; } static int print_address(void *data, void *user_data) { char buffer[INET6_ADDRSTRLEN]; address_t *a; a = (address_t *)data; if (a == NULL) { return TRUE; } inet_ntop(a->family, &(a->a.v4), buffer, INET6_ADDRSTRLEN); printf("\t%s (%d): %s\n", a->interface->name, a->interface->index, buffer); return TRUE; } void print_status(void) { list_foreach(nstatus->interfaces, print_interfaces, NULL); list_foreach(nstatus->ipv4, print_address, NULL); list_foreach(nstatus->ipv6, print_address, NULL); printf("Choosen addresses:\n"); print_address(get_best_v4_address(), NULL); print_address(get_best_v6_address(), NULL); }