X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Frtnl.c;h=7da55fa567b736a400209cc6c26380c397113d6a;hb=5bf80017321e03103f588306d2888d22f3f67ffa;hp=b10d8354d7cc73431b3dd1989919cc4109c011d2;hpb=03d76c59d9a59d23515e9f5d5374591886563f5f;p=platform%2Fupstream%2Fconnman.git diff --git a/src/rtnl.c b/src/rtnl.c index b10d835..7da55fa 100644 --- a/src/rtnl.c +++ b/src/rtnl.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2013 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,6 +23,7 @@ #include #endif +#include #include #include #include @@ -45,7 +46,13 @@ #define ARPHDR_PHONET_PIPE (821) #endif -#define print(arg...) do { } while (0) +#if defined TIZEN_EXT +#ifndef ARPHDR_RMNET +#define ARPHDR_RMNET (530) +#endif +#endif + +#define print(arg...) do { if (0) connman_info(arg); } while (0) //#define print(arg...) connman_info(arg) struct watch_data { @@ -64,7 +71,6 @@ 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; @@ -77,92 +83,121 @@ static void free_interface(gpointer data) struct interface_data *interface = data; __connman_technology_remove_interface(interface->service_type, - interface->index, interface->name, interface->ident); + interface->index, interface->ident); g_free(interface->ident); - g_free(interface->name); g_free(interface); } -static connman_bool_t ether_blacklisted(const char *name) +static bool ether_blacklisted(const char *name) { - if (name == NULL) - return TRUE; + if (!name) + 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; + if (__connman_device_isfiltered(name)) + return true; - /* virtual interface from Virtual Machine Manager */ - if (g_str_has_prefix(name, "virbr") == TRUE) - return TRUE; - - return FALSE; + return false; } -static connman_bool_t wext_interface(char *ifname) +#if !defined TIZEN_EXT +static bool wext_interface(char *ifname) { struct iwreq wrq; int fd, err; - fd = socket(PF_INET, SOCK_DGRAM, 0); + fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (fd < 0) - return FALSE; + return false; memset(&wrq, 0, sizeof(wrq)); - strncpy(wrq.ifr_name, ifname, IFNAMSIZ); + strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name) - 1); err = ioctl(fd, SIOCGIWNAME, &wrq); close(fd); if (err < 0) - return FALSE; + return false; - return TRUE; + return true; } +#endif + +#if defined TIZEN_EXT +static bool __connman_rtnl_is_cellular_device(const char *name) +{ + char **pattern; + char **cellular_interfaces; + + cellular_interfaces = + connman_setting_get_string_list( + "NetworkCellularInterfaceList"); + if (!cellular_interfaces) + return false; + + for (pattern = cellular_interfaces; *pattern; pattern++) { + if (g_str_has_prefix(name, *pattern)) { + DBG("Cellular interface: %s", name); + return true; + } + } + + return false; +} +#endif static void read_uevent(struct interface_data *interface) { - char *filename, line[128]; - connman_bool_t found_devtype; + char *filename, *name, line[128]; + bool found_devtype; FILE *f; - if (ether_blacklisted(interface->name) == TRUE) { + name = connman_inet_ifname(interface->index); + +#if defined TIZEN_EXT + if (__connman_rtnl_is_cellular_device(name)) { + interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR; + interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR; + g_free(name); + return; + } +#endif + + if (ether_blacklisted(name)) { interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; + goto out; } 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); + filename = g_strdup_printf("/sys/class/net/%s/uevent", name); f = fopen(filename, "re"); g_free(filename); - if (f == NULL) - return; + if (!f) { + interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; + interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; + goto out; + } - found_devtype = FALSE; + found_devtype = false; while (fgets(line, sizeof(line), f)) { char *pos; pos = strchr(line, '\n'); - if (pos == NULL) + if (!pos) continue; pos[0] = '\0'; if (strncmp(line, "DEVTYPE=", 8) != 0) continue; - found_devtype = TRUE; + found_devtype = true; if (strcmp(line + 8, "wlan") == 0) { interface->service_type = CONNMAN_SERVICE_TYPE_WIFI; @@ -173,13 +208,15 @@ static void read_uevent(struct interface_data *interface) } 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 if (strcmp(line + 8, "vlan") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; + interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; + } else if (strcmp(line + 8, "bond") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; + interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; } else { interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; @@ -189,16 +226,21 @@ static void read_uevent(struct interface_data *interface) fclose(f); if (found_devtype) - return; + goto out; +#if !defined TIZEN_EXT + /* TIZEN does not use old wext interface */ /* We haven't got a DEVTYPE, let's check if it's a wireless device */ - if (wext_interface(interface->name)) { + if (wext_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); + connman_error("%s runs an unsupported 802.11 driver", name); } +#endif + +out: + g_free(name); } enum connman_device_type __connman_rtnl_get_device_type(int index) @@ -207,7 +249,7 @@ enum connman_device_type __connman_rtnl_get_device_type(int index) interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index)); - if (interface == NULL) + if (!interface) return CONNMAN_DEVICE_TYPE_UNKNOWN; return interface->device_type; @@ -229,7 +271,7 @@ unsigned int connman_rtnl_add_newlink_watch(int index, struct watch_data *watch; watch = g_try_new0(struct watch_data, 1); - if (watch == NULL) + if (!watch) return 0; watch->id = ++watch_id; @@ -243,7 +285,7 @@ unsigned int connman_rtnl_add_newlink_watch(int index, DBG("id %d", watch->id); if (callback) { - unsigned int flags = __connman_ipconfig_get_flags(index); + unsigned int flags = __connman_ipconfig_get_flags_from_index(index); if (flags > 0) callback(flags, 0, user_data); @@ -283,16 +325,18 @@ static void trigger_rtnl(int index, void *user_data) struct connman_rtnl *rtnl = user_data; if (rtnl->newlink) { - unsigned short type = __connman_ipconfig_get_type(index); - unsigned int flags = __connman_ipconfig_get_flags(index); + 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(index); + const char *gateway = + __connman_ipconfig_get_gateway_from_index(index, + CONNMAN_IPCONFIG_TYPE_ALL); - if (gateway != NULL) + if (gateway) rtnl->newgateway(index, gateway); } } @@ -362,10 +406,10 @@ static const char *operstate2str(unsigned char operstate) return ""; } -static void extract_link(struct ifinfomsg *msg, int bytes, +static bool 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 rtnl_link_stats *stats) { struct rtattr *attr; @@ -373,37 +417,40 @@ static void extract_link(struct ifinfomsg *msg, int bytes, attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { case IFLA_ADDRESS: - if (address != NULL) + if (address) memcpy(address, RTA_DATA(attr), ETH_ALEN); break; case IFLA_IFNAME: - if (ifname != NULL) + if (ifname) *ifname = RTA_DATA(attr); break; case IFLA_MTU: - if (mtu != NULL) + if (mtu) *mtu = *((unsigned int *) RTA_DATA(attr)); break; case IFLA_STATS: - if (stats != NULL) + if (stats) memcpy(stats, RTA_DATA(attr), sizeof(struct rtnl_link_stats)); break; case IFLA_OPERSTATE: - if (operstate != NULL) + if (operstate) *operstate = *((unsigned char *) RTA_DATA(attr)); break; case IFLA_LINKMODE: break; + case IFLA_WIRELESS: + return false; } } + + return true; } static void process_newlink(unsigned short type, int index, unsigned flags, unsigned change, struct ifinfomsg *msg, int bytes) { 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; @@ -413,7 +460,30 @@ static void process_newlink(unsigned short type, int index, unsigned flags, GSList *list; memset(&stats, 0, sizeof(stats)); - extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats); + if (!extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats)) + return; + +#if defined TIZEN_EXT_WIFI_MESH + /* Do not accept Wi-Fi Mesh interface */ + if (g_strrstr(ifname, "mesh") != NULL) { + DBG("Newlink event for Wi-Fi Mesh interface ignored"); + return; + } + + /* Do not accept Wi-Fi WLAN1 interface "dedicated for softAP */ + if (!g_strcmp0(ifname, "wlan1")) { + DBG("Newlink event for Wi-Fi WLAN1 interface ignored"); + return; + } +#endif + +#if defined TIZEN_EXT + /* Do not accept Wi-Fi P2P interface */ + if (g_strrstr(ifname, "p2p") != NULL) { + DBG("Newlink event for Wi-Fi P2P interface ignored"); + return; + } +#endif snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x", address.ether_addr_octet[0], @@ -431,19 +501,38 @@ static void process_newlink(unsigned short type, int index, unsigned flags, address.ether_addr_octet[4], address.ether_addr_octet[5]); + if (flags & IFF_SLAVE) { + connman_info("%s {newlink} ignoring slave, index %d address %s", + ifname, index, str); + return; + } + +#ifdef TIZEN_EXT + if (TIZEN_TV_EXT && g_strcmp0(ident, "eeeeeeeeeeee") == 0) { + DBG("Newlink event with Dummy MAC. Ignored!"); + return; + } +#endif + switch (type) { case ARPHRD_ETHER: case ARPHRD_LOOPBACK: case ARPHDR_PHONET_PIPE: + case ARPHRD_PPP: case ARPHRD_NONE: +#if defined TIZEN_EXT +/* + * Description: ARPHDR_RMNET for QC modem using QMI + */ + case ARPHDR_RMNET: +#endif __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); + 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>", @@ -451,10 +540,9 @@ static void process_newlink(unsigned short type, int index, unsigned flags, operstate2str(operstate)); interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index)); - if (interface == NULL) { + if (!interface) { 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, @@ -462,7 +550,29 @@ static void process_newlink(unsigned short type, int index, unsigned flags, if (type == ARPHRD_ETHER) read_uevent(interface); - } +#if defined TIZEN_EXT + if (type == ARPHRD_PPP || type == ARPHDR_RMNET) + read_uevent(interface); + + } else if (g_strcmp0(interface->ident, ident) != 0) { + /* If an original address is built-in physical device, + * it's hardly get an address at a initial creation + */ + __connman_technology_remove_interface(interface->service_type, + interface->index, interface->ident); + + g_free(interface->ident); + interface->ident = g_strdup(ident); + + __connman_technology_add_interface(interface->service_type, + interface->index, interface->ident); + + interface = NULL; +#endif + } else if (type == ARPHRD_ETHER && interface->device_type == CONNMAN_DEVICE_TYPE_UNKNOWN) + read_uevent(interface); + else + interface = NULL; for (list = rtnl_list; list; list = list->next) { struct connman_rtnl *rtnl = list->data; @@ -471,8 +581,15 @@ static void process_newlink(unsigned short type, int index, unsigned flags, rtnl->newlink(type, index, flags, change); } - __connman_technology_add_interface(interface->service_type, - interface->index, interface->name, interface->ident); + /* + * The interface needs to be added after the newlink call. + * The newlink will create the technology when needed and + * __connman_technology_add_interface() expects the + * technology to be there already. + */ + if (interface) + __connman_technology_add_interface(interface->service_type, + interface->index, interface->ident); for (list = watch_list; list; list = list->next) { struct watch_data *watch = list->data; @@ -494,7 +611,8 @@ static void process_dellink(unsigned short type, int index, unsigned flags, GSList *list; memset(&stats, 0, sizeof(stats)); - extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats); + if (!extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats)) + return; if (operstate != 0xff) connman_info("%s {dellink} index %d operstate %u <%s>", @@ -511,7 +629,16 @@ static void process_dellink(unsigned short type, int index, unsigned flags, switch (type) { case ARPHRD_ETHER: case ARPHRD_LOOPBACK: + case ARPHDR_PHONET_PIPE: + case ARPHRD_PPP: case ARPHRD_NONE: +#if defined TIZEN_EXT + /* + * Description: SLP requires ARPHRD_PPP for PPP type device + * ARPHDR_RMNET for QC modem using QMI + */ + case ARPHDR_RMNET: +#endif __connman_ipconfig_dellink(index, &stats); break; } @@ -531,19 +658,19 @@ static void extract_ipv4_addr(struct ifaddrmsg *msg, int bytes, attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { case IFA_ADDRESS: - if (address != NULL) + if (address) *address = *((struct in_addr *) RTA_DATA(attr)); break; case IFA_LOCAL: - if (local != NULL) + if (local) *local = *((struct in_addr *) RTA_DATA(attr)); break; case IFA_BROADCAST: - if (broadcast != NULL) + if (broadcast) *broadcast = *((struct in_addr *) RTA_DATA(attr)); break; case IFA_LABEL: - if (label != NULL) + if (label) *label = RTA_DATA(attr); break; } @@ -560,11 +687,11 @@ static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes, attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { case IFA_ADDRESS: - if (addr != NULL) + if (addr) *addr = *((struct in6_addr *) RTA_DATA(attr)); break; case IFA_LOCAL: - if (local != NULL) + if (local) *local = *((struct in6_addr *) RTA_DATA(attr)); break; } @@ -574,68 +701,74 @@ static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes, static void process_newaddr(unsigned char family, unsigned char prefixlen, int index, struct ifaddrmsg *msg, int bytes) { + struct in_addr ipv4_addr = { INADDR_ANY }; + struct in6_addr ipv6_address, ipv6_local; const char *label = NULL; void *src; char ip_string[INET6_ADDRSTRLEN]; - if (family != AF_INET && family != AF_INET6) - return; - if (family == AF_INET) { - struct in_addr ipv4_addr = { INADDR_ANY }; 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; - extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local); if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address)) return; src = &ipv6_address; + } else { + return; } - if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) + if (!inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN)) return; - __connman_ipconfig_newaddr(index, family, label, - prefixlen, ip_string); + if (__connman_ipconfig_newaddr(index, family, label, + prefixlen, ip_string) >= 0) { + if (family == AF_INET6) { + /* + * Re-create RDNSS configured servers if there + * are any for this interface. This is done + * because we might have now properly + * configured interface with proper + * autoconfigured address. + */ + __connman_resolver_redo_servers(index); + } + } } static void process_deladdr(unsigned char family, unsigned char prefixlen, int index, struct ifaddrmsg *msg, int bytes) { + struct in_addr ipv4_addr = { INADDR_ANY }; + struct in6_addr ipv6_address, ipv6_local; const char *label = NULL; void *src; char ip_string[INET6_ADDRSTRLEN]; - if (family != AF_INET && family != AF_INET6) - return; - if (family == AF_INET) { - struct in_addr ipv4_addr = { INADDR_ANY }; - 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; - extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local); if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address)) return; src = &ipv6_address; + } else { + return; } - if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) + if (!inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN)) return; __connman_ipconfig_deladdr(index, family, label, prefixlen, ip_string); } -static void extract_route(struct rtmsg *msg, int bytes, int *index, +static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index, struct in_addr *dst, struct in_addr *gateway) { @@ -645,15 +778,41 @@ static void extract_route(struct rtmsg *msg, int bytes, int *index, attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { case RTA_DST: - if (dst != NULL) + if (dst) *dst = *((struct in_addr *) RTA_DATA(attr)); break; case RTA_GATEWAY: - if (gateway != NULL) + if (gateway) *gateway = *((struct in_addr *) RTA_DATA(attr)); break; case RTA_OIF: - if (index != NULL) + if (index) + *index = *((int *) RTA_DATA(attr)); + break; + } + } +} + +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) + *dst = *((struct in6_addr *) RTA_DATA(attr)); + break; + case RTA_GATEWAY: + if (gateway) + *gateway = + *((struct in6_addr *) RTA_DATA(attr)); + break; + case RTA_OIF: + if (index) *index = *((int *) RTA_DATA(attr)); break; } @@ -664,26 +823,49 @@ static void process_newroute(unsigned char family, unsigned char scope, struct rtmsg *msg, int bytes) { GSList *list; - struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY }; - char dststr[16], gatewaystr[16]; + 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 }; - extract_route(msg, bytes, &index, &dst, &gateway); + extract_ipv4_route(msg, bytes, &index, &dst, &gateway); - inet_ntop(family, &dst, dststr, sizeof(dststr)); - inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); - __connman_ipconfig_newroute(index, family, scope, dststr, gatewaystr); + __connman_ipconfig_newroute(index, family, scope, dststr, + gatewaystr); - /* skip host specific routes */ - if (scope != RT_SCOPE_UNIVERSE && + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY)) - return; + 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); + + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); - if (dst.s_addr != INADDR_ANY) + __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) { @@ -698,26 +880,49 @@ static void process_delroute(unsigned char family, unsigned char scope, struct rtmsg *msg, int bytes) { GSList *list; - struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY }; - char dststr[16], gatewaystr[16]; + 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 }; - extract_route(msg, bytes, &index, &dst, &gateway); + extract_ipv4_route(msg, bytes, &index, &dst, &gateway); - inet_ntop(family, &dst, dststr, sizeof(dststr)); - inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); + inet_ntop(family, &dst, dststr, sizeof(dststr)); + inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr)); - __connman_ipconfig_delroute(index, family, scope, dststr, gatewaystr); + __connman_ipconfig_delroute(index, family, scope, dststr, + gatewaystr); - /* skip host specific routes */ - if (scope != RT_SCOPE_UNIVERSE && + /* skip host specific routes */ + if (scope != RT_SCOPE_UNIVERSE && !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY)) - return; + return; + + if (dst.s_addr != INADDR_ANY) + return; + + } else if (family == AF_INET6) { + struct in6_addr dst = IN6ADDR_ANY_INIT, + gateway = IN6ADDR_ANY_INIT; - if (dst.s_addr != INADDR_ANY) + extract_ipv6_route(msg, bytes, &index, &dst, &gateway); + + 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) { @@ -859,6 +1064,9 @@ static void rtnl_newlink(struct nlmsghdr *hdr) rtnl_link(hdr); + if (hdr->nlmsg_type == IFLA_WIRELESS) + connman_warn_once("Obsolete WEXT WiFi driver detected"); + process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags, msg->ifi_change, msg, IFA_PAYLOAD(hdr)); } @@ -986,20 +1194,22 @@ static void rtnl_route(struct nlmsghdr *hdr) } } -static connman_bool_t is_route_rtmsg(struct rtmsg *msg) +static bool is_route_rtmsg(struct rtmsg *msg) { + if (msg->rtm_flags & RTM_F_CLONED) + return false; if (msg->rtm_table != RT_TABLE_MAIN) - return FALSE; + return false; if (msg->rtm_protocol != RTPROT_BOOT && msg->rtm_protocol != RTPROT_KERNEL) - return FALSE; + return false; if (msg->rtm_type != RTN_UNICAST) - return FALSE; + return false; - return TRUE; + return true; } static void rtnl_newroute(struct nlmsghdr *hdr) @@ -1024,61 +1234,183 @@ static void rtnl_delroute(struct nlmsghdr *hdr) msg, RTM_PAYLOAD(hdr)); } -static void rtnl_nd_opt_rdnss(char *interface, struct nd_opt_hdr *opt) +static void *rtnl_nd_opt_rdnss(struct nd_opt_hdr *opt, guint32 *lifetime, + int *nr_servers) { guint32 *optint = (void *)opt; - guint32 lifetime; - char buf[80]; - int i; if (opt->nd_opt_len < 3) - return; + return NULL; - lifetime = ntohl(optint[1]); - if (!lifetime) - return; + 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; + } - optint += 2; - for (i = 0; i < opt->nd_opt_len / 2; i++, optint += 4) { - if (inet_ntop(AF_INET6, optint, buf, sizeof(buf))) - connman_resolver_append_lifetime(interface, NULL, - buf, lifetime); + tmp = i; + i += optc[i] + 1; + + if (i >= data_len) { + DBG("DNSSL data overflows option length"); + return NULL; + } + + optc[tmp] = '.'; } + + 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]; + struct nd_opt_hdr *opt; + guint32 lifetime = -1; + const char **domains = NULL; + struct in6_addr *servers = NULL; + int i, nr_servers = 0; int msglen = msg->nduseropt_opts_len; - char *interface; + int index; - 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); + DBG("family %d index %d len %d type %d code %d", + 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) + msg->nduseropt_icmp_type != ND_ROUTER_ADVERT || + msg->nduseropt_icmp_code != 0) return; - interface = connman_inet_ifname(msg->nduseropt_ifindex); - if (!interface) + index = msg->nduseropt_ifindex; + if (index < 0) + return; + +#if defined TIZEN_EXT + struct connman_service *service; + enum connman_service_state state; + enum connman_dnsconfig_method ipv6_dns_method; + + service = __connman_service_lookup_from_index(index); + if (!service) { + DBG("Invalid service"); return; + } + + DBG("service: %p index: %d\n", service, index); + + if (connman_setting_get_bool("SingleConnectedTechnology") == TRUE) { + state = __connman_service_ipconfig_get_state(service, CONNMAN_IPCONFIG_TYPE_IPV6); + if (state != CONNMAN_SERVICE_STATE_ASSOCIATION && + state != CONNMAN_SERVICE_STATE_CONFIGURATION && + state != CONNMAN_SERVICE_STATE_READY && + state != CONNMAN_SERVICE_STATE_ONLINE) { + DBG("Service state[%d] is not connecting/connected", state); + return; + } + } + + ipv6_dns_method = connman_service_get_ipv6_dns_method(service); + if (ipv6_dns_method != CONNMAN_DNSCONFIG_METHOD_DHCP) { + DBG("IPv6 DNS method is not Auto ignore RA!!! [DNS method: %d]", ipv6_dns_method); + return; + } +#endif 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) { + msglen > 0; + msglen -= opt->nd_opt_len * 8, + opt = ((void *)opt) + opt->nd_opt_len*8) { - DBG("nd opt type %d len %d\n", - opt->nd_opt_type, opt->nd_opt_len); + DBG("remaining %d nd opt type %d len %d\n", + msglen, opt->nd_opt_type, opt->nd_opt_len); - if (opt->nd_opt_type == 25) - rtnl_nd_opt_rdnss(interface, opt); + if (opt->nd_opt_type == 25) { /* ND_OPT_RDNSS */ + char buf[40]; +#if defined TIZEN_EXT + struct connman_service *service; + + service = __connman_service_lookup_from_index(index); + DBG("service: %p\n",service); +#endif + servers = rtnl_nd_opt_rdnss(opt, &lifetime, + &nr_servers); + for (i = 0; i < nr_servers; i++) { + if (!inet_ntop(AF_INET6, servers + i, buf, + sizeof(buf))) + continue; + +#if defined TIZEN_EXT + __connman_service_nameserver_remove(service, + buf, false, + CONNMAN_IPCONFIG_TYPE_IPV6); + __connman_service_nameserver_append(service, + buf, false, + CONNMAN_IPCONFIG_TYPE_IPV6); +#endif + connman_resolver_append_lifetime(index, + NULL, buf, lifetime); + } + + } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */ + g_free(domains); + + domains = rtnl_nd_opt_dnssl(opt, &lifetime); + for (i = 0; domains && domains[i]; i++) + connman_resolver_append_lifetime(index, + domains[i], NULL, lifetime); + } } - g_free(interface); + + g_free(domains); } static const char *type2string(uint16_t type) @@ -1098,6 +1430,8 @@ static const char *type2string(uint16_t type) return "NEWLINK"; case RTM_DELLINK: return "DELLINK"; + case RTM_GETADDR: + return "GETADDR"; case RTM_NEWADDR: return "NEWADDR"; case RTM_DELADDR: @@ -1116,6 +1450,7 @@ static const char *type2string(uint16_t type) } static GIOChannel *channel = NULL; +static guint channel_watch = 0; struct rtnl_request { struct nlmsghdr hdr; @@ -1176,13 +1511,13 @@ static int process_response(guint32 seq) DBG("seq %d", seq); req = find_request(seq); - if (req != NULL) { + if (req) { request_list = g_slist_remove(request_list, req); g_free(req); } req = g_slist_nth_data(request_list, 0); - if (req == NULL) + if (!req) return 0; return send_request(req); @@ -1190,8 +1525,6 @@ static int process_response(guint32 seq) static void rtnl_message(void *buf, size_t len) { - DBG("buf %p len %zd", buf, len); - while (len > 0) { struct nlmsghdr *hdr = buf; struct nlmsgerr *err; @@ -1199,10 +1532,11 @@ static void rtnl_message(void *buf, size_t len) if (!NLMSG_OK(hdr, len)) break; - DBG("%s len %d type %d flags 0x%04x seq %d", + DBG("%s len %u type %u flags 0x%04x seq %u pid %u", type2string(hdr->nlmsg_type), hdr->nlmsg_len, hdr->nlmsg_type, - hdr->nlmsg_flags, hdr->nlmsg_seq); + hdr->nlmsg_flags, hdr->nlmsg_seq, + hdr->nlmsg_pid); switch (hdr->nlmsg_type) { case NLMSG_NOOP: @@ -1244,26 +1578,57 @@ static void rtnl_message(void *buf, size_t len) } } -static gboolean netlink_event(GIOChannel *chan, - GIOCondition cond, gpointer data) +static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data) { unsigned char buf[4096]; - gsize len; - GIOError err; - + struct sockaddr_nl nladdr; + socklen_t addr_len = sizeof(nladdr); + ssize_t status; + int fd; + +#if defined TIZEN_EXT + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) { + __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT); + return FALSE; + } +#else /* TIZEN_EXT */ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; +#endif /* TIZEN_EXT */ memset(buf, 0, sizeof(buf)); + memset(&nladdr, 0, sizeof(nladdr)); + + fd = g_io_channel_unix_get_fd(chan); - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); - if (err) { - if (err == G_IO_ERROR_AGAIN) + status = recvfrom(fd, buf, sizeof(buf), 0, + (struct sockaddr *) &nladdr, &addr_len); + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) return TRUE; + +#if defined TIZEN_EXT + __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT); +#endif /* TIZEN_EXT */ return FALSE; } - rtnl_message(buf, len); +#if defined TIZEN_EXT + if (status == 0) { + __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT); + return FALSE; + } +#else /* TIZEN_EXT */ + if (status == 0) + return FALSE; +#endif /* TIZEN_EXT */ + + if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */ + DBG("Received msg from %u, ignoring it", nladdr.nl_pid); + return TRUE; + } + + rtnl_message(buf, status); return TRUE; } @@ -1275,7 +1640,7 @@ static int send_getlink(void) DBG(""); req = g_try_malloc0(RTNL_REQUEST_SIZE); - if (req == NULL) + if (!req) return -ENOMEM; req->hdr.nlmsg_len = RTNL_REQUEST_SIZE; @@ -1295,7 +1660,7 @@ static int send_getaddr(void) DBG(""); req = g_try_malloc0(RTNL_REQUEST_SIZE); - if (req == NULL) + if (!req) return -ENOMEM; req->hdr.nlmsg_len = RTNL_REQUEST_SIZE; @@ -1315,7 +1680,7 @@ static int send_getroute(void) DBG(""); req = g_try_malloc0(RTNL_REQUEST_SIZE); - if (req == NULL) + if (!req) return -ENOMEM; req->hdr.nlmsg_len = RTNL_REQUEST_SIZE; @@ -1386,7 +1751,7 @@ unsigned int __connman_rtnl_update_interval_remove(unsigned int interval) update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval)); - if (update_list != NULL) + if (update_list) min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0)); if (min > update_interval) @@ -1400,19 +1765,34 @@ int __connman_rtnl_request_update(void) return send_getlink(); } +#if defined TIZEN_EXT +int __connman_rtnl_init(int retry_count) +#else /* TIZEN_EXT */ int __connman_rtnl_init(void) +#endif /* TIZEN_EXT */ { struct sockaddr_nl addr; int sk; +#if defined TIZEN_EXT + if (retry_count < 0) + return -1; + + DBG("retry_count %d", retry_count); +#else /* TIZEN_EXT */ DBG(""); +#endif /* TIZEN_EXT */ interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_interface); - sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE); if (sk < 0) +#if defined TIZEN_EXT + return __connman_rtnl_init(retry_count - 1); +#else /* TIZEN_EXT */ return -1; +#endif /* TIZEN_EXT */ memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; @@ -1422,14 +1802,22 @@ int __connman_rtnl_init(void) if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(sk); +#if defined TIZEN_EXT + return __connman_rtnl_init(retry_count - 1); +#else /* TIZEN_EXT */ return -1; +#endif /* TIZEN_EXT */ } channel = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(channel, TRUE); - g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, - netlink_event, NULL); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + channel_watch = g_io_add_watch(channel, + G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + netlink_event, NULL); return 0; } @@ -1479,6 +1867,11 @@ void __connman_rtnl_cleanup(void) g_slist_free(request_list); request_list = NULL; + if (channel_watch) { + g_source_remove(channel_watch); + channel_watch = 0; + } + g_io_channel_shutdown(channel, TRUE, NULL); g_io_channel_unref(channel);