Fix derefrencing of NULL pointer
[platform/upstream/connman.git] / src / inet.c
old mode 100755 (executable)
new mode 100644 (file)
index 02d9604..08b1a25
@@ -25,7 +25,6 @@
 #include <config.h>
 #endif
 
-#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
@@ -190,6 +189,112 @@ 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(&eth, &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(&eth, &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
+
+bool __connman_inet_is_any_addr(const char *address, int family)
+{
+       bool ret = false;
+       struct addrinfo hints;
+       struct addrinfo *result = NULL;
+       struct sockaddr_in6 *in6 = NULL;
+       struct sockaddr_in *in4 = NULL;
+
+       if (!address || !*address)
+               goto out;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+
+       hints.ai_family = family;
+
+       if (getaddrinfo(address, NULL, &hints, &result))
+               goto out;
+
+       if (result) {
+               if (result->ai_family == AF_INET6) {
+                       in6 = (struct sockaddr_in6*)result->ai_addr;
+                       ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+               } else if (result->ai_family == AF_INET) {
+                       in4 = (struct sockaddr_in*)result->ai_addr;
+                       ret = in4->sin_addr.s_addr == INADDR_ANY;
+               }
+
+               freeaddrinfo(result);
+       }
+
+out:
+       return ret;
+}
+
 int connman_inet_ifindex(const char *name)
 {
        struct ifreq ifr;
@@ -347,29 +452,14 @@ void connman_inet_update_device_ident(struct connman_device *device)
        case CONNMAN_DEVICE_TYPE_GADGET:
        case CONNMAN_DEVICE_TYPE_WIFI:
                addr = index2addr(index);
-               break;
-       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
-       case CONNMAN_DEVICE_TYPE_CELLULAR:
-       case CONNMAN_DEVICE_TYPE_GPS:
-       case CONNMAN_DEVICE_TYPE_VENDOR:
-               break;
-       }
-
-       switch (type) {
-       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_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;
        }
 
@@ -385,6 +475,40 @@ void connman_inet_update_device_ident(struct connman_device *device)
 }
 #endif
 
+bool connman_inet_is_ifup(int index)
+{
+       int sk;
+       struct ifreq ifr;
+       bool ret = false;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               connman_warn("Failed to open socket");
+               return false;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               connman_warn("Failed to get interface name for interface %d", index);
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               connman_warn("Failed to get interface flags for index %d", index);
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP)
+               ret = true;
+
+done:
+       close(sk);
+
+       return ret;
+}
+
 struct in6_ifreq {
        struct in6_addr ifr6_addr;
        __u32 ifr6_prefixlen;
@@ -533,7 +657,17 @@ int connman_inet_add_network_route(int index, const char *host,
 
        memset(&rt, 0, sizeof(rt));
        rt.rt_flags = RTF_UP;
-       if (gateway)
+
+       /*
+        * Set RTF_GATEWAY only when gateway is set and the gateway IP address
+        * is not IPv4 any address (0.0.0.0). If the given gateway IP address is
+        * any address adding of route will fail when RTF_GATEWAY set. Passing
+        * gateway as NULL or INADDR_ANY should have the same effect. Setting
+        * the gateway address later to the struct is not affected by this,
+        * since given IPv4 any address (0.0.0.0) equals the value set with
+        * INADDR_ANY.
+        */
+       if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET))
                rt.rt_flags |= RTF_GATEWAY;
        if (!netmask)
                rt.rt_flags |= RTF_HOST;
@@ -696,10 +830,17 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
 
        rt.rtmsg_flags = RTF_UP | RTF_HOST;
 
-       if (gateway) {
+       /*
+        * Set RTF_GATEWAY only when gateway is set, the gateway IP address is
+        * not IPv6 any address (e.g., ::) and the address is valid (conversion
+        * succeeds). If the given gateway IP address is any address then
+        * adding of route will fail when RTF_GATEWAY set. Passing gateway as
+        * NULL or IPv6 any address should have the same effect.
+        */
+
+       if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) &&
+               inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0)
                rt.rtmsg_flags |= RTF_GATEWAY;
-               inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
-       }
 
        rt.rtmsg_metric = 1;
        rt.rtmsg_ifindex = index;
@@ -1051,7 +1192,7 @@ bool connman_inet_compare_subnet(int index, const char *host)
                return false;
 
        if (inet_aton(host, &_host_addr) == 0)
-               return -1;
+               return false;
        host_addr = _host_addr.s_addr;
 
        sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
