X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Finet.c;h=25e537273852986a2dafb67b5233ef7388ef8805;hb=d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302;hp=12c87e75c465bb00e9a5697e66290b82122b0acb;hpb=3973603a07be70d0a5a0dfda88d71bc9252a9c0a;p=platform%2Fupstream%2Fconnman.git diff --git a/src/inet.c b/src/inet.c index 12c87e7..25e5372 100644 --- a/src/inet.c +++ b/src/inet.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. * Copyright (C) 2003-2005 Go-Core Project * Copyright (C) 2003-2006 Helsinki University of Technology * @@ -25,6 +25,7 @@ #include #endif +#define _GNU_SOURCE #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +44,19 @@ #include #include #include +#include +#include +#include #include "connman.h" +#include #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((uint8_t*) (nmsg)) + \ NLMSG_ALIGN((nmsg)->nlmsg_len))) -static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, - const void *data, size_t data_length) +int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length, + int type, const void *data, size_t data_length) { size_t length; struct rtattr *rta; @@ -88,13 +94,15 @@ int __connman_inet_modify_address(int cmd, int flags, struct in_addr ipv4_addr, ipv4_dest, ipv4_bcast; int sk, err; - DBG(""); + DBG("cmd %#x flags %#x index %d family %d address %s peer %s " + "prefixlen %hhu broadcast %s", cmd, flags, index, family, + address, peer, prefixlen, broadcast); - if (address == NULL) - return -1; + if (!address) + return -EINVAL; if (family != AF_INET && family != AF_INET6) - return -1; + return -EINVAL; memset(&request, 0, sizeof(request)); @@ -115,42 +123,57 @@ int __connman_inet_modify_address(int cmd, int flags, if (inet_pton(AF_INET, address, &ipv4_addr) < 1) return -1; - if (broadcast != NULL) + if (broadcast) inet_pton(AF_INET, broadcast, &ipv4_bcast); else ipv4_bcast.s_addr = ipv4_addr.s_addr | htonl(0xfffffffflu >> prefixlen); - if (peer != NULL) { + if (peer) { if (inet_pton(AF_INET, peer, &ipv4_dest) < 1) return -1; - if ((err = add_rtattr(header, sizeof(request), - IFA_ADDRESS, - &ipv4_dest, sizeof(ipv4_dest))) < 0) - return err; + err = __connman_inet_rtnl_addattr_l(header, + sizeof(request), + IFA_ADDRESS, + &ipv4_dest, + sizeof(ipv4_dest)); + if (err < 0) + return err; } - if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL, - &ipv4_addr, sizeof(ipv4_addr))) < 0) + err = __connman_inet_rtnl_addattr_l(header, + sizeof(request), + IFA_LOCAL, + &ipv4_addr, + sizeof(ipv4_addr)); + if (err < 0) return err; - if ((err = add_rtattr(header, sizeof(request), IFA_BROADCAST, - &ipv4_bcast, sizeof(ipv4_bcast))) < 0) + err = __connman_inet_rtnl_addattr_l(header, + sizeof(request), + IFA_BROADCAST, + &ipv4_bcast, + sizeof(ipv4_bcast)); + if (err < 0) return err; } else if (family == AF_INET6) { if (inet_pton(AF_INET6, address, &ipv6_addr) < 1) return -1; - if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL, - &ipv6_addr, sizeof(ipv6_addr))) < 0) + err = __connman_inet_rtnl_addattr_l(header, + sizeof(request), + IFA_LOCAL, + &ipv6_addr, + sizeof(ipv6_addr)); + if (err < 0) return err; } - sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + sk = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE); if (sk < 0) - return -1; + return -errno; memset(&nl_addr, 0, sizeof(nl_addr)); nl_addr.nl_family = AF_NETLINK; @@ -167,20 +190,92 @@ done: return err; } +#if defined TIZEN_EXT_WIFI_MESH +char *connman_inet_ifaddr(const char *name) +{ + struct ifreq ifr; + struct ether_addr eth; + char *str; + int sk, err; + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return NULL; + + strncpy(ifr.ifr_name, name, IFNAMSIZ-1); + + err = ioctl(sk, SIOCGIFHWADDR, &ifr); + close(sk); + + if (err < 0) + return NULL; + + str = g_malloc(18); + if (!str) + return NULL; + + memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); + snprintf(str, 13, "%02x%02x%02x%02x%02x%02x", + eth.ether_addr_octet[0], + eth.ether_addr_octet[1], + eth.ether_addr_octet[2], + eth.ether_addr_octet[3], + eth.ether_addr_octet[4], + eth.ether_addr_octet[5]); + + return str; +} + +char *connman_inet_ifname2addr(const char *name) +{ + struct ifreq ifr; + struct ether_addr eth; + char *str; + int sk, err; + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return NULL; + + strncpy(ifr.ifr_name, name, IFNAMSIZ-1); + + err = ioctl(sk, SIOCGIFHWADDR, &ifr); + close(sk); + + if (err < 0) + return NULL; + + str = g_malloc(18); + if (!str) + return NULL; + + memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); + snprintf(str, 18, "%02x:%02x:%02x:%02x:%02x:%02x", + eth.ether_addr_octet[0], + eth.ether_addr_octet[1], + eth.ether_addr_octet[2], + eth.ether_addr_octet[3], + eth.ether_addr_octet[4], + eth.ether_addr_octet[5]); + + return str; +} +#endif + int connman_inet_ifindex(const char *name) { struct ifreq ifr; int sk, err; - if (name == NULL) + if (!name) return -1; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1); err = ioctl(sk, SIOCGIFINDEX, &ifr); @@ -200,7 +295,7 @@ char *connman_inet_ifname(int index) if (index < 0) return NULL; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return NULL; @@ -214,37 +309,7 @@ char *connman_inet_ifname(int index) if (err < 0) return NULL; - return strdup(ifr.ifr_name); -} - -short int connman_inet_ifflags(int index) -{ - struct ifreq ifr; - int sk, err; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -errno; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - err = -errno; - goto done; - } - - if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { - err = -errno; - goto done; - } - - err = ifr.ifr_flags; - -done: - close(sk); - - return err; + return g_strdup(ifr.ifr_name); } int connman_inet_ifup(int index) @@ -252,7 +317,7 @@ int connman_inet_ifup(int index) struct ifreq ifr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -274,7 +339,7 @@ int connman_inet_ifup(int index) goto done; } - ifr.ifr_flags |= IFF_UP; + ifr.ifr_flags |= (IFF_UP|IFF_DYNAMIC); if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) { err = -errno; @@ -291,10 +356,11 @@ done: int connman_inet_ifdown(int index) { - struct ifreq ifr; + struct ifreq ifr, addr_ifr; + struct sockaddr_in *addr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -311,12 +377,19 @@ int connman_inet_ifdown(int index) goto done; } + memset(&addr_ifr, 0, sizeof(addr_ifr)); + memcpy(&addr_ifr.ifr_name, &ifr.ifr_name, sizeof(ifr.ifr_name) - 1); + addr = (struct sockaddr_in *)&addr_ifr.ifr_addr; + addr->sin_family = AF_INET; + if (ioctl(sk, SIOCSIFADDR, &addr_ifr) < 0) + connman_warn("Could not clear IPv4 address index %d", index); + if (!(ifr.ifr_flags & IFF_UP)) { err = -EALREADY; goto done; } - ifr.ifr_flags &= ~IFF_UP; + ifr.ifr_flags = (ifr.ifr_flags & ~IFF_UP) | IFF_DYNAMIC; if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) err = -errno; @@ -329,208 +402,45 @@ done: return err; } -static char *index2addr(int index) -{ - struct ifreq ifr; - struct ether_addr eth; - char *str; - int sk, err; - - if (index < 0) - return NULL; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return NULL; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - err = ioctl(sk, SIOCGIFNAME, &ifr); - - if (err == 0) - err = ioctl(sk, SIOCGIFHWADDR, &ifr); - - close(sk); - - if (err < 0) - return NULL; - - str = malloc(18); - if (!str) - return NULL; - - memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); - snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X", - eth.ether_addr_octet[0], - eth.ether_addr_octet[1], - eth.ether_addr_octet[2], - eth.ether_addr_octet[3], - eth.ether_addr_octet[4], - eth.ether_addr_octet[5]); - - return str; -} - -static char *index2ident(int index, const char *prefix) -{ - struct ifreq ifr; - struct ether_addr eth; - char *str; - int sk, err, len; - - if (index < 0) - return NULL; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return NULL; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - err = ioctl(sk, SIOCGIFNAME, &ifr); - - if (err == 0) - err = ioctl(sk, SIOCGIFHWADDR, &ifr); - - close(sk); - - if (err < 0) - return NULL; - - len = prefix ? strlen(prefix) + 18 : 18; - - str = malloc(len); - if (!str) - return NULL; - - memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); - snprintf(str, len, "%s%02x%02x%02x%02x%02x%02x", - prefix ? prefix : "", - eth.ether_addr_octet[0], - eth.ether_addr_octet[1], - eth.ether_addr_octet[2], - eth.ether_addr_octet[3], - eth.ether_addr_octet[4], - eth.ether_addr_octet[5]); - - return str; -} - -connman_bool_t connman_inet_is_cfg80211(int index) -{ - connman_bool_t result = FALSE; - char phy80211_path[PATH_MAX]; - struct stat st; - struct ifreq ifr; - int sk; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return FALSE; - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) - goto done; - - snprintf(phy80211_path, PATH_MAX, - "/sys/class/net/%s/phy80211", ifr.ifr_name); - - if (stat(phy80211_path, &st) == 0 && (st.st_mode & S_IFDIR)) - result = TRUE; - -done: - close(sk); - - return result; -} - -struct connman_device *connman_inet_create_device(int index) +#if defined TIZEN_EXT +void connman_inet_update_device_ident(struct connman_device *device) { + int index; enum connman_device_type type; - struct connman_device *device; - char *devname, *ident = NULL; - char *addr = NULL, *name = NULL; - - if (index < 0) - return NULL; - - devname = connman_inet_ifname(index); - if (devname == NULL) - return NULL; + char *ident = NULL, *addr = NULL; - if (__connman_device_isfiltered(devname) == TRUE) { - connman_info("Ignoring interface %s (filtered)", devname); - free(devname); - return NULL; - } - - type = __connman_rtnl_get_device_type(index); + index = connman_device_get_index(device); + type = connman_device_get_type(device); switch (type) { case CONNMAN_DEVICE_TYPE_UNKNOWN: - connman_info("Ignoring interface %s (type unknown)", devname); - free(devname); - return NULL; + return; case CONNMAN_DEVICE_TYPE_ETHERNET: case CONNMAN_DEVICE_TYPE_GADGET: case CONNMAN_DEVICE_TYPE_WIFI: - case CONNMAN_DEVICE_TYPE_WIMAX: - name = index2ident(index, ""); addr = index2addr(index); - break; - case CONNMAN_DEVICE_TYPE_BLUETOOTH: - case CONNMAN_DEVICE_TYPE_CELLULAR: - case CONNMAN_DEVICE_TYPE_GPS: - case CONNMAN_DEVICE_TYPE_VENDOR: - name = strdup(devname); - break; - } - - device = connman_device_create(name, type); - if (device == NULL) - goto done; - - switch (type) { - case CONNMAN_DEVICE_TYPE_UNKNOWN: - case CONNMAN_DEVICE_TYPE_VENDOR: - case CONNMAN_DEVICE_TYPE_GPS: - break; - case CONNMAN_DEVICE_TYPE_ETHERNET: - case CONNMAN_DEVICE_TYPE_GADGET: ident = index2ident(index, NULL); break; - case CONNMAN_DEVICE_TYPE_WIFI: - case CONNMAN_DEVICE_TYPE_WIMAX: + case CONNMAN_DEVICE_TYPE_CELLULAR: ident = index2ident(index, NULL); break; case CONNMAN_DEVICE_TYPE_BLUETOOTH: - break; - case CONNMAN_DEVICE_TYPE_CELLULAR: - ident = index2ident(index, NULL); + case CONNMAN_DEVICE_TYPE_GPS: + case CONNMAN_DEVICE_TYPE_VENDOR: break; } - connman_device_set_index(device, index); - connman_device_set_interface(device, devname); - if (ident != NULL) { connman_device_set_ident(device, ident); - free(ident); + g_free(ident); } - connman_device_set_string(device, "Address", addr); - -done: - free(devname); - free(name); - free(addr); - - return device; + if (addr != NULL) { + connman_device_set_string(device, "Address", addr); + g_free(addr); + } } +#endif struct in6_ifreq { struct in6_addr ifr6_addr; @@ -541,10 +451,11 @@ struct in6_ifreq { int connman_inet_set_ipv6_address(int index, struct connman_ipaddress *ipaddress) { + int err; unsigned char prefix_len; const char *address; - if (ipaddress->local == NULL) + if (!ipaddress->local) return 0; prefix_len = ipaddress->prefixlen; @@ -552,11 +463,12 @@ int connman_inet_set_ipv6_address(int index, DBG("index %d address %s prefix_len %d", index, address, prefix_len); - if ((__connman_inet_modify_address(RTM_NEWADDR, - NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6, - address, NULL, prefix_len, NULL)) < 0) { - connman_error("Set IPv6 address error"); - return -1; + err = __connman_inet_modify_address(RTM_NEWADDR, + NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6, + address, NULL, prefix_len, NULL); + if (err < 0) { + connman_error("%s: %s", __func__, strerror(-err)); + return err; } return 0; @@ -564,10 +476,11 @@ int connman_inet_set_ipv6_address(int index, int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) { + int err; unsigned char prefix_len; const char *address, *broadcast, *peer; - if (ipaddress->local == NULL) + if (!ipaddress->local) return -1; prefix_len = ipaddress->prefixlen; @@ -577,11 +490,12 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) DBG("index %d address %s prefix_len %d", index, address, prefix_len); - if ((__connman_inet_modify_address(RTM_NEWADDR, - NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, - address, peer, prefix_len, broadcast)) < 0) { - DBG("address setting failed"); - return -1; + err = __connman_inet_modify_address(RTM_NEWADDR, + NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, + address, peer, prefix_len, broadcast); + if (err < 0) { + connman_error("%s: %s", __func__, strerror(-err)); + return err; } return 0; @@ -590,12 +504,18 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) int connman_inet_clear_ipv6_address(int index, const char *address, int prefix_len) { + int err; + DBG("index %d address %s prefix_len %d", index, address, prefix_len); - if ((__connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6, - address, NULL, prefix_len, NULL)) < 0) { - connman_error("Clear IPv6 address error"); - return -1; + if (!address) + return -EINVAL; + + err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6, + address, NULL, prefix_len, NULL); + if (err < 0) { + connman_error("%s: %s", __func__, strerror(-err)); + return err; } return 0; @@ -603,6 +523,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address, int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress) { + int err; unsigned char prefix_len; const char *address, *broadcast, *peer; @@ -611,12 +532,17 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress) broadcast = ipaddress->broadcast; peer = ipaddress->peer; - DBG("index %d address %s prefix_len %d", index, address, prefix_len); + DBG("index %d address %s prefix_len %d peer %s broadcast %s", index, + address, prefix_len, peer, broadcast); - if ((__connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET, - address, peer, prefix_len, broadcast)) < 0) { - DBG("address removal failed"); - return -1; + if (!address) + return -EINVAL; + + err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET, + address, peer, prefix_len, broadcast); + if (err < 0) { + connman_error("%s: %s", __func__, strerror(-err)); + return err; } return 0; @@ -640,27 +566,33 @@ int connman_inet_add_network_route(int index, const char *host, struct ifreq ifr; struct rtentry rt; struct sockaddr_in addr; - int sk, err; + int sk, err = 0; - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + DBG("index %d host %s gateway %s netmask %s", index, + host, gateway, netmask); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); memset(&rt, 0, sizeof(rt)); rt.rt_flags = RTF_UP; - if (gateway != NULL) + if (gateway) rt.rt_flags |= RTF_GATEWAY; - if (netmask == NULL) + if (!netmask) rt.rt_flags |= RTF_HOST; memset(&addr, 0, sizeof(addr)); @@ -670,7 +602,7 @@ int connman_inet_add_network_route(int index, const char *host, memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - if (gateway != NULL) + if (gateway) addr.sin_addr.s_addr = inet_addr(gateway); else addr.sin_addr.s_addr = INADDR_ANY; @@ -679,7 +611,7 @@ int connman_inet_add_network_route(int index, const char *host, memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; - if (netmask != NULL) + if (netmask) addr.sin_addr.s_addr = inet_addr(netmask); else addr.sin_addr.s_addr = INADDR_ANY; @@ -687,13 +619,16 @@ int connman_inet_add_network_route(int index, const char *host, rt.rt_dev = ifr.ifr_name; - err = ioctl(sk, SIOCADDRT, &rt); - if (err < 0) - connman_error("Adding host route failed (%s)", - strerror(errno)); + if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST) + err = -errno; close(sk); +out: + if (err < 0) + connman_error("Adding host route failed (%s)", + strerror(-err)); + return err; } @@ -702,18 +637,23 @@ int connman_inet_del_network_route(int index, const char *host) struct ifreq ifr; struct rtentry rt; struct sockaddr_in addr; - int sk, err; + int sk, err = 0; - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + DBG("index %d host %s", index, host); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); @@ -728,13 +668,16 @@ int connman_inet_del_network_route(int index, const char *host) rt.rt_dev = ifr.ifr_name; - err = ioctl(sk, SIOCDELRT, &rt); - if (err < 0) - connman_error("Deleting host route failed (%s)", - strerror(errno)); + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; close(sk); +out: + if (err < 0) + connman_error("Deleting host route failed (%s)", + strerror(-err)); + return err; } @@ -742,37 +685,42 @@ int connman_inet_del_ipv6_network_route(int index, const char *host, unsigned char prefix_len) { struct in6_rtmsg rt; - int sk, err; + int sk, err = 0; DBG("index %d host %s", index, host); - if (host == NULL) + if (!host) return -EINVAL; memset(&rt, 0, sizeof(rt)); rt.rtmsg_dst_len = prefix_len; - err = inet_pton(AF_INET6, host, &rt.rtmsg_dst); - if (err < 0) + if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) { + err = -errno; goto out; + } rt.rtmsg_flags = RTF_UP | RTF_HOST; rt.rtmsg_metric = 1; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { - err = -1; + err = -errno; goto out; } - err = ioctl(sk, SIOCDELRT, &rt); + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; + close(sk); + out: if (err < 0) - connman_error("Del IPv6 host route error"); + connman_error("Del IPv6 host route error (%s)", + strerror(-err)); return err; } @@ -784,27 +732,28 @@ int connman_inet_del_ipv6_host_route(int index, const char *host) int connman_inet_add_ipv6_network_route(int index, const char *host, const char *gateway, - unsigned char prefix_len) + unsigned char prefix_len) { struct in6_rtmsg rt; - int sk, err; + int sk, err = 0; DBG("index %d host %s gateway %s", index, host, gateway); - if (host == NULL) + if (!host) return -EINVAL; memset(&rt, 0, sizeof(rt)); rt.rtmsg_dst_len = prefix_len; - err = inet_pton(AF_INET6, host, &rt.rtmsg_dst); - if (err < 0) + if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) { + err = -errno; goto out; + } rt.rtmsg_flags = RTF_UP | RTF_HOST; - if (gateway != NULL) { + if (gateway) { rt.rtmsg_flags |= RTF_GATEWAY; inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); } @@ -812,17 +761,21 @@ int connman_inet_add_ipv6_network_route(int index, const char *host, rt.rtmsg_metric = 1; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { - err = -1; + err = -errno; goto out; } - err = ioctl(sk, SIOCADDRT, &rt); + if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST) + err = -errno; + close(sk); + out: if (err < 0) - connman_error("Set IPv6 host route error"); + connman_error("Set IPv6 host route error (%s)", + strerror(-err)); return err; } @@ -833,146 +786,122 @@ int connman_inet_add_ipv6_host_route(int index, const char *host, return connman_inet_add_ipv6_network_route(index, host, gateway, 128); } -int connman_inet_set_ipv6_gateway_address(int index, const char *gateway) -{ - struct in6_rtmsg rt; - int sk, err; - - DBG("index %d, gateway %s", index, gateway); - - if (gateway == NULL) - return -EINVAL; - - memset(&rt, 0, sizeof(rt)); - - err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); - if (err < 0) - goto out; - - rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; - rt.rtmsg_metric = 1; - rt.rtmsg_dst_len = 0; - rt.rtmsg_ifindex = index; - - sk = socket(AF_INET6, SOCK_DGRAM, 0); - if (sk < 0) { - err = -1; - goto out; - } - - err = ioctl(sk, SIOCADDRT, &rt); - close(sk); -out: - if (err < 0) - connman_error("Set default IPv6 gateway error"); - - return err; -} - int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway) { struct in6_rtmsg rt; - int sk, err; + int sk, err = 0; - DBG("index %d, gateway %s", index, gateway); + DBG("index %d gateway %s", index, gateway); - if (gateway == NULL) + if (!gateway) return -EINVAL; memset(&rt, 0, sizeof(rt)); - err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); - if (err < 0) + if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) { + err = -errno; goto out; + } rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; rt.rtmsg_metric = 1; rt.rtmsg_dst_len = 0; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { - err = -1; + err = -errno; goto out; } - err = ioctl(sk, SIOCDELRT, &rt); + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; + close(sk); + out: if (err < 0) - connman_error("Clear default IPv6 gateway error"); + connman_error("Clear default IPv6 gateway error (%s)", + strerror(-err)); return err; } -int connman_inet_set_gateway_address(int index, const char *gateway) +int connman_inet_set_gateway_interface(int index) { struct ifreq ifr; struct rtentry rt; struct sockaddr_in addr; - int sk, err; + int sk, err = 0; - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + DBG("index %d", index); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); memset(&rt, 0, sizeof(rt)); - rt.rt_flags = RTF_UP | RTF_GATEWAY; + rt.rt_flags = RTF_UP; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; - memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst)); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(gateway); + memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); + memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst)); memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway)); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); + rt.rt_dev = ifr.ifr_name; - err = ioctl(sk, SIOCADDRT, &rt); - if (err < 0) - connman_error("Setting default gateway route failed (%s)", - strerror(errno)); + if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST) + err = -errno; close(sk); +out: + if (err < 0) + connman_error("Setting default interface route failed (%s)", + strerror(-err)); + return err; } -int connman_inet_set_gateway_interface(int index) +int connman_inet_set_ipv6_gateway_interface(int index) { struct ifreq ifr; struct rtentry rt; - struct sockaddr_in addr; - int sk, err; + struct sockaddr_in6 addr; + const struct in6_addr any = IN6ADDR_ANY_INIT; + int sk, err = 0; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); @@ -981,8 +910,8 @@ int connman_inet_set_gateway_interface(int index) rt.rt_flags = RTF_UP; memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + addr.sin6_family = AF_INET6; + addr.sin6_addr = any; memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst)); @@ -990,11 +919,15 @@ int connman_inet_set_gateway_interface(int index) rt.rt_dev = ifr.ifr_name; - err = ioctl(sk, SIOCADDRT, &rt); + if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST) + err = -errno; + + close(sk); + +out: if (err < 0) connman_error("Setting default interface route failed (%s)", - strerror(errno)); - close(sk); + strerror(-err)); return err; } @@ -1004,20 +937,23 @@ int connman_inet_clear_gateway_address(int index, const char *gateway) struct ifreq ifr; struct rtentry rt; struct sockaddr_in addr; - int sk, err; + int sk, err = 0; - DBG(""); + DBG("index %d gateway %s", index, gateway); - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); @@ -1040,13 +976,16 @@ int connman_inet_clear_gateway_address(int index, const char *gateway) addr.sin_addr.s_addr = INADDR_ANY; memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); - err = ioctl(sk, SIOCDELRT, &rt); - if (err < 0) - connman_error("Removing default gateway route failed (%s)", - strerror(errno)); + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; close(sk); +out: + if (err < 0) + connman_error("Removing default gateway route failed (%s)", + strerror(-err)); + return err; } @@ -1055,20 +994,23 @@ int connman_inet_clear_gateway_interface(int index) struct ifreq ifr; struct rtentry rt; struct sockaddr_in addr; - int sk, err; + int sk, err = 0; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; close(sk); - return -1; + goto out; } DBG("ifname %s", ifr.ifr_name); @@ -1086,16 +1028,73 @@ int connman_inet_clear_gateway_interface(int index) rt.rt_dev = ifr.ifr_name; - err = ioctl(sk, SIOCDELRT, &rt); + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; + + close(sk); + +out: if (err < 0) connman_error("Removing default interface route failed (%s)", - strerror(errno)); + strerror(-err)); + + return err; +} + +int connman_inet_clear_ipv6_gateway_interface(int index) +{ + struct ifreq ifr; + struct rtentry rt; + struct sockaddr_in6 addr; + const struct in6_addr any = IN6ADDR_ANY_INIT; + int sk, err = 0; + + DBG("index %d", index); + + sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = index; + + if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + err = -errno; + close(sk); + goto out; + } + + DBG("ifname %s", ifr.ifr_name); + + memset(&rt, 0, sizeof(rt)); + rt.rt_flags = RTF_UP; + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = any; + + memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); + memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst)); + memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway)); + + rt.rt_dev = ifr.ifr_name; + + if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH) + err = -errno; + close(sk); +out: + if (err < 0) + connman_error("Removing default interface route failed (%s)", + strerror(-err)); + return err; } -connman_bool_t connman_inet_compare_subnet(int index, const char *host) +bool connman_inet_compare_subnet(int index, const char *host) { struct ifreq ifr; struct in_addr _host_addr; @@ -1105,28 +1104,28 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host) DBG("host %s", host); - if (host == NULL) - return FALSE; + if (!host) + return false; if (inet_aton(host, &_host_addr) == 0) return -1; host_addr = _host_addr.s_addr; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) - return FALSE; + return false; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = index; if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { close(sk); - return FALSE; + return false; } if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) { close(sk); - return FALSE; + return false; } netmask = (struct sockaddr_in *)&ifr.ifr_netmask; @@ -1134,8 +1133,11 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host) if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) { close(sk); - return FALSE; + return false; } + + close(sk); + addr = (struct sockaddr_in *)&ifr.ifr_addr; if_addr = addr->sin_addr.s_addr; @@ -1145,59 +1147,63 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host) int connman_inet_remove_from_bridge(int index, const char *bridge) { struct ifreq ifr; - int sk, err; + int sk, err = 0; - if (bridge == NULL) + if (!bridge) return -EINVAL; - sk = socket(AF_INET, SOCK_STREAM, 0); - if (sk < 0) - return sk; + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); + strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1); ifr.ifr_ifindex = index; - err = ioctl(sk, SIOCBRDELIF, &ifr); + if (ioctl(sk, SIOCBRDELIF, &ifr) < 0) + err = -errno; close(sk); - if (err < 0) { +out: + if (err < 0) connman_error("Remove interface from bridge error %s", - strerror(errno)); - return err; - } + strerror(-err)); - return 0; + return err; } int connman_inet_add_to_bridge(int index, const char *bridge) { struct ifreq ifr; - int sk, err; + int sk, err = 0; - if (bridge == NULL) + if (!bridge) return -EINVAL; - sk = socket(AF_INET, SOCK_STREAM, 0); - if (sk < 0) - return sk; + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + err = -errno; + goto out; + } memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); + strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1); ifr.ifr_ifindex = index; - err = ioctl(sk, SIOCBRADDIF, &ifr); + if (ioctl(sk, SIOCBRADDIF, &ifr) < 0) + err = -errno; close(sk); - if (err < 0) { - connman_error("Add interface to bridge error %s", - strerror(errno)); - return err; - } +out: + if (err < 0) + connman_error("Add interface to bridge error %s", + strerror(-err)); - return 0; + return err; } int connman_inet_set_mtu(int index, int mtu) @@ -1205,7 +1211,7 @@ int connman_inet_set_mtu(int index, int mtu) struct ifreq ifr; int sk, err; - sk = socket(AF_INET, SOCK_DGRAM, 0); + sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; @@ -1229,23 +1235,21 @@ int connman_inet_setup_tunnel(char *tunnel, int mtu) __u32 mask; __u32 flags; - if (tunnel == NULL) + if (!tunnel) return -EINVAL; - sk = socket(AF_INET, SOCK_DGRAM, 0); + sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; index = if_nametoindex(tunnel); err = connman_inet_set_mtu(index, mtu); - if (err < 0) - return err; - else if (err) + if (err != 0) goto done; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, tunnel, IFNAMSIZ); + strncpy(ifr.ifr_name, tunnel, sizeof(ifr.ifr_name) - 1); err = ioctl(sk, SIOCGIFFLAGS, &ifr); if (err) goto done; @@ -1272,7 +1276,7 @@ int connman_inet_create_tunnel(char **iface) struct ifreq ifr; int i, fd; - fd = open("/dev/net/tun", O_RDWR); + fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) { i = -errno; connman_error("Failed to open /dev/net/tun: %s", @@ -1301,11 +1305,16 @@ int connman_inet_create_tunnel(char **iface) return fd; } -struct rs_cb_data { +/* + * This callback struct is used when sending router and neighbor + * solicitation and advertisement messages. + */ +struct xs_cb_data { GIOChannel *channel; - __connman_inet_rs_cb_t callback; + void *callback; struct sockaddr_in6 addr; - guint rs_timeout; + guint timeout; + guint watch_id; void *user_data; }; @@ -1319,51 +1328,52 @@ static const struct in6_addr in6addr_all_nodes_mc = IN6ADDR_ALL_NODES_MC_INIT; static const struct in6_addr in6addr_all_routers_mc = IN6ADDR_ALL_ROUTERS_MC_INIT; -/* from netinet/in.h */ -struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - unsigned int ipi6_ifindex; /* send/recv interface index */ -}; - -static void rs_cleanup(struct rs_cb_data *data) +static void xs_cleanup(struct xs_cb_data *data) { - g_io_channel_shutdown(data->channel, TRUE, NULL); - g_io_channel_unref(data->channel); - data->channel = 0; + if (data->channel) { + g_io_channel_shutdown(data->channel, TRUE, NULL); + g_io_channel_unref(data->channel); + data->channel = NULL; + } + + if (data->timeout > 0) + g_source_remove(data->timeout); - if (data->rs_timeout > 0) - g_source_remove(data->rs_timeout); + if (data->watch_id > 0) + g_source_remove(data->watch_id); g_free(data); } static gboolean rs_timeout_cb(gpointer user_data) { - struct rs_cb_data *data = user_data; + struct xs_cb_data *data = user_data; DBG("user data %p", user_data); - if (data == NULL) + if (!data) return FALSE; - if (data->callback != NULL) - data->callback(NULL, data->user_data); + if (data->callback) { + __connman_inet_rs_cb_t cb = data->callback; + cb(NULL, 0, data->user_data); + } - data->rs_timeout = 0; - rs_cleanup(data); + data->timeout = 0; + xs_cleanup(data); return FALSE; } -static int icmpv6_recv(int fd, gpointer user_data) +static int icmpv6_recv(int fd, struct xs_cb_data *data) { struct msghdr mhdr; struct iovec iov; unsigned char chdr[CMSG_BUF_LEN]; unsigned char buf[1540]; - struct rs_cb_data *data = user_data; struct nd_router_advert *hdr; struct sockaddr_in6 saddr; ssize_t len; + __connman_inet_rs_cb_t cb = data->callback; DBG(""); @@ -1372,6 +1382,7 @@ static int icmpv6_recv(int fd, gpointer user_data) mhdr.msg_name = (void *)&saddr; mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_flags = 0; mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (void *)chdr; @@ -1379,40 +1390,74 @@ static int icmpv6_recv(int fd, gpointer user_data) len = recvmsg(fd, &mhdr, 0); if (len < 0) { - data->callback(NULL, data->user_data); + cb(NULL, 0, data->user_data); return -errno; } +#if defined TIZEN_EXT + /* Set Received Source Address from router as IPv6 Gateway Address */ + char src_addr[INET6_ADDRSTRLEN]; + if(inet_ntop(AF_INET6, &(saddr.sin6_addr), src_addr, INET6_ADDRSTRLEN) + == NULL) + return -errno; + + DBG("Received Source Address %s from router", src_addr); + + /* icmpv6_recv() function can be called in two scenarios : + * 1. When __connman_inet_ipv6_send_rs() is called from check_dhcpv6() + * 2. When __connman_inet_ipv6_send_rs() is called from + * __connman_6to4_probe() + * In the second case it is not required to set DHCPv6 Gateway Address + * as DHCPv6 was not started and network structure was not passed as + * user_data. If it is tried to add Source Address as Gateway Address + * then it will lead to crash because of user_data being ip_address + * instead of network structure. So Adding Gateway Address in case 1st + * case only. + */ + char *address = data->user_data; + int err = 0; + unsigned char buffer[sizeof(struct in6_addr)] = {0, }; + /* Checking if user_data is an ip_address */ + err = inet_pton(AF_INET, address, buffer); + /* Setting Received Source Address from + * router as Gateway Address */ + if(err <= 0) + __connman_network_set_auto_ipv6_gateway(src_addr, data->user_data); +#endif hdr = (struct nd_router_advert *)buf; + DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len, + sizeof(struct nd_router_advert)); if (hdr->nd_ra_code != 0) return 0; - data->callback(hdr, data->user_data); - rs_cleanup(data); + cb(hdr, len, data->user_data); return len; } -static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond, - gpointer data) +static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond, gpointer data) { int fd, ret; + struct xs_cb_data *xs_data = data; DBG(""); if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + goto cleanup; fd = g_io_channel_unix_get_fd(chan); - ret = icmpv6_recv(fd, data); + ret = icmpv6_recv(fd, xs_data); if (ret == 0) return TRUE; - return FALSE; +cleanup: + xs_cleanup(xs_data); + return TRUE; } /* Adapted from RFC 1071 "C" Implementation Example */ -static uint16_t csum(const void *phdr, const void *data, socklen_t datalen) +static uint16_t csum(const void *phdr, const void *data, socklen_t datalen, + const void *extra_data, socklen_t extra_datalen) { register unsigned long sum = 0; socklen_t count; @@ -1433,13 +1478,25 @@ static uint16_t csum(const void *phdr, const void *data, socklen_t datalen) count -= 2; } + if (extra_data) { + count = extra_datalen; + addr = (uint16_t *)extra_data; + + while (count > 1) { + sum += *(addr++); + count -= 2; + } + } + while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return (uint16_t)~sum; } -static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest) +static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest, + const struct in6_addr *source, + unsigned char *buf, size_t len, uint16_t lifetime) { struct _phdr { struct in6_addr src; @@ -1455,55 +1512,84 @@ static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest) struct icmp6_hdr icmp; struct nd_neighbor_solicit ns; struct nd_router_solicit rs; + struct nd_router_advert ra; } i; } frame; struct msghdr msgh; struct cmsghdr *cmsg; struct in6_pktinfo *pinfo; - struct sockaddr_in6 dst; + struct sockaddr_in6 dst, src; char cbuf[CMSG_SPACE(sizeof(*pinfo))]; - struct iovec iov; - int fd, datalen, ret; + struct iovec iov[2]; + int fd, datalen, ret, iovlen = 1; +#if defined TIZEN_EXT + char ebuf[256]; +#endif DBG(""); - fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); if (fd < 0) return -errno; memset(&frame, 0, sizeof(frame)); memset(&dst, 0, sizeof(dst)); - datalen = sizeof(frame.i.rs); /* 8, csum() safe */ + if (type == ND_ROUTER_SOLICIT) + datalen = sizeof(frame.i.rs); /* 8, csum() safe */ + else if (type == ND_ROUTER_ADVERT) { + datalen = sizeof(frame.i.ra); /* 16, csum() safe */ + frame.i.ra.nd_ra_router_lifetime = htons(lifetime); + } else if (type == ND_NEIGHBOR_SOLICIT) { + datalen = sizeof(frame.i.ns); /* 24, csum() safe */ + memcpy(&frame.i.ns.nd_ns_target, buf, sizeof(struct in6_addr)); + } else { + close(fd); + return -EINVAL; + } + dst.sin6_addr = *dest; + if (source) + src.sin6_addr = *source; + else + src.sin6_addr = in6addr_any; + /* Fill in the IPv6 header */ frame.ip.ip6_vfc = 0x60; - frame.ip.ip6_plen = htons(datalen); + frame.ip.ip6_plen = htons(datalen + len); frame.ip.ip6_nxt = IPPROTO_ICMPV6; frame.ip.ip6_hlim = 255; frame.ip.ip6_dst = dst.sin6_addr; + frame.ip.ip6_src = src.sin6_addr; /* all other fields are already set to zero */ /* Prepare pseudo header for csum */ memset(&phdr, 0, sizeof(phdr)); phdr.dst = dst.sin6_addr; - phdr.plen = htonl(datalen); + phdr.src = src.sin6_addr; + phdr.plen = htonl(datalen + len); phdr.nxt = IPPROTO_ICMPV6; /* Fill in remaining ICMP header fields */ frame.i.icmp.icmp6_type = type; - frame.i.icmp.icmp6_cksum = csum(&phdr, &frame.i, datalen); + frame.i.icmp.icmp6_cksum = csum(&phdr, &frame.i, datalen, buf, len); + + iov[0].iov_base = &frame; + iov[0].iov_len = sizeof(frame.ip) + datalen; - iov.iov_base = &frame; - iov.iov_len = sizeof(frame.ip) + datalen; + if (buf) { + iov[1].iov_base = buf; + iov[1].iov_len = len; + iovlen = 2; + } dst.sin6_family = AF_INET6; msgh.msg_name = &dst; msgh.msg_namelen = sizeof(dst); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; + msgh.msg_iov = iov; + msgh.msg_iovlen = iovlen; msgh.msg_flags = 0; memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo))); @@ -1518,6 +1604,9 @@ static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest) msgh.msg_controllen = cmsg->cmsg_len; ret = sendmsg(fd, &msgh, 0); +#if defined TIZEN_EXT + DBG("sendmsg errno: %d/%s", errno, strerror_r(errno, ebuf, sizeof(ebuf))); +#endif close(fd); return ret; @@ -1553,39 +1642,49 @@ static int if_mc_group(int sock, int ifindex, const struct in6_addr *mc_addr, ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(int)); + if (ret < 0) { + ret = -errno; + DBG("Cannot set IPV6_MULTICAST_LOOP %d/%s", ret, + strerror(-ret)); + return ret; + } - if (ret < 0) + ret = setsockopt(sock, IPPROTO_IPV6, cmd, &mreq, sizeof(mreq)); + if (ret < 0) { + ret = -errno; + DBG("Cannot set option %d %d/%s", cmd, ret, strerror(-ret)); return ret; + } - return setsockopt(sock, IPPROTO_IPV6, cmd, &mreq, sizeof(mreq)); + return 0; } int __connman_inet_ipv6_send_rs(int index, int timeout, __connman_inet_rs_cb_t callback, void *user_data) { - struct rs_cb_data *data; + struct xs_cb_data *data; struct icmp6_filter filter; struct in6_addr solicit; struct in6_addr dst = in6addr_all_routers_mc; int sk; - DBG(""); - if (timeout <= 0) return -EINVAL; - data = g_try_malloc0(sizeof(struct rs_cb_data)); - if (data == NULL) + data = g_try_malloc0(sizeof(struct xs_cb_data)); + if (!data) return -ENOMEM; data->callback = callback; data->user_data = user_data; - data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data); + data->timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data); - sk = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); if (sk < 0) return -errno; + DBG("sock %d", sk); + ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); @@ -1602,11 +1701,1717 @@ int __connman_inet_ipv6_send_rs(int index, int timeout, g_io_channel_set_encoding(data->channel, NULL, NULL); g_io_channel_set_buffered(data->channel, FALSE); - g_io_add_watch(data->channel, + data->watch_id = g_io_add_watch(data->channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, icmpv6_event, data); - ndisc_send_unspec(ND_ROUTER_SOLICIT, index, &dst); + ndisc_send_unspec(ND_ROUTER_SOLICIT, index, &dst, NULL, NULL, 0, 0); + + return 0; +} + +static inline void ipv6_addr_advert_mult(const struct in6_addr *addr, + struct in6_addr *advert) +{ + ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2), + htonl(0xFF000000) | addr->s6_addr32[3]); +} + +#define MSG_SIZE_SEND 1452 + +static int inc_len(int len, int inc) +{ + if (len > MSG_SIZE_SEND) + return -EINVAL; + + len += inc; + return len; +} + +int __connman_inet_ipv6_send_ra(int index, struct in6_addr *src_addr, + GSList *prefixes, int router_lifetime) +{ + GSList *list; + struct in6_addr src, *source; + struct in6_addr dst = in6addr_all_nodes_mc; + GDHCPIAPrefix *prefix; + unsigned char buf[MSG_SIZE_SEND]; + char addr_str[INET6_ADDRSTRLEN]; + int sk, err = 0; + int len, count = 0; + + if (!prefixes) + return -EINVAL; + + sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); + if (sk < 0) + return -errno; + + if (!src_addr) { + __connman_inet_get_interface_ll_address(index, AF_INET6, &src); + source = &src; + } else + source = src_addr; + + DBG("sock %d index %d prefixes %p src %s lifetime %d", sk, index, + prefixes, inet_ntop(AF_INET6, source, addr_str, + INET6_ADDRSTRLEN), + router_lifetime); + + memset(buf, 0, MSG_SIZE_SEND); + len = 0; + + for (list = prefixes; list; list = list->next) { + struct nd_opt_prefix_info *pinfo; + + prefix = list->data; + pinfo = (struct nd_opt_prefix_info *)(buf + len); + + len = inc_len(len, sizeof(*pinfo)); + if (len < 0) { + err = len; + goto out; + } + + pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + pinfo->nd_opt_pi_len = 4; + pinfo->nd_opt_pi_prefix_len = prefix->prefixlen; + pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK; + pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; + if (router_lifetime > 0) { + pinfo->nd_opt_pi_valid_time = htonl(prefix->valid); + pinfo->nd_opt_pi_preferred_time = + htonl(prefix->preferred); + } + pinfo->nd_opt_pi_reserved2 = 0; + + memcpy(&pinfo->nd_opt_pi_prefix, &prefix->prefix, + sizeof(struct in6_addr)); + + DBG("[%d] index %d prefix %s/%d", count, index, + inet_ntop(AF_INET6, &prefix->prefix, addr_str, + INET6_ADDRSTRLEN), prefix->prefixlen); + + count++; + } + + if (count > 0) { + err = ndisc_send_unspec(ND_ROUTER_ADVERT, index, &dst, source, + buf, len, router_lifetime); + if (err < 0) + DBG("cannot send RA %d/%s", err, strerror(-err)); + } + +out: + close(sk); + return err; +} + +void __connman_inet_ipv6_stop_recv_rs(void *context) +{ + if (!context) + return; + + xs_cleanup(context); +} + +static int icmpv6_rs_recv(int fd, struct xs_cb_data *data) +{ + struct msghdr mhdr; + struct iovec iov; + unsigned char chdr[CMSG_BUF_LEN]; + unsigned char buf[1540]; + struct nd_router_solicit *hdr; + struct sockaddr_in6 saddr; + ssize_t len; + __connman_inet_recv_rs_cb_t cb = data->callback; + + DBG(""); + + iov.iov_len = sizeof(buf); + iov.iov_base = buf; + + mhdr.msg_name = (void *)&saddr; + mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_flags = 0; + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = (void *)chdr; + mhdr.msg_controllen = CMSG_BUF_LEN; + + len = recvmsg(fd, &mhdr, 0); + if (len < 0) { + cb(NULL, 0, data->user_data); + return -errno; + } + + hdr = (struct nd_router_solicit *)buf; + DBG("code %d len %zd hdr %zd", hdr->nd_rs_code, len, + sizeof(struct nd_router_solicit)); + if (hdr->nd_rs_code != 0) + return 0; + + cb(hdr, len, data->user_data); + return len; +} + +static gboolean icmpv6_rs_event(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + int fd, ret; + struct xs_cb_data *xs_data = data; + + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) + goto cleanup; + + fd = g_io_channel_unix_get_fd(chan); + ret = icmpv6_rs_recv(fd, xs_data); + if (ret == 0) + return TRUE; + +cleanup: + xs_data->watch_id = 0; + return FALSE; +} + +int __connman_inet_ipv6_start_recv_rs(int index, + __connman_inet_recv_rs_cb_t callback, + void *user_data, + void **context) +{ + struct xs_cb_data *data; + struct icmp6_filter filter; + char addr_str[INET6_ADDRSTRLEN]; + int sk, err; + + data = g_try_malloc0(sizeof(struct xs_cb_data)); + if (!data) + return -ENOMEM; + + data->callback = callback; + data->user_data = user_data; + + sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); + if (sk < 0) { + g_free(data); + return -errno; + } + + DBG("sock %d", sk); + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); + + setsockopt(sk, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, + sizeof(struct icmp6_filter)); + + err = if_mc_group(sk, index, &in6addr_all_routers_mc, IPV6_JOIN_GROUP); + if (err < 0) + DBG("Cannot join mc %s %d/%s", inet_ntop(AF_INET6, + &in6addr_all_routers_mc, addr_str, INET6_ADDRSTRLEN), + err, strerror(-err)); + + data->channel = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(data->channel, TRUE); + + g_io_channel_set_encoding(data->channel, NULL, NULL); + g_io_channel_set_buffered(data->channel, FALSE); + + data->watch_id = g_io_add_watch(data->channel, + G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + icmpv6_rs_event, data); + + *context = data; return 0; } + +static gboolean ns_timeout_cb(gpointer user_data) +{ + struct xs_cb_data *data = user_data; + + DBG("user data %p", user_data); + + if (!data) + return FALSE; + + if (data->callback) { + __connman_inet_ns_cb_t cb = data->callback; + cb(NULL, 0, &data->addr.sin6_addr, data->user_data); + } + + data->timeout = 0; + xs_cleanup(data); + return FALSE; +} + +static int icmpv6_nd_recv(int fd, struct xs_cb_data *data) +{ + struct msghdr mhdr; + struct iovec iov; + unsigned char chdr[CMSG_BUF_LEN]; + unsigned char buf[1540]; + struct nd_neighbor_advert *hdr; + struct sockaddr_in6 saddr; + ssize_t len; + __connman_inet_ns_cb_t cb = data->callback; + + DBG(""); + + iov.iov_len = sizeof(buf); + iov.iov_base = buf; + + mhdr.msg_name = (void *)&saddr; + mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_flags = 0; + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = (void *)chdr; + mhdr.msg_controllen = CMSG_BUF_LEN; + + len = recvmsg(fd, &mhdr, 0); + if (len < 0) { + cb(NULL, 0, &data->addr.sin6_addr, data->user_data); + return -errno; + } + + hdr = (struct nd_neighbor_advert *)buf; + DBG("code %d len %zd hdr %zd", hdr->nd_na_code, len, + sizeof(struct nd_neighbor_advert)); + if (hdr->nd_na_code != 0) + return 0; + + /* + * We can receive any neighbor advertisement so we need to check if the + * packet was meant for us and ignore the packet otherwise. + */ + if (memcmp(&data->addr.sin6_addr, &hdr->nd_na_target, + sizeof(struct in6_addr))) + return 0; + + cb(hdr, len, &data->addr.sin6_addr, data->user_data); + + return len; +} + +static gboolean icmpv6_nd_event(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + int fd, ret; + struct xs_cb_data *xs_data = data; + + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) + goto cleanup; + + fd = g_io_channel_unix_get_fd(chan); + ret = icmpv6_nd_recv(fd, xs_data); + if (ret == 0) + return TRUE; + +cleanup: + xs_cleanup(xs_data); + return TRUE; +} + +int __connman_inet_ipv6_do_dad(int index, int timeout_ms, + struct in6_addr *addr, + __connman_inet_ns_cb_t callback, + void *user_data) +{ + struct xs_cb_data *data; + struct icmp6_filter filter; + struct in6_addr solicit; + int sk, err, val = 1; + + if (timeout_ms <= 0) + return -EINVAL; + + data = g_try_malloc0(sizeof(struct xs_cb_data)); + if (!data) + return -ENOMEM; + + data->callback = callback; + data->user_data = user_data; + data->timeout = g_timeout_add_full(G_PRIORITY_DEFAULT, + (guint)timeout_ms, + ns_timeout_cb, + data, + NULL); + memcpy(&data->addr.sin6_addr, addr, sizeof(struct in6_addr)); + + sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); + if (sk < 0) + return -errno; + + DBG("sock %d", sk); + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter); + + setsockopt(sk, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, + sizeof(struct icmp6_filter)); + + if (setsockopt(sk, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &val, sizeof(val)) < 0) { + err = -errno; + DBG("Cannot set IPV6_RECVPKTINFO %d/%s", err, + strerror(-err)); + close(sk); + return err; + } + + if (setsockopt(sk, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + &val, sizeof(val)) < 0) { + err = -errno; + DBG("Cannot set IPV6_RECVHOPLIMIT %d/%s", err, + strerror(-err)); + close(sk); + return err; + } + + val = 0; + setsockopt(sk, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)); + + ipv6_addr_solict_mult(addr, &solicit); + if_mc_group(sk, index, &in6addr_all_nodes_mc, IPV6_JOIN_GROUP); + if_mc_group(sk, index, &solicit, IPV6_JOIN_GROUP); + + data->channel = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(data->channel, TRUE); + + g_io_channel_set_encoding(data->channel, NULL, NULL); + g_io_channel_set_buffered(data->channel, FALSE); + + data->watch_id = g_io_add_watch(data->channel, + G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + icmpv6_nd_event, data); + + err = ndisc_send_unspec(ND_NEIGHBOR_SOLICIT, index, &solicit, NULL, + (unsigned char *)addr, 0, 0); + if (err < 0) { + DBG("Cannot send NS %d/%s", err, strerror(-err)); + xs_cleanup(data); + } + + return err; +} + +GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr, + unsigned int length) +{ + GSList *prefixes = NULL; + uint8_t *pos; + int len; + + if (length <= sizeof(struct nd_router_advert)) + return NULL; + + len = length - sizeof(struct nd_router_advert); + pos = (uint8_t *)hdr + sizeof(struct nd_router_advert); + + while (len > 0) { + struct nd_opt_prefix_info *pinfo; + char prefix_str[INET6_ADDRSTRLEN+1], *str; + const char *prefix; + int optlen; + + if (len < 2) + break; + + optlen = pos[1] << 3; + if (optlen == 0 || optlen > len) + break; + + switch (pos[0]) { + case ND_OPT_PREFIX_INFORMATION: + pinfo = (struct nd_opt_prefix_info *)pos; + prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefix_str, INET6_ADDRSTRLEN); + if (!prefix) + break; + + str = g_strdup_printf("%s/%d", prefix, + pinfo->nd_opt_pi_prefix_len); + prefixes = g_slist_prepend(prefixes, str); + + DBG("prefix %s", str); + + break; + } + + len -= optlen; + pos += optlen; + } + + return prefixes; +} + +static int get_dest_addr(int family, int index, char *buf, int len) +{ + struct ifreq ifr; + void *addr; + int sk; + + sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = index; + + if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno)); + close(sk); + return -errno; + } + + if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { + DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno)); + close(sk); + return -errno; + } + + if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) { + close(sk); + errno = EINVAL; + return -errno; + } + + DBG("index %d %s", index, ifr.ifr_name); + + if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) { + connman_error("Get destination address failed (%s)", + strerror(errno)); + close(sk); + return -errno; + } + + close(sk); + + switch (family) { + case AF_INET: + addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr; + break; + case AF_INET6: + addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr; + break; + default: + errno = EINVAL; + return -errno; + } + + if (!inet_ntop(family, addr, buf, len)) { + DBG("error %d/%s", errno, strerror(errno)); + return -errno; + } + + return 0; +} + +int connman_inet_get_dest_addr(int index, char **dest) +{ + char addr[INET_ADDRSTRLEN]; + int ret; + + ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN); + if (ret < 0) + return ret; + + *dest = g_strdup(addr); + + DBG("destination %s", *dest); + + return 0; +} + +int connman_inet_ipv6_get_dest_addr(int index, char **dest) +{ + char addr[INET6_ADDRSTRLEN]; + int ret; + + ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN); + if (ret < 0) + return ret; + + *dest = g_strdup(addr); + + DBG("destination %s", *dest); + + return 0; +} + +int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth) +{ + int sndbuf = 1024; + int rcvbuf = 1024 * 4; + + rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if (rth->fd < 0) { + connman_error("Can not open netlink socket: %s", + strerror(errno)); + return -errno; + } + + if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, + sizeof(sndbuf)) < 0) { + connman_error("SO_SNDBUF: %s", strerror(errno)); + return -errno; + } + + if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, + sizeof(rcvbuf)) < 0) { + connman_error("SO_RCVBUF: %s", strerror(errno)); + return -errno; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = 0; + + if (bind(rth->fd, (struct sockaddr *)&rth->local, + sizeof(rth->local)) < 0) { + connman_error("Can not bind netlink socket: %s", + strerror(errno)); + return -errno; + } + + rth->seq = time(NULL); + + DBG("fd %d", rth->fd); + + return 0; +} + +struct inet_rtnl_cb_data { + GIOChannel *channel; + __connman_inet_rtnl_cb_t callback; + guint rtnl_timeout; + guint watch_id; + struct __connman_inet_rtnl_handle *rtnl; + void *user_data; +}; + +static void inet_rtnl_cleanup(struct inet_rtnl_cb_data *data) +{ + struct __connman_inet_rtnl_handle *rth = data->rtnl; + + if (data->channel) { + g_io_channel_shutdown(data->channel, TRUE, NULL); + g_io_channel_unref(data->channel); + data->channel = NULL; + } + + DBG("data %p", data); + + if (data->rtnl_timeout > 0) + g_source_remove(data->rtnl_timeout); + + if (data->watch_id > 0) + g_source_remove(data->watch_id); + + if (rth) { + __connman_inet_rtnl_close(rth); + g_free(rth); + } + + g_free(data); +} + +static gboolean inet_rtnl_timeout_cb(gpointer user_data) +{ + struct inet_rtnl_cb_data *data = user_data; + + DBG("user data %p", user_data); + + if (!data) + return FALSE; + + if (data->callback) + data->callback(NULL, data->user_data); + + data->rtnl_timeout = 0; + inet_rtnl_cleanup(data); + return FALSE; +} + +static int inet_rtnl_recv(GIOChannel *chan, struct inet_rtnl_cb_data *rtnl_data) +{ + struct __connman_inet_rtnl_handle *rth = rtnl_data->rtnl; + struct nlmsghdr *h = NULL; + struct sockaddr_nl nladdr; + socklen_t addr_len = sizeof(nladdr); + unsigned char buf[4096]; + void *ptr = buf; + gsize len; + int status, fd; + + memset(buf, 0, sizeof(buf)); + memset(&nladdr, 0, sizeof(nladdr)); + + 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 0; + + return -1; + } + + if (status == 0) + return -1; + + if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */ + DBG("Received msg from %u, ignoring it", nladdr.nl_pid); + return 0; + } + + len = status; + + while (len > 0) { + struct nlmsgerr *err; + + h = ptr; + + if (!NLMSG_OK(h, len)) + return -1; + + if (h->nlmsg_seq != rth->seq) { + /* Skip this msg */ + DBG("skip %d/%d len %d", rth->seq, + h->nlmsg_seq, h->nlmsg_len); + + len -= h->nlmsg_len; + ptr += h->nlmsg_len; + continue; + } + + switch (h->nlmsg_type) { + case NLMSG_NOOP: + case NLMSG_OVERRUN: + return -1; + + case NLMSG_ERROR: + err = (struct nlmsgerr *)NLMSG_DATA(h); + connman_error("RTNETLINK answers %s (%d)", + strerror(-err->error), -err->error); + return err->error; + } + + break; + } + + if (h->nlmsg_seq == rth->seq) { + DBG("received %d seq %d", h->nlmsg_len, h->nlmsg_seq); + + rtnl_data->callback(h, rtnl_data->user_data); + + inet_rtnl_cleanup(rtnl_data); + } + + return 0; +} + +static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + int ret; + struct inet_rtnl_cb_data *rtnl_data = user_data; + + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) + goto cleanup; + + ret = inet_rtnl_recv(chan, rtnl_data); + if (ret == 0) + return TRUE; + +cleanup: + inet_rtnl_cleanup(rtnl_data); + return TRUE; +} + +int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl, + struct nlmsghdr *n, int timeout, + __connman_inet_rtnl_cb_t callback, void *user_data) +{ + struct sockaddr_nl nladdr; + struct inet_rtnl_cb_data *data; + int err; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + n->nlmsg_seq = ++rtnl->seq; + + if (callback) { + data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data)); + if (!data) + return -ENOMEM; + + data->callback = callback; + data->user_data = user_data; + data->rtnl = rtnl; + data->rtnl_timeout = g_timeout_add_seconds(timeout, + inet_rtnl_timeout_cb, data); + + data->channel = g_io_channel_unix_new(rtnl->fd); + + g_io_channel_set_encoding(data->channel, NULL, NULL); + g_io_channel_set_buffered(data->channel, FALSE); + + data->watch_id = g_io_add_watch(data->channel, + G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + inet_rtnl_event, data); + } else + n->nlmsg_flags |= NLM_F_ACK; + + err = sendto(rtnl->fd, &rtnl->req.n, rtnl->req.n.nlmsg_len, 0, + (struct sockaddr *) &nladdr, sizeof(nladdr)); + DBG("handle %p len %d", rtnl, rtnl->req.n.nlmsg_len); + if (err < 0) { + connman_error("Can not talk to rtnetlink err %d %s", + -errno, strerror(errno)); + return -errno; + } + + if ((unsigned int)err != rtnl->req.n.nlmsg_len) { + connman_error("Sent %d bytes, msg truncated", err); + return -EINVAL; + } + + return 0; +} + +void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth) +{ + DBG("handle %p", rth); + + if (rth->fd >= 0) { + close(rth->fd); + rth->fd = -1; + } +} + +int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, int type, + __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + DBG("Error! max allowed bound %zd exceeded", maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + + return 0; +} + +static int parse_rtattr(struct rtattr *tb[], int max, + struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if ((rta->rta_type <= max) && (!tb[rta->rta_type])) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta, len); + } + if (len) + connman_error("Deficit %d, rta_len=%d", len, rta->rta_len); + + return 0; +} + +struct get_route_cb_data { + connman_inet_addr_cb_t callback; + void *user_data; +}; + +static void get_route_cb(struct nlmsghdr *answer, void *user_data) +{ + struct get_route_cb_data *data = user_data; + struct rtattr *tb[RTA_MAX+1]; + struct rtmsg *r = NLMSG_DATA(answer); + int len, index = -1; + char abuf[256]; + const char *addr = NULL; + + DBG("answer %p data %p", answer, user_data); + + if (!answer) + goto out; + + len = answer->nlmsg_len; + + if (answer->nlmsg_type != RTM_NEWROUTE && + answer->nlmsg_type != RTM_DELROUTE) { + connman_error("Not a route: %08x %08x %08x", + answer->nlmsg_len, answer->nlmsg_type, + answer->nlmsg_flags); + goto out; + } + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + connman_error("BUG: wrong nlmsg len %d", len); + goto out; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_OIF]) + index = *(int *)RTA_DATA(tb[RTA_OIF]); + + if (tb[RTA_GATEWAY]) + addr = inet_ntop(r->rtm_family, + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf)); + + DBG("addr %s index %d user %p", addr, index, data->user_data); + +out: + if (data && data->callback) + data->callback(addr, index, data->user_data); + + g_free(data); + + return; +} + +/* + * Return the interface index that contains route to host. + */ +int __connman_inet_get_route(const char *dest_address, + connman_inet_addr_cb_t callback, void *user_data) +{ + struct get_route_cb_data *data; + struct addrinfo hints, *rp; + struct __connman_inet_rtnl_handle *rth; + int err; + + DBG("dest %s", dest_address); + + if (!dest_address) + return -EINVAL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST; + + err = getaddrinfo(dest_address, NULL, &hints, &rp); + if (err) + return -EINVAL; + + rth = g_try_malloc0(sizeof(struct __connman_inet_rtnl_handle)); + if (!rth) { + freeaddrinfo(rp); + return -ENOMEM; + } + + rth->req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + rth->req.n.nlmsg_flags = NLM_F_REQUEST; + rth->req.n.nlmsg_type = RTM_GETROUTE; + rth->req.u.r.rt.rtm_family = rp->ai_family; + rth->req.u.r.rt.rtm_table = 0; + rth->req.u.r.rt.rtm_protocol = 0; + rth->req.u.r.rt.rtm_scope = 0; + rth->req.u.r.rt.rtm_type = 0; + rth->req.u.r.rt.rtm_src_len = 0; + rth->req.u.r.rt.rtm_dst_len = rp->ai_addrlen << 3; + rth->req.u.r.rt.rtm_tos = 0; + + __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req), RTA_DST, + &rp->ai_addr, rp->ai_addrlen); + + freeaddrinfo(rp); + + err = __connman_inet_rtnl_open(rth); + if (err < 0) + goto fail; + + data = g_try_malloc(sizeof(struct get_route_cb_data)); + if (!data) { + err = -ENOMEM; + goto done; + } + + data->callback = callback; + data->user_data = user_data; + +#define GET_ROUTE_TIMEOUT 2 + err = __connman_inet_rtnl_talk(rth, &rth->req.n, GET_ROUTE_TIMEOUT, + get_route_cb, data); + if (err < 0) { + g_free(data); + goto done; + } + + return 0; + +done: + __connman_inet_rtnl_close(rth); + +fail: + g_free(rth); + return err; +} + +int connman_inet_check_ipaddress(const char *host) +{ + struct addrinfo hints; + struct addrinfo *addr; + int result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + addr = NULL; + + result = getaddrinfo(host, NULL, &hints, &addr); + if (result == 0) + result = addr->ai_family; + freeaddrinfo(addr); + + return result; +} + +/* Check routine modified from ics-dhcp 4.2.3-P2 */ +bool connman_inet_check_hostname(const char *ptr, size_t len) +{ + const char *p; + + /* + * Not empty or complete length not over 255 characters. + */ + if ((len == 0) || (len > 256)) + return false; + + /* + * Consists of [[:alnum:]-]+ labels separated by [.] + * a [_] is against RFC but seems to be "widely used" + */ + for (p = ptr; (*p != 0) && (len-- > 0); p++) { + + if ((*p == '-') || (*p == '_')) { + /* + * Not allowed at begin or end of a label. + */ + if (((p - ptr) == 0) || (len == 0) || (p[1] == '.')) + return false; + + } else if (*p == '.') { + /* + * Each label has to be 1-63 characters; + * we allow [.] at the end ('foo.bar.') + */ + size_t d = p - ptr; + + if ((d <= 0) || (d >= 64)) + return false; + + ptr = p + 1; /* Jump to the next label */ + + } else if (isalnum((unsigned char)*p) == 0) { + /* + * Also numbers at the begin are fine + */ + return false; + } + } + + return true; +} + +char **__connman_inet_get_running_interfaces(void) +{ + char **result; + struct ifconf ifc; + struct ifreq *ifr = NULL; + int sk, i, numif, count = 0; + + memset(&ifc, 0, sizeof(ifc)); + + sk = socket(AF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return NULL; + + if (ioctl(sk, SIOCGIFCONF, &ifc) < 0) + goto error; + + /* + * Allocate some extra bytes just in case there will + * be new interfaces added between two SIOCGIFCONF + * calls. + */ + ifr = g_try_malloc0(ifc.ifc_len * 2); + if (!ifr) + goto error; + + ifc.ifc_req = ifr; + + if (ioctl(sk, SIOCGIFCONF, &ifc) < 0) + goto error; + + numif = ifc.ifc_len / sizeof(struct ifreq); + + result = g_try_malloc0((numif + 1) * sizeof(char *)); + if (!result) + goto error; + + close(sk); + + for (i = 0; i < numif; i++) { + struct ifreq *r = &ifr[i]; + struct in6_addr *addr6; + in_addr_t addr4; + + /* + * Note that we do not return loopback interfaces here as they + * are not needed for our purposes. + */ + switch (r->ifr_addr.sa_family) { + case AF_INET: + addr4 = ntohl(((struct sockaddr_in *) + &r->ifr_addr)->sin_addr.s_addr); + if (((addr4 & 0xff000000) >> 24) == 127) + continue; + break; + case AF_INET6: + addr6 = &((struct sockaddr_in6 *) + &r->ifr_addr)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(addr6)) + continue; + break; + } + + result[count++] = g_strdup(r->ifr_name); + } + + g_free(ifr); + + if (count < numif) + { + char **prev_result = result; + result = g_try_realloc(result, (count + 1) * sizeof(char *)); + if (!result) { + g_free(prev_result); + return NULL; + } + } + + return result; + +error: + close(sk); + g_free(ifr); + return NULL; +} + +bool connman_inet_is_ipv6_supported() +{ + int sk; + + sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return false; + + close(sk); + return true; +} + +int __connman_inet_get_interface_address(int index, int family, void *address) +{ + struct ifaddrs *ifaddr, *ifa; + int err = -ENOENT; + char name[IF_NAMESIZE]; + + if (!if_indextoname(index, name)) + return -EINVAL; + + DBG("index %d interface %s", index, name); + + if (getifaddrs(&ifaddr) < 0) { + err = -errno; + DBG("Cannot get addresses err %d/%s", err, strerror(-err)); + return err; + } + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 && + ifa->ifa_addr->sa_family == family) { + if (family == AF_INET) { + struct sockaddr_in *in4 = (struct sockaddr_in *) + ifa->ifa_addr; + if (in4->sin_addr.s_addr == INADDR_ANY) + continue; + memcpy(address, &in4->sin_addr, + sizeof(struct in_addr)); + } else if (family == AF_INET6) { + struct sockaddr_in6 *in6 = + (struct sockaddr_in6 *)ifa->ifa_addr; + if (memcmp(&in6->sin6_addr, &in6addr_any, + sizeof(struct in6_addr)) == 0) + continue; + memcpy(address, &in6->sin6_addr, + sizeof(struct in6_addr)); + + } else { + err = -EINVAL; + goto out; + } + + err = 0; + break; + } + } + +out: + freeifaddrs(ifaddr); + return err; +} + +static int iprule_modify(int cmd, int family, uint32_t table_id, + uint32_t fwmark) +{ + struct __connman_inet_rtnl_handle rth; + int ret; + + memset(&rth, 0, sizeof(rth)); + + rth.req.n.nlmsg_type = cmd; + rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + rth.req.n.nlmsg_flags = NLM_F_REQUEST; + rth.req.u.r.rt.rtm_family = family; + rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT; + rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE; + rth.req.u.r.rt.rtm_table = table_id; + rth.req.u.r.rt.rtm_type = RTN_UNSPEC; + rth.req.u.r.rt.rtm_flags = 0; + + if (cmd == RTM_NEWRULE) { + rth.req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; + rth.req.u.r.rt.rtm_type = RTN_UNICAST; + } + + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), + FRA_FWMARK, fwmark); + + if (table_id < 256) { + rth.req.u.r.rt.rtm_table = table_id; + } else { + rth.req.u.r.rt.rtm_table = RT_TABLE_UNSPEC; + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), + FRA_TABLE, table_id); + } + + if (rth.req.u.r.rt.rtm_family == AF_UNSPEC) + rth.req.u.r.rt.rtm_family = AF_INET; + + ret = __connman_inet_rtnl_open(&rth); + if (ret < 0) + goto done; + + ret = __connman_inet_rtnl_send(&rth, &rth.req.n); + +done: + __connman_inet_rtnl_close(&rth); + + return ret; +} + +int __connman_inet_add_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark) +{ + /* ip rule add fwmark 9876 table 1234 */ + + return iprule_modify(RTM_NEWRULE, family, table_id, fwmark); +} + +int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark) +{ + return iprule_modify(RTM_DELRULE, family, table_id, fwmark); +} + +static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex, + const char *gateway) +{ + struct __connman_inet_rtnl_handle rth; + unsigned char buf[sizeof(struct in6_addr)]; + int ret, len; + int family = connman_inet_check_ipaddress(gateway); + + switch (family) { + case AF_INET: + len = 4; + break; + case AF_INET6: + len = 16; + break; + default: + return -EINVAL; + } + + ret = inet_pton(family, gateway, buf); + if (ret <= 0) + return -EINVAL; + + memset(&rth, 0, sizeof(rth)); + + rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + rth.req.n.nlmsg_type = cmd; + rth.req.u.r.rt.rtm_family = family; + rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN; + rth.req.u.r.rt.rtm_scope = RT_SCOPE_NOWHERE; + rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT; + rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE; + rth.req.u.r.rt.rtm_type = RTN_UNICAST; + + __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY, + buf, len); + if (table_id < 256) { + rth.req.u.r.rt.rtm_table = table_id; + } else { + rth.req.u.r.rt.rtm_table = RT_TABLE_UNSPEC; + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), + RTA_TABLE, table_id); + } + + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), + RTA_OIF, ifindex); + + ret = __connman_inet_rtnl_open(&rth); + if (ret < 0) + goto done; + + ret = __connman_inet_rtnl_send(&rth, &rth.req.n); + +done: + __connman_inet_rtnl_close(&rth); + + return ret; +} + +int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex, + const char *gateway) +{ + /* ip route add default via 1.2.3.4 dev wlan0 table 1234 */ + + return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway); +} + +int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, + const char *gateway) +{ + /* ip route del default via 1.2.3.4 dev wlan0 table 1234 */ + + return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway); +} + +int __connman_inet_get_interface_ll_address(int index, int family, + void *address) +{ + struct ifaddrs *ifaddr, *ifa; + int err = -ENOENT; + char name[IF_NAMESIZE]; + + if (!if_indextoname(index, name)) + return -EINVAL; + + DBG("index %d interface %s", index, name); + + if (getifaddrs(&ifaddr) < 0) { + err = -errno; + DBG("Cannot get addresses err %d/%s", err, strerror(-err)); + return err; + } + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 && + ifa->ifa_addr->sa_family == family) { + if (family == AF_INET) { + struct sockaddr_in *in4 = (struct sockaddr_in *) + ifa->ifa_addr; + if (in4->sin_addr.s_addr == INADDR_ANY) + continue; + if ((in4->sin_addr.s_addr & IN_CLASSB_NET) != + ((in_addr_t) 0xa9fe0000)) + continue; + memcpy(address, &in4->sin_addr, + sizeof(struct in_addr)); + } else if (family == AF_INET6) { + struct sockaddr_in6 *in6 = + (struct sockaddr_in6 *)ifa->ifa_addr; + if (memcmp(&in6->sin6_addr, &in6addr_any, + sizeof(struct in6_addr)) == 0) + continue; + if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) + continue; + + memcpy(address, &in6->sin6_addr, + sizeof(struct in6_addr)); + } else { + err = -EINVAL; + goto out; + } + + err = 0; + break; + } + } + +out: + freeifaddrs(ifaddr); + return err; +} + +int __connman_inet_get_address_netmask(int ifindex, + struct sockaddr_in *address, + struct sockaddr_in *netmask) +{ + int sk, ret = -EINVAL; + struct ifreq ifr; + + DBG("index %d", ifindex); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EINVAL; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = ifindex; + + if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) + goto out; + + if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) + goto out; + + memcpy(netmask, (struct sockaddr_in *)&ifr.ifr_netmask, + sizeof(struct sockaddr_in)); + + if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) + goto out; + + memcpy(address, (struct sockaddr_in *)&ifr.ifr_addr, + sizeof(struct sockaddr_in)); + ret = 0; + +out: + close(sk); + return ret; +} + +static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file, + struct in_addr *addr) +{ + char *s, *nfsargs; + size_t len; + char addrstr[INET_ADDRSTRLEN]; + struct in_addr taddr; + GError *error = NULL; + char *cmdline = NULL; + char *pnp = NULL; + char **args = NULL; + char **pnpent = NULL; + char **pp = NULL; + int err = -1; + + if (!cmdline_file) + cmdline_file = "/proc/cmdline"; + if (!pnp_file) + pnp_file = "/proc/net/pnp"; + if (!addr) + addr = &taddr; + addr->s_addr = INADDR_NONE; + + if (!g_file_get_contents(cmdline_file, &cmdline, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + cmdline_file, error->message); + goto out; + } + + if (g_file_test(pnp_file, G_FILE_TEST_EXISTS)) { + if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + pnp_file, error->message); + goto out; + } + } else { + connman_error("%s: File %s doesn't exist\n", __func__, pnp_file); + goto out; + } + + len = strlen(cmdline); + if (len <= 1) { + /* too short */ + goto out; + } + /* remove newline */ + if (cmdline[len - 1] == '\n') + cmdline[--len] = '\0'; + + /* split in arguments (seperated by space) */ + args = g_strsplit(cmdline, " ", 0); + if (!args) { + connman_error("%s: Cannot split cmdline \"%s\"\n", __func__, + cmdline); + goto out; + } + + /* split in entries (by newlines) */ + pnpent = g_strsplit(pnp, "\n", 0); + if (!pnpent) { + connman_error("%s: Cannot split pnp at file \"%s\"\n", __func__, + pnp_file); + goto out; + } + + /* first find root argument */ + for (pp = args; *pp; pp++) { + if (!strcmp(*pp, "root=/dev/nfs")) + break; + } + /* no rootnfs found */ + if (!*pp) + goto out; + + /* locate nfsroot argument */ + for (pp = args; *pp; pp++) { + if (!strncmp(*pp, "nfsroot=", strlen("nfsroot="))) + break; + } + /* no nfsroot argument found */ + if (!*pp) + goto out; + + /* determine if nfsroot server is provided */ + nfsargs = strchr(*pp, '='); + if (!nfsargs) + goto out; + nfsargs++; + + /* find whether serverip is present */ + s = strchr(nfsargs, ':'); + if (s) { + len = s - nfsargs; + s = nfsargs; + } else { + /* no serverip, use bootserver */ + for (pp = pnpent; *pp; pp++) { + if (!strncmp(*pp, "bootserver ", strlen("bootserver "))) + break; + } + /* no bootserver found */ + if (!*pp) + goto out; + s = *pp + strlen("bootserver "); + len = strlen(s); + } + + /* copy to addr string buffer */ + if (len >= sizeof(addrstr)) { + connman_error("%s: Bad server\n", __func__); + goto out; + } + memcpy(addrstr, s, len); + addrstr[len] = '\0'; + + err = inet_pton(AF_INET, addrstr, addr); + if (err <= 0) { + connman_error("%s: Cannot convert to numeric addr \"%s\"\n", + __func__, addrstr); + err = -1; + goto out; + } + + /* all done */ + err = 0; +out: + g_strfreev(pnpent); + g_strfreev(args); + if (error) + g_error_free(error); + g_free(pnp); + g_free(cmdline); + + return err; +} + +/* get interface out of which peer is reachable (IPv4 only) */ +static int get_peer_iface(struct in_addr *addr, char *ifname) +{ + struct ifaddrs *ifaddr, *ifa; + struct sockaddr_in saddr, *ifsaddr; + socklen_t socklen; + int s; + int err = -1; + + /* Obtain address(es) matching host/port */ + err = getifaddrs(&ifaddr); + if (err < 0) { + connman_error("%s: getifaddrs() failed %d (%s)\n", + __func__, errno, strerror(errno)); + return -1; + } + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) { + connman_error("%s: socket() failed %d (%s)\n", + __func__, errno, strerror(errno)); + return -1; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; /* any port */ + saddr.sin_addr = *addr; + + /* no need to bind, connect will select iface */ + err = connect(s, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); + if (err < 0) { + connman_error("%s: connect() failed: %d (%s)\n", + __func__, errno, strerror(errno)); + goto out; + } + + socklen = sizeof(saddr); + err = getsockname(s, (struct sockaddr *)&saddr, &socklen); + if (err < 0) { + connman_error("%s: getsockname() failed: %d (%s)\n", + __func__, errno, strerror(errno)); + goto out; + } + + err = -1; + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + /* only IPv4 address */ + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + ifsaddr = (struct sockaddr_in *)ifa->ifa_addr; + + /* match address? */ + if (ifsaddr->sin_addr.s_addr == saddr.sin_addr.s_addr) + break; + } + + if (ifa) { + err = 0; + if (ifname) + strcpy(ifname, ifa->ifa_name); + } + +out: + close(s); + + freeifaddrs(ifaddr); + + return err; +} + +bool __connman_inet_isrootnfs_device(const char *devname) +{ + struct in_addr addr; + char ifname[IFNAMSIZ]; + + return get_nfs_server_ip(NULL, NULL, &addr) == 0 && + get_peer_iface(&addr, ifname) == 0 && + strcmp(devname, ifname) == 0; +} + +char **__connman_inet_get_pnp_nameservers(const char *pnp_file) +{ + char **pp; + char *s; + int pass, count; + GError *error = NULL; + char *pnp = NULL; + char **pnpent = NULL; + char **nameservers = NULL; + + if (!pnp_file) + pnp_file = "/proc/net/pnp"; + + if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) { + connman_error("%s: Cannot read %s %s\n", __func__, + pnp_file, error->message); + goto out; + } + + /* split in entries (by newlines) */ + pnpent = g_strsplit(pnp, "\n", 0); + if (!pnpent) { + connman_error("%s: Cannot split pnp \"%s\"\n", __func__, + pnp_file); + goto out; + } + + /* + * Perform two passes to retreive a char ** array of + * nameservers that are not 0.0.0.0 + * + * The first pass counts them, the second fills in the + * array. + */ + count = 0; + nameservers = NULL; + for (pass = 1; pass <= 2; pass++) { + + /* at the start of the second pass allocate */ + if (pass == 2) + nameservers = g_new(char *, count + 1); + + count = 0; + for (pp = pnpent; *pp; pp++) { + /* match 'nameserver ' at the start of each line */ + if (strncmp(*pp, "nameserver ", strlen("nameserver "))) + continue; + + /* compare it against 0.0.0.0 */ + s = *pp + strlen("nameserver "); + if (!strcmp(s, "0.0.0.0")) + continue; + + /* on second pass fill in array */ + if (pass == 2) + nameservers[count] = g_strdup(s); + count++; + } + + /* no nameservers? */ + if (count == 0) + goto out; + + /* and terminate char ** array with NULL */ + if (pass == 2) + nameservers[count] = NULL; + + } + +out: + g_strfreev(pnpent); + g_free(pnp); + if (error) + g_error_free(error); + + return nameservers; +}