provider: Allow user to set routes when creating provider
[platform/upstream/connman.git] / src / inet.c
index 5e5d9cc..5b81273 100644 (file)
@@ -25,6 +25,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
@@ -40,6 +41,8 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
 
 #include "connman.h"
 
@@ -86,13 +89,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;
+               return -EINVAL;
 
        if (family != AF_INET && family != AF_INET6)
-               return -1;
+               return -EINVAL;
 
        memset(&request, 0, sizeof(request));
 
@@ -146,9 +151,9 @@ int __connman_inet_modify_address(int cmd, int flags,
                        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;
@@ -173,7 +178,7 @@ int connman_inet_ifindex(const char *name)
        if (name == NULL)
                return -1;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -198,7 +203,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;
 
@@ -220,7 +225,7 @@ short int connman_inet_ifflags(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;
 
@@ -250,7 +255,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;
 
@@ -292,7 +297,7 @@ int connman_inet_ifdown(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;
 
@@ -337,7 +342,7 @@ static char *index2addr(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;
 
@@ -380,7 +385,7 @@ static char *index2ident(int index, const char *prefix)
        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;
 
@@ -424,7 +429,7 @@ connman_bool_t connman_inet_is_cfg80211(int index)
        struct ifreq ifr;
        int sk;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return FALSE;
 
@@ -460,7 +465,7 @@ struct connman_device *connman_inet_create_device(int index)
        if (devname == NULL)
                return NULL;
 
-       if (__connman_element_device_isfiltered(devname) == TRUE) {
+       if (__connman_device_isfiltered(devname) == TRUE) {
                connman_info("Ignoring interface %s (filtered)", devname);
                free(devname);
                return NULL;
@@ -539,6 +544,7 @@ struct in6_ifreq {
 int connman_inet_set_ipv6_address(int index,
                struct connman_ipaddress *ipaddress)
 {
+       int err;
        unsigned char prefix_len;
        const char *address;
 
@@ -550,11 +556,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;
@@ -562,6 +569,7 @@ 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;
 
@@ -575,11 +583,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;
@@ -588,12 +597,15 @@ 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;
+       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;
@@ -601,6 +613,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,10 +624,11 @@ int connman_inet_clear_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_DELADDR, 0, index, AF_INET,
-                               address, peer, prefix_len, broadcast)) < 0) {
-               DBG("address removal failed");
-               return -1;
+       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,7 +654,10 @@ int connman_inet_add_network_route(int index, const char *host,
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       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)
                return -1;
 
@@ -702,7 +719,9 @@ int connman_inet_del_network_route(int index, const char *host)
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       DBG("index %d host %s", index, host);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -760,7 +779,7 @@ int connman_inet_del_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;
                goto out;
@@ -770,7 +789,8 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
        close(sk);
 out:
        if (err < 0)
-               connman_error("Del IPv6 host route error");
+               connman_error("Del IPv6 host route error (%s)",
+                                               strerror(errno));
 
        return err;
 }
@@ -782,7 +802,7 @@ 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;
@@ -810,7 +830,7 @@ 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;
                goto out;
@@ -820,7 +840,8 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
        close(sk);
 out:
        if (err < 0)
-               connman_error("Set IPv6 host route error");
+               connman_error("Set IPv6 host route error (%s)",
+                                               strerror(errno));
 
        return err;
 }
@@ -836,7 +857,7 @@ 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);
+       DBG("index %d gateway %s", index, gateway);
 
        if (gateway == NULL)
                return -EINVAL;
@@ -852,7 +873,7 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
        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;
                goto out;
@@ -862,7 +883,8 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
        close(sk);
 out:
        if (err < 0)
-               connman_error("Set default IPv6 gateway error");
+               connman_error("Set default IPv6 gateway error (%s)",
+                                               strerror(errno));
 
        return err;
 }
@@ -872,7 +894,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
        struct in6_rtmsg rt;
        int sk, err;
 
-       DBG("index %d, gateway %s", index, gateway);
+       DBG("index %d gateway %s", index, gateway);
 
        if (gateway == NULL)
                return -EINVAL;
@@ -888,7 +910,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
        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;
                goto out;
@@ -898,7 +920,8 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
        close(sk);
 out:
        if (err < 0)
-               connman_error("Clear default IPv6 gateway error");
+               connman_error("Clear default IPv6 gateway error (%s)",
+                                               strerror(errno));
 
        return err;
 }
@@ -910,7 +933,9 @@ int connman_inet_set_gateway_address(int index, const char *gateway)
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       DBG("index %d gateway %s", index, gateway);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -959,9 +984,9 @@ int connman_inet_set_gateway_interface(int index)
        struct sockaddr_in addr;
        int sk, err;
 
-       DBG("");
+       DBG("index %d", index);
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -997,6 +1022,52 @@ int connman_inet_set_gateway_interface(int index)
        return err;
 }
 
