X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Frtnl.c;h=4f5021958c52b19557b59401c212b799d1578e1d;hb=0e9992014e61671dc81685b7ce87c2da39b877fc;hp=5c0fa360634850531d62565fd9485247ad98312a;hpb=1fdc889f14ad169adce8cdfd624a56c260ac7053;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/rtnl.c b/src/rtnl.c index 5c0fa36..4f50219 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-2012 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,16 +23,20 @@ #include #endif +#include #include #include #include #include +#include #include #include +#include #include #include #include #include +#include #include @@ -42,7 +46,7 @@ #define ARPHDR_PHONET_PIPE (821) #endif -#define print(arg...) do { } while (0) +#define print(arg...) do { if (0) connman_info(arg); } while (0) //#define print(arg...) connman_info(arg) struct watch_data { @@ -63,7 +67,8 @@ struct interface_data { int index; char *name; char *ident; - enum connman_service_type type; + enum connman_service_type service_type; + enum connman_device_type device_type; }; static GHashTable *interface_list = NULL; @@ -72,7 +77,7 @@ static void free_interface(gpointer data) { struct interface_data *interface = data; - __connman_technology_remove_interface(interface->type, + __connman_technology_remove_interface(interface->service_type, interface->index, interface->name, interface->ident); g_free(interface->ident); @@ -100,15 +105,41 @@ static connman_bool_t ether_blacklisted(const char *name) return FALSE; } +static connman_bool_t wext_interface(char *ifname) +{ + struct iwreq wrq; + int fd, err; + + fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 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 read_uevent(struct interface_data *interface) { char *filename, line[128]; + connman_bool_t found_devtype; FILE *f; - if (ether_blacklisted(interface->name) == TRUE) - interface->type = CONNMAN_SERVICE_TYPE_UNKNOWN; - else - interface->type = CONNMAN_SERVICE_TYPE_ETHERNET; + 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); @@ -120,6 +151,7 @@ static void read_uevent(struct interface_data *interface) if (f == NULL) return; + found_devtype = FALSE; while (fgets(line, sizeof(line), f)) { char *pos; @@ -131,19 +163,55 @@ static void read_uevent(struct interface_data *interface) if (strncmp(line, "DEVTYPE=", 8) != 0) continue; - if (strcmp(line + 8, "wlan") == 0) - interface->type = CONNMAN_SERVICE_TYPE_WIFI; - else if (strcmp(line + 8, "wwan") == 0) - interface->type = CONNMAN_SERVICE_TYPE_CELLULAR; - else if (strcmp(line + 8, "bluetooth") == 0) - interface->type = CONNMAN_SERVICE_TYPE_BLUETOOTH; - else if (strcmp(line + 8, "wimax") == 0) - interface->type = CONNMAN_SERVICE_TYPE_WIMAX; - else - interface->type = CONNMAN_SERVICE_TYPE_UNKNOWN; + 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); + } +} + +enum connman_device_type __connman_rtnl_get_device_type(int index) +{ + struct interface_data *interface; + + interface = g_hash_table_lookup(interface_list, + GINT_TO_POINTER(index)); + if (interface == NULL) + return CONNMAN_DEVICE_TYPE_UNKNOWN; + + return interface->device_type; } /** @@ -176,7 +244,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); @@ -216,14 +284,16 @@ 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) rtnl->newgateway(index, gateway); @@ -339,6 +409,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags, 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]; @@ -382,10 +453,8 @@ static void process_newlink(unsigned short type, int index, unsigned flags, ifname, index, operstate, operstate2str(operstate)); - if (g_hash_table_lookup(interface_list, - GINT_TO_POINTER(index)) == NULL) { - struct interface_data *interface; - + 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); @@ -397,7 +466,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags, if (type == ARPHRD_ETHER) read_uevent(interface); - __connman_technology_add_interface(interface->type, + __connman_technology_add_interface(interface->service_type, interface->index, interface->name, interface->ident); } @@ -512,9 +581,6 @@ static void process_newaddr(unsigned char family, unsigned char prefixlen, 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 }; @@ -528,6 +594,8 @@ static void process_newaddr(unsigned char family, unsigned char prefixlen, return; src = &ipv6_address; + } else { + return; } if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) @@ -535,6 +603,20 @@ static void process_newaddr(unsigned char family, unsigned char prefixlen, __connman_ipconfig_newaddr(index, family, label, prefixlen, ip_string); + + 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. + */ + char *interface = connman_inet_ifname(index); + + __connman_resolver_redo_servers(interface); + + g_free(interface); + } } static void process_deladdr(unsigned char family, unsigned char prefixlen, @@ -544,9 +626,6 @@ static void process_deladdr(unsigned char family, unsigned char prefixlen, 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 }; @@ -560,6 +639,8 @@ static void process_deladdr(unsigned char family, unsigned char prefixlen, return; src = &ipv6_address; + } else { + return; } if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL) @@ -569,7 +650,7 @@ static void process_deladdr(unsigned char family, unsigned char prefixlen, 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) { @@ -594,30 +675,79 @@ static void extract_route(struct rtmsg *msg, int bytes, int *index, } } +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_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) { @@ -632,26 +762,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) + 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)); + + __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) { @@ -958,6 +1111,142 @@ static void rtnl_delroute(struct nlmsghdr *hdr) 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] = '.'; + } + + 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; + 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; + + 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) + return; + + interface = connman_inet_ifname(msg->nduseropt_ifindex); + if (!interface) + return; + + for (opt = (void *)&msg[1]; + msglen > 0; + msglen -= opt->nd_opt_len * 8, + opt = ((void *)opt) + opt->nd_opt_len*8) { + + 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) { /* ND_OPT_RDNSS */ + char buf[40]; + + 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; + + connman_resolver_append_lifetime(interface, + 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 != NULL && domains[i] != NULL; i++) + connman_resolver_append_lifetime(interface, + domains[i], NULL, lifetime); + } + } + + g_free(domains); + g_free(interface); +} + static const char *type2string(uint16_t type) { switch (type) { @@ -985,6 +1274,8 @@ static const char *type2string(uint16_t type) return "NEWROUTE"; case RTM_DELROUTE: return "DELROUTE"; + case RTM_NEWNDUSEROPT: + return "NEWNDUSEROPT"; default: return "UNKNOWN"; } @@ -1074,10 +1365,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 %d type %d flags 0x%04x seq %d pid %d", 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: @@ -1109,6 +1401,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; @@ -1120,22 +1415,37 @@ 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 (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; memset(buf, 0, sizeof(buf)); + memset(&nladdr, 0, sizeof(nladdr)); - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); - if (err) { - if (err == G_IO_ERROR_AGAIN) + fd = g_io_channel_unix_get_fd(chan); + + status = recvfrom(fd, buf, sizeof(buf), 0, + (struct sockaddr *) &nladdr, &addr_len); + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) return TRUE; + + return FALSE; + } + + if (status == 0) return FALSE; + + 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, len); + rtnl_message(buf, status); return TRUE; } @@ -1282,14 +1592,15 @@ int __connman_rtnl_init(void) 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) return -1; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | - RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; + RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE | + (1<<(RTNLGRP_ND_USEROPT-1)); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(sk); @@ -1299,6 +1610,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);