@@ -1307,13 +1448,12 @@ static gboolean rs_timeout_cb(gpointer user_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 xs_cb_data *data = user_data;
        struct nd_router_advert *hdr;
        struct sockaddr_in6 saddr;
        ssize_t len;
@@ -1335,7 +1475,6 @@ static int icmpv6_recv(int fd, gpointer user_data)
        len = recvmsg(fd, &mhdr, 0);
        if (len < 0) {
                cb(NULL, 0, data->user_data);
-               xs_cleanup(data);
                return -errno;
        }
 
@@ -1343,10 +1482,9 @@ static int icmpv6_recv(int fd, gpointer user_data)
        /* 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) {
-               xs_cleanup(data);
+                       == NULL)
                return -errno;
-       }
+
        DBG("Received Source Address %s from router", src_addr);
 
        /* icmpv6_recv() function can be called in two scenarios :
@@ -1377,7 +1515,6 @@ static int icmpv6_recv(int fd, gpointer user_data)
                return 0;
 
        cb(hdr, len, data->user_data);
-       xs_cleanup(data);
 
        return len;
 }
@@ -1385,18 +1522,21 @@ static int icmpv6_recv(int fd, gpointer user_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 */
@@ -1759,13 +1899,12 @@ void __connman_inet_ipv6_stop_recv_rs(void *context)
        xs_cleanup(context);
 }
 
-static int icmpv6_rs_recv(int fd, gpointer user_data)
+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 xs_cb_data *data = user_data;
        struct nd_router_solicit *hdr;
        struct sockaddr_in6 saddr;
        ssize_t len;
@@ -1804,17 +1943,20 @@ 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))
-               return FALSE;
+               goto cleanup;
 
        fd = g_io_channel_unix_get_fd(chan);
-       ret = icmpv6_rs_recv(fd, data);
+       ret = icmpv6_rs_recv(fd, xs_data);
        if (ret == 0)
                return TRUE;
 
+cleanup:
+       xs_data->watch_id = 0;
        return FALSE;
 }
 
@@ -1889,13 +2031,12 @@ static gboolean ns_timeout_cb(gpointer user_data)
        return FALSE;
 }
 
-static int icmpv6_nd_recv(int fd, gpointer user_data)
+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 xs_cb_data *data = user_data;
        struct nd_neighbor_advert *hdr;
        struct sockaddr_in6 saddr;
        ssize_t len;
@@ -1917,7 +2058,6 @@ static int icmpv6_nd_recv(int fd, gpointer user_data)
        len = recvmsg(fd, &mhdr, 0);
        if (len < 0) {
                cb(NULL, 0, &data->addr.sin6_addr, data->user_data);
-               xs_cleanup(data);
                return -errno;
        }
 
@@ -1936,7 +2076,6 @@ static int icmpv6_nd_recv(int fd, gpointer user_data)
                return 0;
 
        cb(hdr, len, &data->addr.sin6_addr, data->user_data);
-       xs_cleanup(data);
 
        return len;
 }
@@ -1945,18 +2084,21 @@ 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))
-               return FALSE;
+               goto cleanup;
 
        fd = g_io_channel_unix_get_fd(chan);
-       ret = icmpv6_nd_recv(fd, data);
+       ret = icmpv6_nd_recv(fd, xs_data);
        if (ret == 0)
                return TRUE;
 
-       return FALSE;
+cleanup:
+       xs_cleanup(xs_data);
+       return TRUE;
 }
 
 int __connman_inet_ipv6_do_dad(int index, int timeout_ms,
@@ -2280,9 +2422,8 @@ static gboolean inet_rtnl_timeout_cb(gpointer user_data)
        return FALSE;
 }
 
-static int inet_rtnl_recv(GIOChannel *chan, gpointer user_data)
+static int inet_rtnl_recv(GIOChannel *chan, struct inet_rtnl_cb_data *rtnl_data)
 {
-       struct inet_rtnl_cb_data *rtnl_data = user_data;
        struct __connman_inet_rtnl_handle *rth = rtnl_data->rtnl;
        struct nlmsghdr *h = NULL;
        struct sockaddr_nl nladdr;
@@ -2364,17 +2505,21 @@ 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))
-               return FALSE;
+               goto cleanup;
 
-       ret = inet_rtnl_recv(chan, user_data);
-       if (ret != 0)
+       ret = inet_rtnl_recv(chan, rtnl_data);
+       if (ret == 0)
                return TRUE;
 
-       return FALSE;
+cleanup:
+       rtnl_data->callback(NULL, rtnl_data->user_data);
+       inet_rtnl_cleanup(rtnl_data);
+       return TRUE;
 }
 
 int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
@@ -2383,13 +2528,12 @@ int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
 {
        struct sockaddr_nl nladdr;
        struct inet_rtnl_cb_data *data;
-       unsigned seq;
        int err;
 
        memset(&nladdr, 0, sizeof(nladdr));
        nladdr.nl_family = AF_NETLINK;
 
-       n->nlmsg_seq = seq = ++rtnl->seq;
+       n->nlmsg_seq = ++rtnl->seq;
 
        if (callback) {
                data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data));
