X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Frtnl.c;h=6002b5bcb9d48e95c5ed70019a274ac616311055;hb=6fd94b729495d6b9cdb66ae33adafd59f8b38957;hp=679a9dfef8f4c2ce171cc6f3c66fab4b0d813a2d;hpb=5640935cc685a7aa2bc8b6230d48f71d9ca0fdcc;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/rtnl.c b/src/rtnl.c index 679a9df..6002b5b 100644 --- a/src/rtnl.c +++ b/src/rtnl.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,20 +23,31 @@ #include #endif +#include #include #include #include +#include #include - +#include +#include +#include #include -#include #include #include +#include #include #include "connman.h" +#ifndef ARPHDR_PHONET_PIPE +#define ARPHDR_PHONET_PIPE (821) +#endif + +#define print(arg...) do { if (0) connman_info(arg); } while (0) +//#define print(arg...) connman_info(arg) + struct watch_data { unsigned int id; int index; @@ -47,26 +58,159 @@ struct watch_data { static GSList *watch_list = NULL; static unsigned int watch_id = 0; -static GHashTable *ipconfig_hash = NULL; -static GSList *ipconfig_list = NULL; +static GSList *update_list = NULL; +static guint update_interval = G_MAXUINT; +static guint update_timeout = 0; + +struct interface_data { + int index; + char *name; + char *ident; + enum connman_service_type service_type; + enum connman_device_type device_type; +}; + +static GHashTable *interface_list = NULL; -static void register_ipconfig(struct connman_ipconfig *ipconfig) +static void free_interface(gpointer data) { - ipconfig_list = g_slist_append(ipconfig_list, ipconfig); + struct interface_data *interface = data; + + __connman_technology_remove_interface(interface->service_type, + interface->index, interface->name, interface->ident); + + g_free(interface->ident); + g_free(interface->name); + g_free(interface); +} + +static connman_bool_t ether_blacklisted(const char *name) +{ + if (name == NULL) + return TRUE; + + /* virtual interface from VMware */ + if (g_str_has_prefix(name, "vmnet") == TRUE) + return TRUE; + + /* virtual interface from VirtualBox */ + if (g_str_has_prefix(name, "vboxnet") == TRUE) + return TRUE; + + /* virtual interface from Virtual Machine Manager */ + if (g_str_has_prefix(name, "virbr") == TRUE) + return TRUE; + + return FALSE; +} + +static connman_bool_t wext_interface(char *ifname) +{ + struct iwreq wrq; + int fd, err; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return FALSE; + + memset(&wrq, 0, sizeof(wrq)); + strncpy(wrq.ifr_name, ifname, IFNAMSIZ); + + err = ioctl(fd, SIOCGIWNAME, &wrq); + + close(fd); + + if (err < 0) + return FALSE; + + return TRUE; } -static void unregister_ipconfig(struct connman_ipconfig *ipconfig) +static void read_uevent(struct interface_data *interface) { - ipconfig_list = g_slist_remove(ipconfig_list, ipconfig); + char *filename, line[128]; + connman_bool_t found_devtype; + FILE *f; + + if (ether_blacklisted(interface->name) == TRUE) { + interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; + interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; + } else { + interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; + interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; + } + + filename = g_strdup_printf("/sys/class/net/%s/uevent", + interface->name); + + f = fopen(filename, "re"); + + g_free(filename); + + if (f == NULL) + return; + + found_devtype = FALSE; + while (fgets(line, sizeof(line), f)) { + char *pos; + + pos = strchr(line, '\n'); + if (pos == NULL) + continue; + pos[0] = '\0'; + + if (strncmp(line, "DEVTYPE=", 8) != 0) + continue; + + found_devtype = TRUE; + + if (strcmp(line + 8, "wlan") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_WIFI; + interface->device_type = CONNMAN_DEVICE_TYPE_WIFI; + } else if (strcmp(line + 8, "wwan") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR; + interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR; + } else if (strcmp(line + 8, "bluetooth") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_BLUETOOTH; + interface->device_type = CONNMAN_DEVICE_TYPE_BLUETOOTH; + } else if (strcmp(line + 8, "wimax") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_WIMAX; + interface->device_type = CONNMAN_DEVICE_TYPE_WIMAX; + } else if (strcmp(line + 8, "gadget") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_GADGET; + interface->device_type = CONNMAN_DEVICE_TYPE_GADGET; + + } else { + interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; + interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; + } + } + + fclose(f); + + if (found_devtype) + return; + + /* We haven't got a DEVTYPE, let's check if it's a wireless device */ + if (wext_interface(interface->name)) { + interface->service_type = CONNMAN_SERVICE_TYPE_WIFI; + interface->device_type = CONNMAN_DEVICE_TYPE_WIFI; + + connman_error("%s runs an unsupported 802.11 driver", + interface->name); + } } -static void free_ipconfig(gpointer data) +enum connman_device_type __connman_rtnl_get_device_type(int index) { - struct connman_ipconfig *ipconfig = data; + struct interface_data *interface; - unregister_ipconfig(ipconfig); + interface = g_hash_table_lookup(interface_list, + GINT_TO_POINTER(index)); + if (interface == NULL) + return CONNMAN_DEVICE_TYPE_UNKNOWN; - connman_ipconfig_unref(ipconfig); + return interface->device_type; } /** @@ -82,7 +226,6 @@ static void free_ipconfig(gpointer data) unsigned int connman_rtnl_add_newlink_watch(int index, connman_rtnl_link_cb_t callback, void *user_data) { - struct connman_ipconfig *ipconfig; struct watch_data *watch; watch = g_try_new0(struct watch_data, 1); @@ -99,11 +242,10 @@ unsigned int connman_rtnl_add_newlink_watch(int index, DBG("id %d", watch->id); - ipconfig = g_hash_table_lookup(ipconfig_hash, GINT_TO_POINTER(index)); - if (ipconfig != NULL) { - unsigned int flags = __connman_ipconfig_get_flags(ipconfig); + if (callback) { + unsigned int flags = __connman_ipconfig_get_flags_from_index(index); - if (callback) + if (flags > 0) callback(flags, 0, user_data); } @@ -136,21 +278,23 @@ void connman_rtnl_remove_watch(unsigned int id) } } -static void trigger_newlink(gpointer key, gpointer value, gpointer user_data) +static void trigger_rtnl(int index, void *user_data) { struct connman_rtnl *rtnl = user_data; - struct connman_ipconfig *ipconfig = value; - int index = GPOINTER_TO_INT(key); - - if (index < 0 || ipconfig == NULL) - return; if (rtnl->newlink) { - unsigned short type = __connman_ipconfig_get_type(ipconfig); - unsigned int flags = __connman_ipconfig_get_flags(ipconfig); + unsigned short type = __connman_ipconfig_get_type_from_index(index); + unsigned int flags = __connman_ipconfig_get_flags_from_index(index); rtnl->newlink(type, index, flags, 0); } + + if (rtnl->newgateway) { + const char *gateway = __connman_ipconfig_get_gateway_from_index(index); + + if (gateway != NULL) + rtnl->newgateway(index, gateway); + } } static GSList *rtnl_list = NULL; @@ -178,7 +322,7 @@ int connman_rtnl_register(struct connman_rtnl *rtnl) rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl, compare_priority); - g_hash_table_foreach(ipconfig_hash, trigger_newlink, rtnl); + __connman_ipconfig_foreach(trigger_rtnl, rtnl); return 0; } @@ -196,34 +340,133 @@ void connman_rtnl_unregister(struct connman_rtnl *rtnl) rtnl_list = g_slist_remove(rtnl_list, rtnl); } -static void process_newlink(unsigned short type, int index, - unsigned flags, unsigned change) +static const char *operstate2str(unsigned char operstate) +{ + switch (operstate) { + case IF_OPER_UNKNOWN: + return "UNKNOWN"; + case IF_OPER_NOTPRESENT: + return "NOT-PRESENT"; + case IF_OPER_DOWN: + return "DOWN"; + case IF_OPER_LOWERLAYERDOWN: + return "LOWER-LAYER-DOWN"; + case IF_OPER_TESTING: + return "TESTING"; + case IF_OPER_DORMANT: + return "DORMANT"; + case IF_OPER_UP: + return "UP"; + } + + return ""; +} + +static void extract_link(struct ifinfomsg *msg, int bytes, + struct ether_addr *address, const char **ifname, + unsigned int *mtu, unsigned char *operstate, + struct rtnl_link_stats *stats) +{ + struct rtattr *attr; + + for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes); + attr = RTA_NEXT(attr, bytes)) { + switch (attr->rta_type) { + case IFLA_ADDRESS: + if (address != NULL) + memcpy(address, RTA_DATA(attr), ETH_ALEN); + break; + case IFLA_IFNAME: + if (ifname != NULL) + *ifname = RTA_DATA(attr); + break; + case IFLA_MTU: + if (mtu != NULL) + *mtu = *((unsigned int *) RTA_DATA(attr)); + break; + case IFLA_STATS: + if (stats != NULL) + memcpy(stats, RTA_DATA(attr), + sizeof(struct rtnl_link_stats)); + break; + case IFLA_OPERSTATE: + if (operstate != NULL) + *operstate = *((unsigned char *) RTA_DATA(attr)); + break; + case IFLA_LINKMODE: + break; + } + } +} + +static void process_newlink(unsigned short type, int index, unsigned flags, + unsigned change, struct ifinfomsg *msg, int bytes) { - struct connman_ipconfig *ipconfig; + struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }}; + struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }}; + struct rtnl_link_stats stats; + unsigned char operstate = 0xff; + struct interface_data *interface; + const char *ifname = NULL; + unsigned int mtu = 0; + char ident[13], str[18]; GSList *list; + memset(&stats, 0, sizeof(stats)); + extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats); + + snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x", + address.ether_addr_octet[0], + address.ether_addr_octet[1], + address.ether_addr_octet[2], + address.ether_addr_octet[3], + address.ether_addr_octet[4], + address.ether_addr_octet[5]); + + snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X", + address.ether_addr_octet[0], + address.ether_addr_octet[1], + address.ether_addr_octet[2], + address.ether_addr_octet[3], + address.ether_addr_octet[4], + address.ether_addr_octet[5]); + switch (type) { case ARPHRD_ETHER: case ARPHRD_LOOPBACK: + case ARPHDR_PHONET_PIPE: case ARPHRD_NONE: - ipconfig = g_hash_table_lookup(ipconfig_hash, - GINT_TO_POINTER(index)); - if (ipconfig == NULL) { - ipconfig = connman_ipconfig_create(index); - if (ipconfig != NULL) { - g_hash_table_insert(ipconfig_hash, - GINT_TO_POINTER(index), ipconfig); - - register_ipconfig(ipconfig); - } - } - - if (ipconfig != NULL) - __connman_ipconfig_update_link(ipconfig, - flags, change); + __connman_ipconfig_newlink(index, type, flags, + str, mtu, &stats); break; } + if (memcmp(&address, &compare, ETH_ALEN) != 0) + connman_info("%s {newlink} index %d address %s mtu %u", + ifname, index, str, mtu); + + if (operstate != 0xff) + connman_info("%s {newlink} index %d operstate %u <%s>", + ifname, index, operstate, + operstate2str(operstate)); + + interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index)); + if (interface == NULL) { + interface = g_new0(struct interface_data, 1); + interface->index = index; + interface->name = g_strdup(ifname); + interface->ident = g_strdup(ident); + + g_hash_table_insert(interface_list, + GINT_TO_POINTER(index), interface); + + if (type == ARPHRD_ETHER) + read_uevent(interface); + + __connman_technology_add_interface(interface->service_type, + interface->index, interface->name, interface->ident); + } + for (list = rtnl_list; list; list = list->next) { struct connman_rtnl *rtnl = list->data; @@ -242,11 +485,22 @@ static void process_newlink(unsigned short type, int index, } } -static void process_dellink(unsigned short type, int index, - unsigned flags, unsigned change) +static void process_dellink(unsigned short type, int index, unsigned flags, + unsigned change, struct ifinfomsg *msg, int bytes) { + struct rtnl_link_stats stats; + unsigned char operstate = 0xff; + const char *ifname = NULL; GSList *list; + memset(&stats, 0, sizeof(stats)); + extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats); + + if (operstate != 0xff) + connman_info("%s {dellink} index %d operstate %u <%s>", + ifname, index, operstate, + operstate2str(operstate)); + for (list = rtnl_list; list; list = list->next) { struct connman_rtnl *rtnl = list->data; @@ -258,184 +512,347 @@ static void process_dellink(unsigned short type, int index, case ARPHRD_ETHER: case ARPHRD_LOOPBACK: case ARPHRD_NONE: - g_hash_table_remove(ipconfig_hash, GINT_TO_POINTER(index)); + __connman_ipconfig_dellink(index, &stats); break; } + + g_hash_table_remove(interface_list, GINT_TO_POINTER(index)); } -static char *extract_gateway(struct rtmsg *msg, int bytes, int *index) +static void extract_ipv4_addr(struct ifaddrmsg *msg, int bytes, + const char **label, + struct in_addr *local, + struct in_addr *address, + struct in_addr *broadcast) { - char *gateway = NULL; - struct in_addr addr; struct rtattr *attr; - for (attr = RTM_RTA(msg); RTA_OK(attr, bytes); + for (attr = IFA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { - case RTA_GATEWAY: - addr = *((struct in_addr *) RTA_DATA(attr)); - g_free(gateway); - gateway = g_strdup(inet_ntoa(addr)); + case IFA_ADDRESS: + if (address != NULL) + *address = *((struct in_addr *) RTA_DATA(attr)); break; - case RTA_OIF: - *index = *((int *) RTA_DATA(attr)); + case IFA_LOCAL: + if (local != NULL) + *local = *((struct in_addr *) RTA_DATA(attr)); + break; + case IFA_BROADCAST: + if (broadcast != NULL) + *broadcast = *((struct in_addr *) RTA_DATA(attr)); + break; + case IFA_LABEL: + if (label != NULL) + *label = RTA_DATA(attr); break; } } +} - return gateway; +static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes, + struct in6_addr *addr, + struct in6_addr *local) +{ + struct rtattr *attr; + + for (attr = IFA_RTA(msg); RTA_OK(attr, bytes); + attr = RTA_NEXT(attr, bytes)) { + switch (attr->rta_type) { + case IFA_ADDRESS: + if (addr != NULL) + *addr = *((struct in6_addr *) RTA_DATA(attr)); + break; + case IFA_LOCAL: + if (local != NULL) + *local = *((struct in6_addr *) RTA_DATA(attr)); + break; + } + } } -static void process_newgateway(struct rtmsg *msg, int bytes) +static void process_newaddr(unsigned char family, unsigned char prefixlen, + int index, struct ifaddrmsg *msg, int bytes) { - int index = -1; - char *gateway; - GSList *list; + const char *label = NULL; + void *src; + char ip_string[INET6_ADDRSTRLEN]; - gateway = extract_gateway(msg, bytes, &index); - if (gateway == NULL || index < 0) - return; + if (family == AF_INET) { + struct in_addr ipv4_addr = { INADDR_ANY }; - for (list = rtnl_list; list; list = list->next) { - struct connman_rtnl *rtnl = list->data; + extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL); + src = &ipv4_addr; + } else if (family == AF_INET6) { + struct in6_addr ipv6_address, ipv6_local; - if (rtnl->newgateway) - rtnl->newgateway(index, gateway); + extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local); + if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address)) + return; + + src = &ipv6_address; + } else { + return; } - g_free(gateway); + if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) + return; + + __connman_ipconfig_newaddr(index, family, label, + prefixlen, ip_string); } -static void process_delgateway(struct rtmsg *msg, int bytes) +static void process_deladdr(unsigned char family, unsigned char prefixlen, + int index, struct ifaddrmsg *msg, int bytes) { - int index = -1; - char *gateway; - GSList *list; + const char *label = NULL; + void *src; + char ip_string[INET6_ADDRSTRLEN]; - gateway = extract_gateway(msg, bytes, &index); - if (gateway == NULL || index < 0) - return; + if (family == AF_INET) { + struct in_addr ipv4_addr = { INADDR_ANY }; - for (list = rtnl_list; list; list = list->next) { - struct connman_rtnl *rtnl = list->data; + extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL); + src = &ipv4_addr; + } else if (family == AF_INET6) { + struct in6_addr ipv6_address, ipv6_local; - if (rtnl->delgateway) - rtnl->delgateway(index, gateway); + extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local); + if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address)) + return; + + src = &ipv6_address; + } else { + return; } - g_free(gateway); + if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) + return; + + __connman_ipconfig_deladdr(index, family, label, + prefixlen, ip_string); } -static void extract_addr(struct ifaddrmsg *msg, int bytes, - const char **label, - struct in_addr *local, - struct in_addr *address, - struct in_addr *broadcast) +static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index, + struct in_addr *dst, + struct in_addr *gateway) { struct rtattr *attr; - for (attr = IFA_RTA(msg); RTA_OK(attr, bytes); + for (attr = RTM_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { - case IFA_ADDRESS: - if (address != NULL) - *address = *((struct in_addr *) RTA_DATA(attr)); + case RTA_DST: + if (dst != NULL) + *dst = *((struct in_addr *) RTA_DATA(attr)); break; - case IFA_LOCAL: - if (local != NULL) - *local = *((struct in_addr *) RTA_DATA(attr)); + case RTA_GATEWAY: + if (gateway != NULL) + *gateway = *((struct in_addr *) RTA_DATA(attr)); break; - case IFA_BROADCAST: - if (broadcast != NULL) - *broadcast = *((struct in_addr *) RTA_DATA(attr)); + case RTA_OIF: + if (index != NULL) + *index = *((int *) RTA_DATA(attr)); break; - case IFA_LABEL: - if (label != NULL) - *label = RTA_DATA(attr); + } + } +} + +static void extract_ipv6_route(struct rtmsg *msg, int bytes, int *index, + struct in6_addr *dst, + struct in6_addr *gateway) +{ + struct rtattr *attr; + + for (attr = RTM_RTA(msg); RTA_OK(attr, bytes); + attr = RTA_NEXT(attr, bytes)) { + switch (attr->rta_type) { + case RTA_DST: + if (dst != NULL) + *dst = *((struct in6_addr *) RTA_DATA(attr)); + break; + case RTA_GATEWAY: + if (gateway != NULL) + *gateway = + *((struct in6_addr *) RTA_DATA(attr)); + break; + case RTA_OIF: + if (index != NULL) + *index = *((int *) RTA_DATA(attr)); break; } } } -static void process_newaddr(int family, int prefixlen, int index, - struct ifaddrmsg *msg, int bytes) +static void process_newroute(unsigned char family, unsigned char scope, + struct rtmsg *msg, int bytes) { GSList *list; - const char *label; - struct in_addr address; + char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN]; + int index = -1; - if (family != AF_INET) - return; + if (family == AF_INET) { + struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY }; - for (list = ipconfig_list; list; list = list->next) { - struct connman_ipconfig *ipconfig = list->data; + extract_ipv4_route(msg, bytes, &index, &dst, &gateway); - if (__connman_ipconfig_get_index(ipconfig) != index) - continue; + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + + __connman_ipconfig_newroute(index, family, scope, dststr, + gatewaystr); + + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && + !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY)) + return; + + if (dst.s_addr != INADDR_ANY) + return; + + } else if (family == AF_INET6) { + struct in6_addr dst = IN6ADDR_ANY_INIT, + gateway = IN6ADDR_ANY_INIT; + + extract_ipv6_route(msg, bytes, &index, &dst, &gateway); - extract_addr(msg, bytes, &label, &address, NULL, NULL); - __connman_ipconfig_add_address(ipconfig, label, prefixlen, - inet_ntoa(address), NULL); + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + + __connman_ipconfig_newroute(index, family, scope, dststr, + gatewaystr); + + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && + !(scope == RT_SCOPE_LINK && + IN6_IS_ADDR_UNSPECIFIED(&dst))) + return; + + if (!IN6_IS_ADDR_UNSPECIFIED(&dst)) + return; + } else + return; + + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; + + if (rtnl->newgateway) + rtnl->newgateway(index, gatewaystr); } } -static void process_deladdr(int family, int prefixlen, int index, - struct ifaddrmsg *msg, int bytes) +static void process_delroute(unsigned char family, unsigned char scope, + struct rtmsg *msg, int bytes) { GSList *list; - const char *label; - struct in_addr address; + char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN]; + int index = -1; - if (family != AF_INET) - return; + if (family == AF_INET) { + struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY }; - for (list = ipconfig_list; list; list = list->next) { - struct connman_ipconfig *ipconfig = list->data; + extract_ipv4_route(msg, bytes, &index, &dst, &gateway); - if (__connman_ipconfig_get_index(ipconfig) != index) - continue; + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + + __connman_ipconfig_delroute(index, family, scope, dststr, + gatewaystr); + + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && + !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY)) + return; + + if (dst.s_addr != INADDR_ANY) + return; + + } else if (family == AF_INET6) { + struct in6_addr dst = IN6ADDR_ANY_INIT, + gateway = IN6ADDR_ANY_INIT; + + extract_ipv6_route(msg, bytes, &index, &dst, &gateway); - extract_addr(msg, bytes, &label, &address, NULL, NULL); - __connman_ipconfig_del_address(ipconfig, label, prefixlen, - inet_ntoa(address), NULL); + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + + __connman_ipconfig_delroute(index, family, scope, dststr, + gatewaystr); + + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && + !(scope == RT_SCOPE_LINK && + IN6_IS_ADDR_UNSPECIFIED(&dst))) + return; + + if (!IN6_IS_ADDR_UNSPECIFIED(&dst)) + return; + } else + return; + + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; + + if (rtnl->delgateway) + rtnl->delgateway(index, gatewaystr); } } -static inline void print_inet(struct rtattr *attr, const char *name, int family) +static inline void print_ether(struct rtattr *attr, const char *name) { - if (family == AF_INET) { + int len = (int) RTA_PAYLOAD(attr); + + if (len == ETH_ALEN) { + struct ether_addr eth; + memcpy(ð, RTA_DATA(attr), ETH_ALEN); + print(" attr %s (len %d) %s\n", name, len, ether_ntoa(ð)); + } else + print(" attr %s (len %d)\n", name, len); +} + +static inline void print_inet(struct rtattr *attr, const char *name, + unsigned char family) +{ + int len = (int) RTA_PAYLOAD(attr); + + if (family == AF_INET && len == sizeof(struct in_addr)) { struct in_addr addr; addr = *((struct in_addr *) RTA_DATA(attr)); - DBG(" attr %s (len %d) %s\n", name, - (int) RTA_PAYLOAD(attr), inet_ntoa(addr)); + print(" attr %s (len %d) %s\n", name, len, inet_ntoa(addr)); } else - DBG(" attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr)); + print(" attr %s (len %d)\n", name, len); } -static inline void print_char(struct rtattr *attr, const char *name) +static inline void print_string(struct rtattr *attr, const char *name) { - DBG(" attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr), + print(" attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr), (char *) RTA_DATA(attr)); } static inline void print_byte(struct rtattr *attr, const char *name) { - DBG(" attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr), + print(" attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr), *((unsigned char *) RTA_DATA(attr))); } +static inline void print_integer(struct rtattr *attr, const char *name) +{ + print(" attr %s (len %d) %d\n", name, (int) RTA_PAYLOAD(attr), + *((int *) RTA_DATA(attr))); +} + static inline void print_attr(struct rtattr *attr, const char *name) { - if (name) - DBG(" attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr)); + int len = (int) RTA_PAYLOAD(attr); + + if (name && len > 0) + print(" attr %s (len %d)\n", name, len); else - DBG(" attr %d (len %d)\n", - attr->rta_type, (int) RTA_PAYLOAD(attr)); + print(" attr %d (len %d)\n", attr->rta_type, len); } static void rtnl_link(struct nlmsghdr *hdr) { -#if 0 struct ifinfomsg *msg; struct rtattr *attr; int bytes; @@ -443,22 +860,22 @@ static void rtnl_link(struct nlmsghdr *hdr) msg = (struct ifinfomsg *) NLMSG_DATA(hdr); bytes = IFLA_PAYLOAD(hdr); - DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags); + print("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags); for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { case IFLA_ADDRESS: - print_attr(attr, "address"); + print_ether(attr, "address"); break; case IFLA_BROADCAST: - print_attr(attr, "broadcast"); + print_ether(attr, "broadcast"); break; case IFLA_IFNAME: - print_char(attr, "ifname"); + print_string(attr, "ifname"); break; case IFLA_MTU: - print_attr(attr, "mtu"); + print_integer(attr, "mtu"); break; case IFLA_LINK: print_attr(attr, "link"); @@ -485,7 +902,7 @@ static void rtnl_link(struct nlmsghdr *hdr) print_attr(attr, "protinfo"); break; case IFLA_TXQLEN: - print_attr(attr, "txqlen"); + print_integer(attr, "txqlen"); break; case IFLA_MAP: print_attr(attr, "map"); @@ -504,40 +921,30 @@ static void rtnl_link(struct nlmsghdr *hdr) break; } } -#endif } static void rtnl_newlink(struct nlmsghdr *hdr) { struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr); - DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x", - msg->ifi_type, msg->ifi_index, - msg->ifi_flags, msg->ifi_change); - - process_newlink(msg->ifi_type, msg->ifi_index, - msg->ifi_flags, msg->ifi_change); - rtnl_link(hdr); + + process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags, + msg->ifi_change, msg, IFA_PAYLOAD(hdr)); } static void rtnl_dellink(struct nlmsghdr *hdr) { struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr); - DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x", - msg->ifi_type, msg->ifi_index, - msg->ifi_flags, msg->ifi_change); - - process_dellink(msg->ifi_type, msg->ifi_index, - msg->ifi_flags, msg->ifi_change); - rtnl_link(hdr); + + process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags, + msg->ifi_change, msg, IFA_PAYLOAD(hdr)); } static void rtnl_addr(struct nlmsghdr *hdr) { -#if 0 struct ifaddrmsg *msg; struct rtattr *attr; int bytes; @@ -545,7 +952,7 @@ static void rtnl_addr(struct nlmsghdr *hdr) msg = (struct ifaddrmsg *) NLMSG_DATA(hdr); bytes = IFA_PAYLOAD(hdr); - DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index); + print("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index); for (attr = IFA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { @@ -557,7 +964,7 @@ static void rtnl_addr(struct nlmsghdr *hdr) print_inet(attr, "local", msg->ifa_family); break; case IFA_LABEL: - print_char(attr, "label"); + print_string(attr, "label"); break; case IFA_BROADCAST: print_inet(attr, "broadcast", msg->ifa_family); @@ -576,38 +983,30 @@ static void rtnl_addr(struct nlmsghdr *hdr) break; } } -#endif } static void rtnl_newaddr(struct nlmsghdr *hdr) { struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr); - DBG("ifa_family %d ifa_prefixlen %d ifa_index %d", - msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index); + rtnl_addr(hdr); process_newaddr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index, msg, IFA_PAYLOAD(hdr)); - - rtnl_addr(hdr); } static void rtnl_deladdr(struct nlmsghdr *hdr) { struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr); - DBG("ifa_family %d ifa_prefixlen %d ifa_index %d", - msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index); + rtnl_addr(hdr); process_deladdr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index, msg, IFA_PAYLOAD(hdr)); - - rtnl_addr(hdr); } static void rtnl_route(struct nlmsghdr *hdr) { -#if 0 struct rtmsg *msg; struct rtattr *attr; int bytes; @@ -615,7 +1014,10 @@ static void rtnl_route(struct nlmsghdr *hdr) msg = (struct rtmsg *) NLMSG_DATA(hdr); bytes = RTM_PAYLOAD(hdr); - DBG("rtm_family %d rtm_flags 0x%04x", msg->rtm_family, msg->rtm_flags); + print("rtm_family %d rtm_table %d rtm_protocol %d", + msg->rtm_family, msg->rtm_table, msg->rtm_protocol); + print("rtm_scope %d rtm_type %d rtm_flags 0x%04x", + msg->rtm_scope, msg->rtm_type, msg->rtm_flags); for (attr = RTM_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { @@ -627,10 +1029,10 @@ static void rtnl_route(struct nlmsghdr *hdr) print_inet(attr, "src", msg->rtm_family); break; case RTA_IIF: - print_char(attr, "iif"); + print_string(attr, "iif"); break; case RTA_OIF: - print_attr(attr, "oif"); + print_integer(attr, "oif"); break; case RTA_GATEWAY: print_inet(attr, "gateway", msg->rtm_family); @@ -645,44 +1047,190 @@ static void rtnl_route(struct nlmsghdr *hdr) print_attr(attr, "metrics"); break; case RTA_TABLE: - print_attr(attr, "table"); + print_integer(attr, "table"); break; default: print_attr(attr, NULL); break; } } -#endif +} + +static connman_bool_t is_route_rtmsg(struct rtmsg *msg) +{ + + if (msg->rtm_table != RT_TABLE_MAIN) + return FALSE; + + if (msg->rtm_protocol != RTPROT_BOOT && + msg->rtm_protocol != RTPROT_KERNEL) + return FALSE; + + if (msg->rtm_type != RTN_UNICAST) + return FALSE; + + return TRUE; } static void rtnl_newroute(struct nlmsghdr *hdr) { struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr); - if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN && - msg->rtm_scope == RT_SCOPE_UNIVERSE) { - DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x", - msg->rtm_table, msg->rtm_scope, - msg->rtm_type, msg->rtm_flags); - process_newgateway(msg, RTM_PAYLOAD(hdr)); - } - rtnl_route(hdr); + + if (is_route_rtmsg(msg)) + process_newroute(msg->rtm_family, msg->rtm_scope, + msg, RTM_PAYLOAD(hdr)); } static void rtnl_delroute(struct nlmsghdr *hdr) { struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr); - if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN && - msg->rtm_scope == RT_SCOPE_UNIVERSE) { - DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x", - msg->rtm_table, msg->rtm_scope, - msg->rtm_type, msg->rtm_flags); - process_delgateway(msg, RTM_PAYLOAD(hdr)); + rtnl_route(hdr); + + if (is_route_rtmsg(msg)) + process_delroute(msg->rtm_family, msg->rtm_scope, + msg, RTM_PAYLOAD(hdr)); +} + +static void *rtnl_nd_opt_rdnss(struct nd_opt_hdr *opt, guint32 *lifetime, + int *nr_servers) +{ + guint32 *optint = (void *)opt; + + if (opt->nd_opt_len < 3) + return NULL; + + if (*lifetime > ntohl(optint[1])) + *lifetime = ntohl(optint[1]); + + /* nd_opt_len is in units of 8 bytes. The header is 1 unit (8 bytes) + and each address is another 2 units (16 bytes). + So the number of addresses (given rounding) is nd_opt_len/2 */ + *nr_servers = opt->nd_opt_len / 2; + + /* And they start 8 bytes into the packet, or two guint32s in. */ + return optint + 2; +} + +static const char **rtnl_nd_opt_dnssl(struct nd_opt_hdr *opt, guint32 *lifetime) +{ + const char **domains = NULL; + guint32 *optint = (void *)opt; + unsigned char *optc = (void *)&optint[2]; + int data_len = (opt->nd_opt_len * 8) - 8; + int nr_domains = 0; + int i, tmp; + + if (*lifetime > ntohl(optint[1])) + *lifetime = ntohl(optint[1]); + + /* Turn it into normal strings by converting the length bytes into '.', + and count how many search domains there are while we're at it. */ + i = 0; + while (i < data_len) { + if (optc[i] > 0x3f) { + DBG("DNSSL contains compressed elements in violation of RFC6106"); + return NULL; + } + + if (optc[i] == 0) { + nr_domains++; + i++; + /* Check for double zero */ + if (i < data_len && optc[i] == 0) + break; + continue; + } + + tmp = i; + i += optc[i] + 1; + + if (i >= data_len) { + DBG("DNSSL data overflows option length"); + return NULL; + } + + optc[tmp] = '.'; } - rtnl_route(hdr); + domains = g_try_new0(const char *, nr_domains + 1); + if (!domains) + return NULL; + + /* Now point to the normal strings, missing out the leading '.' that + each of them will have now. */ + for (i = 0; i < nr_domains; i++) { + domains[i] = (char *)optc + 1; + optc += strlen((char *)optc) + 1; + } + + return domains; +} + +static void rtnl_newnduseropt(struct nlmsghdr *hdr) +{ + struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(hdr); + struct nd_opt_hdr *opt = (void *)&msg[1]; + guint32 lifetime = -1; + const char **domains = NULL; + struct in6_addr *servers = NULL; + int nr_servers = 0; + int msglen = msg->nduseropt_opts_len; + char *interface; + + DBG("family %02x index %x len %04x type %02x code %02x", + msg->nduseropt_family, msg->nduseropt_ifindex, + msg->nduseropt_opts_len, msg->nduseropt_icmp_type, + msg->nduseropt_icmp_code); + + if (msg->nduseropt_family != AF_INET6 || + msg->nduseropt_icmp_type != ND_ROUTER_ADVERT || + msg->nduseropt_icmp_code != 0) + return; + + interface = connman_inet_ifname(msg->nduseropt_ifindex); + if (!interface) + return; + + for (opt = (void *)&msg[1]; + msglen >= 2 && msglen >= opt->nd_opt_len && opt->nd_opt_len; + msglen -= opt->nd_opt_len, + opt = ((void *)opt) + opt->nd_opt_len*8) { + + DBG("nd opt type %d len %d\n", + opt->nd_opt_type, opt->nd_opt_len); + + if (opt->nd_opt_type == 25) + servers = rtnl_nd_opt_rdnss(opt, &lifetime, + &nr_servers); + else if (opt->nd_opt_type == 31) + domains = rtnl_nd_opt_dnssl(opt, &lifetime); + } + + if (nr_servers) { + int i, j; + char buf[40]; + + for (i = 0; i < nr_servers; i++) { + if (!inet_ntop(AF_INET6, servers + i, buf, sizeof(buf))) + continue; + + if (domains == NULL || domains[0] == NULL) { + connman_resolver_append_lifetime(interface, + NULL, buf, lifetime); + continue; + } + + for (j = 0; domains[j]; j++) + connman_resolver_append_lifetime(interface, + domains[j], + buf, lifetime); + } + } + g_free(domains); + g_free(interface); } static const char *type2string(uint16_t type) @@ -712,6 +1260,8 @@ static const char *type2string(uint16_t type) return "NEWROUTE"; case RTM_DELROUTE: return "DELROUTE"; + case RTM_NEWNDUSEROPT: + return "NEWNDUSEROPT"; default: return "UNKNOWN"; } @@ -836,6 +1386,9 @@ static void rtnl_message(void *buf, size_t len) case RTM_DELROUTE: rtnl_delroute(hdr); break; + case RTM_NEWNDUSEROPT: + rtnl_newnduseropt(hdr); + break; } len -= hdr->nlmsg_len; @@ -848,17 +1401,22 @@ static gboolean netlink_event(GIOChannel *chan, { unsigned char buf[4096]; gsize len; - GIOError err; + GIOStatus status; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; memset(buf, 0, sizeof(buf)); - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); - if (err) { - if (err == G_IO_ERROR_AGAIN) - return TRUE; + status = g_io_channel_read_chars(chan, (gchar *) buf, + sizeof(buf), &len, NULL); + + switch (status) { + case G_IO_STATUS_NORMAL: + break; + case G_IO_STATUS_AGAIN: + return TRUE; + default: return FALSE; } @@ -907,7 +1465,7 @@ static int send_getaddr(void) return queue_request(req); } -int connman_rtnl_send_getroute(void) +static int send_getroute(void) { struct rtnl_request *req; @@ -927,6 +1485,78 @@ int connman_rtnl_send_getroute(void) return queue_request(req); } +static gboolean update_timeout_cb(gpointer user_data) +{ + __connman_rtnl_request_update(); + + return TRUE; +} + +static void update_interval_callback(guint min) +{ + if (update_timeout > 0) + g_source_remove(update_timeout); + + if (min < G_MAXUINT) { + update_interval = min; + update_timeout = g_timeout_add_seconds(update_interval, + update_timeout_cb, NULL); + } else { + update_timeout = 0; + update_interval = G_MAXUINT; + } +} + +static gint compare_interval(gconstpointer a, gconstpointer b) +{ + guint val_a = GPOINTER_TO_UINT(a); + guint val_b = GPOINTER_TO_UINT(b); + + return val_a - val_b; +} + +unsigned int __connman_rtnl_update_interval_add(unsigned int interval) +{ + guint min; + + if (interval == 0) + return 0; + + update_list = g_slist_insert_sorted(update_list, + GUINT_TO_POINTER(interval), compare_interval); + + min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0)); + if (min < update_interval) { + update_interval_callback(min); + __connman_rtnl_request_update(); + } + + return update_interval; +} + +unsigned int __connman_rtnl_update_interval_remove(unsigned int interval) +{ + guint min = G_MAXUINT; + + if (interval == 0) + return 0; + + update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval)); + + if (update_list != NULL) + min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0)); + + if (min > update_interval) + update_interval_callback(min); + + return min; +} + +int __connman_rtnl_request_update(void) +{ + return send_getlink(); +} + int __connman_rtnl_init(void) { struct sockaddr_nl addr; @@ -934,8 +1564,8 @@ int __connman_rtnl_init(void) DBG(""); - ipconfig_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, free_ipconfig); + interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, free_interface); sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (sk < 0) @@ -943,7 +1573,9 @@ int __connman_rtnl_init(void) memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; + addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | + RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE | + (1<<(RTNLGRP_ND_USEROPT-1)); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(sk); @@ -953,6 +1585,9 @@ int __connman_rtnl_init(void) channel = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, netlink_event, NULL); @@ -965,7 +1600,7 @@ void __connman_rtnl_start(void) send_getlink(); send_getaddr(); - connman_rtnl_send_getroute(); + send_getroute(); } void __connman_rtnl_cleanup(void) @@ -974,12 +1609,6 @@ void __connman_rtnl_cleanup(void) DBG(""); - g_slist_free(ipconfig_list); - ipconfig_list = NULL; - - g_hash_table_destroy(ipconfig_hash); - ipconfig_hash = NULL; - for (list = watch_list; list; list = list->next) { struct watch_data *watch = list->data; @@ -992,6 +1621,9 @@ void __connman_rtnl_cleanup(void) g_slist_free(watch_list); watch_list = NULL; + g_slist_free(update_list); + update_list = NULL; + for (list = request_list; list; list = list->next) { struct rtnl_request *req = list->data; @@ -1011,4 +1643,6 @@ void __connman_rtnl_cleanup(void) g_io_channel_unref(channel); channel = NULL; + + g_hash_table_destroy(interface_list); }