+int connman_inet_set_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;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       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;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
 int connman_inet_clear_gateway_address(int index, const char *gateway)
 {
        struct ifreq ifr;
@@ -1004,9 +1075,9 @@ int connman_inet_clear_gateway_address(int index, const char *gateway)
        struct sockaddr_in addr;
        int sk, err;
 
-       DBG("");
+       DBG("index %d gateway %s", index, gateway);
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1055,9 +1126,9 @@ int connman_inet_clear_gateway_interface(int index)
        struct sockaddr_in addr;
        int sk, err;
 
-       DBG("");
+       DBG("index %d", index);
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1093,6 +1164,52 @@ int connman_inet_clear_gateway_interface(int index)
        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;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       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;
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Removing default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
 connman_bool_t connman_inet_compare_subnet(int index, const char *host)
 {
        struct ifreq ifr;
@@ -1110,7 +1227,7 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host)
                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;
 
@@ -1134,6 +1251,9 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host)
                close(sk);
                return FALSE;
        }
+
+       close(sk);
+
        addr = (struct sockaddr_in *)&ifr.ifr_addr;
        if_addr = addr->sin_addr.s_addr;
 
@@ -1148,7 +1268,7 @@ int connman_inet_remove_from_bridge(int index, const char *bridge)
        if (bridge == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1177,7 +1297,7 @@ int connman_inet_add_to_bridge(int index, const char *bridge)
        if (bridge == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1203,7 +1323,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;
 
@@ -1230,7 +1350,7 @@ int connman_inet_setup_tunnel(char *tunnel, int mtu)
        if (tunnel == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_DGRAM, 0);
+       sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1265,11 +1385,46 @@ done:
        return err;
 }
 
+int connman_inet_create_tunnel(char **iface)
+{
+       struct ifreq ifr;
+       int i, fd;
+
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               i = -errno;
+               connman_error("Failed to open /dev/net/tun: %s",
+                               strerror(errno));
+               return i;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+
+       for (i = 0; i < 256; i++) {
+               sprintf(ifr.ifr_name, "tun%d", i);
+
+               if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
+                       break;
+       }
+
+       if (i == 256) {
+               connman_error("Failed to find available tun device");
+               close(fd);
+               return -ENODEV;
+       }
+
+       *iface = g_strdup(ifr.ifr_name);
+
+       return fd;
+}
+
 struct rs_cb_data {
        GIOChannel *channel;
        __connman_inet_rs_cb_t callback;
        struct sockaddr_in6 addr;
        guint rs_timeout;
+       guint watch_id;
        void *user_data;
 };
 
@@ -1283,21 +1438,20 @@ 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)
 {
-       g_io_channel_shutdown(data->channel, TRUE, NULL);
-       g_io_channel_unref(data->channel);
-       data->channel = 0;
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
 
        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);
 }
 
@@ -1311,7 +1465,7 @@ static gboolean rs_timeout_cb(gpointer user_data)
                return FALSE;
 
        if (data->callback != NULL)
-               data->callback(NULL, data->user_data);
+               data->callback(NULL, 0, data->user_data);
 
        data->rs_timeout = 0;
        rs_cleanup(data);
@@ -1343,15 +1497,18 @@ static int icmpv6_recv(int fd, gpointer user_data)
 
        len = recvmsg(fd, &mhdr, 0);
        if (len < 0) {
-               data->callback(NULL, data->user_data);
+               data->callback(NULL, 0, data->user_data);
+               rs_cleanup(data);
                return -errno;
        }
 
        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);
+       data->callback(hdr, len, data->user_data);
        rs_cleanup(data);
 
        return len;
@@ -1432,7 +1589,7 @@ static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest)
 
        DBG("");
 
-       fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+       fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
        if (fd < 0)
                return -errno;
 
@@ -1546,7 +1703,7 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
        data->user_data = user_data;
        data->rs_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;
 
@@ -1566,7 +1723,7 @@ 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);
 
@@ -1574,3 +1731,53 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
 
        return 0;
 }
+
+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 == NULL)
+                               break;
+
+                       str = g_strdup_printf("%s/%d", prefix,
+                                               pinfo->nd_opt_pi_prefix_len);
+                       prefixes = g_slist_append(prefixes, str);
+
+                       DBG("prefix %s", str);
+
+                       break;
+               }
+
+               len -= optlen;
+               pos += optlen;
+       }
+
+       return prefixes;
+}