@@ -2403,7 +2547,6 @@ int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
                                                inet_rtnl_timeout_cb, data);
 
                data->channel = g_io_channel_unix_new(rtnl->fd);
-               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);
@@ -2527,8 +2670,6 @@ out:
                data->callback(addr, index, data->user_data);
 
        g_free(data);
-
-       return;
 }
 
 /*
@@ -2620,9 +2761,10 @@ int connman_inet_check_ipaddress(const char *host)
        addr = NULL;
 
        result = getaddrinfo(host, NULL, &hints, &addr);
-       if (result == 0)
+       if (result == 0) {
                result = addr->ai_family;
-       freeaddrinfo(addr);
+               freeaddrinfo(addr);
+       }
 
        return result;
 }
@@ -2741,13 +2883,12 @@ char **__connman_inet_get_running_interfaces(void)
 
        g_free(ifr);
 
-       if (count < numif)
-       {
+       if (count < numif) {
                char **prev_result = result;
                result = g_try_realloc(result, (count + 1) * sizeof(char *));
                if (!result) {
                        g_free(prev_result);
-                       goto error;
+                       return NULL;
                }
        }
 
@@ -2825,6 +2966,41 @@ out:
        return err;
 }
 
+int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
+{
+       struct ifreq ifr;
+       int sk, err;
+       int ret = -EINVAL;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               DBG("Open socket error");
+               return ret;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               DBG("Get interface name error");
+               goto done;
+       }
+
+       err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+       if (err < 0) {
+               DBG("Get MAC address error");
+               goto done;
+       }
+
+       memcpy(mac_address, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+       ret = 0;
+
+done:
+       close(sk);
+       return ret;
+}
+
 static int iprule_modify(int cmd, int family, uint32_t table_id,
                        uint32_t fwmark)
 {
@@ -2887,12 +3063,15 @@ int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmar
 }
 
 static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
-                       const char *gateway)
+                       const char *gateway, unsigned char prefixlen)
 {
        struct __connman_inet_rtnl_handle rth;
        unsigned char buf[sizeof(struct in6_addr)];
        int ret, len;
        int family = connman_inet_check_ipaddress(gateway);
+       char *dst = NULL;
+
+       DBG("gateway %s/%u table %u", gateway, prefixlen, table_id);
 
        switch (family) {
        case AF_INET:
@@ -2905,7 +3084,19 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
                return -EINVAL;
        }
 
-       ret = inet_pton(family, gateway, buf);
+       if (prefixlen) {
+               struct in_addr ipv4_subnet_addr, ipv4_mask;
+
+               memset(&ipv4_subnet_addr, 0, sizeof(ipv4_subnet_addr));
+               ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+               ipv4_subnet_addr.s_addr = inet_addr(gateway);
+               ipv4_subnet_addr.s_addr &= ipv4_mask.s_addr;
+
+               dst = g_strdup(inet_ntoa(ipv4_subnet_addr));
+       }
+
+       ret = inet_pton(family, dst ? dst : gateway, buf);
+       g_free(dst);
        if (ret <= 0)
                return -EINVAL;
 
@@ -2920,9 +3111,11 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
        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;
+       rth.req.u.r.rt.rtm_dst_len = prefixlen;
+
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req),
+               prefixlen > 0 ? RTA_DST : RTA_GATEWAY, buf, len);
 
-       __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 {
@@ -2951,7 +3144,14 @@ int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex,
 {
        /* ip route add default via 1.2.3.4 dev wlan0 table 1234 */
 
-       return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway);
+       return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, 0);
+}
+
+int __connman_inet_add_subnet_to_table(uint32_t table_id, int ifindex,
+                                               const char *gateway, unsigned char prefixlen)
+{
+       /* ip route add 1.2.3.4/24 dev eth0 table 1234 */
+       return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, prefixlen);
 }
 
 int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex,
@@ -2959,7 +3159,14 @@ int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex,
 {
        /* ip route del default via 1.2.3.4 dev wlan0 table 1234 */
 
-       return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway);
+       return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, 0);
+}
+
+int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
+                                               const char *gateway, unsigned char prefixlen)
+{
+       /* ip route del 1.2.3.4/24 dev eth0 table 1234 */
+       return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, prefixlen);
 }
 
 int __connman_inet_get_interface_ll_address(int index, int family,
@@ -3058,3 +3265,303 @@ 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
+               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_test(pnp_file, G_FILE_TEST_EXISTS))
+               goto out;
+
+       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;
+}