DA: Skip initializing failed_bssids list when eapol failure case
[platform/upstream/connman.git] / src / inet.c
index 25e5372..268dc51 100644 (file)
@@ -25,7 +25,6 @@
 #include <config.h>
 #endif
 
-#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
@@ -80,7 +79,8 @@ int __connman_inet_modify_address(int cmd, int flags,
                                const char *address,
                                const char *peer,
                                unsigned char prefixlen,
-                               const char *broadcast)
+                               const char *broadcast,
+                               bool is_p2p)
 {
        uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
                        NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
@@ -95,8 +95,9 @@ int __connman_inet_modify_address(int cmd, int flags,
        int sk, err;
 
        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);
+               "prefixlen %hhu broadcast %s p2p %s", cmd, flags, index,
+               family, address, peer, prefixlen, broadcast,
+               is_p2p ? "true" : "false");
 
        if (!address)
                return -EINVAL;
@@ -120,17 +121,11 @@ int __connman_inet_modify_address(int cmd, int flags,
        ifaddrmsg->ifa_index = index;
 
        if (family == AF_INET) {
-               if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+               if (inet_pton(AF_INET, address, &ipv4_addr) != 1)
                        return -1;
 
-               if (broadcast)
-                       inet_pton(AF_INET, broadcast, &ipv4_bcast);
-               else
-                       ipv4_bcast.s_addr = ipv4_addr.s_addr |
-                               htonl(0xfffffffflu >> prefixlen);
-
                if (peer) {
-                       if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
+                       if (inet_pton(AF_INET, peer, &ipv4_dest) != 1)
                                return -1;
 
                        err = __connman_inet_rtnl_addattr_l(header,
@@ -150,16 +145,27 @@ int __connman_inet_modify_address(int cmd, int flags,
                if (err < 0)
                        return err;
 
-               err = __connman_inet_rtnl_addattr_l(header,
-                                               sizeof(request),
-                                               IFA_BROADCAST,
-                                               &ipv4_bcast,
-                                               sizeof(ipv4_bcast));
-               if (err < 0)
-                       return err;
+               /*
+                * Broadcast address must not be added for P2P / VPN as
+                * getifaddrs() cannot interpret destination address.
+                */
+               if (!is_p2p) {
+                       if (broadcast)
+                               inet_pton(AF_INET, broadcast, &ipv4_bcast);
+                       else
+                               ipv4_bcast.s_addr = ipv4_addr.s_addr |
+                                       htonl(0xfffffffflu >> prefixlen);
 
+                       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)
+               if (inet_pton(AF_INET6, address, &ipv6_addr) != 1)
                        return -1;
 
                err = __connman_inet_rtnl_addattr_l(header,
@@ -262,6 +268,66 @@ char *connman_inet_ifname2addr(const char *name)
 }
 #endif
 
+static bool is_addr_unspec(int family, struct sockaddr *addr)
+{
+       struct sockaddr_in *in4;
+       struct sockaddr_in6 *in6;
+
+       switch (family) {
+       case AF_INET:
+               in4 = (struct sockaddr_in*) addr;
+               return in4->sin_addr.s_addr == INADDR_ANY;
+       case AF_INET6:
+               in6 = (struct sockaddr_in6*) addr;
+               return IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+       default:
+               return false;
+       }
+}
+
+static bool is_addr_ll(int family, struct sockaddr *addr)
+{
+       struct sockaddr_in *in4;
+       struct sockaddr_in6 *in6;
+
+       switch (family) {
+       case AF_INET:
+               in4 = (struct sockaddr_in*) addr;
+               return (in4->sin_addr.s_addr & IN_CLASSB_NET) ==
+                                       ((in_addr_t) htonl(0xa9fe0000));
+       case AF_INET6:
+               in6 = (struct sockaddr_in6*) addr;
+               return IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr);
+       default:
+               return false;
+       }
+}
+
+bool __connman_inet_is_any_addr(const char *address, int family)
+{
+       bool ret = false;
+       struct addrinfo hints;
+       struct addrinfo *result = 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) {
+               ret = is_addr_unspec(result->ai_family, result->ai_addr);
+               freeaddrinfo(result);
+       }
+
+out:
+       return ret;
+}
+
 int connman_inet_ifindex(const char *name)
 {
        struct ifreq ifr;
@@ -442,6 +508,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;
@@ -454,18 +554,20 @@ int connman_inet_set_ipv6_address(int index,
        int err;
        unsigned char prefix_len;
        const char *address;
+       bool is_p2p;
 
        if (!ipaddress->local)
                return 0;
 
        prefix_len = ipaddress->prefixlen;
        address = ipaddress->local;
+       is_p2p = ipaddress->is_p2p;
 
        DBG("index %d address %s prefix_len %d", index, address, prefix_len);
 
        err = __connman_inet_modify_address(RTM_NEWADDR,
                                NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
-                               address, NULL, prefix_len, NULL);
+                               address, NULL, prefix_len, NULL, is_p2p);
        if (err < 0) {
                connman_error("%s: %s", __func__, strerror(-err));
                return err;
@@ -479,6 +581,7 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        int err;
        unsigned char prefix_len;
        const char *address, *broadcast, *peer;
+       bool is_p2p;
 
        if (!ipaddress->local)
                return -1;
@@ -487,12 +590,13 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        address = ipaddress->local;
        broadcast = ipaddress->broadcast;
        peer = ipaddress->peer;
+       is_p2p = ipaddress->is_p2p;
 
        DBG("index %d address %s prefix_len %d", index, address, prefix_len);
 
        err = __connman_inet_modify_address(RTM_NEWADDR,
                                NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
-                               address, peer, prefix_len, broadcast);
+                               address, peer, prefix_len, broadcast, is_p2p);
        if (err < 0) {
                connman_error("%s: %s", __func__, strerror(-err));
                return err;
@@ -501,10 +605,17 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        return 0;
 }
 
-int connman_inet_clear_ipv6_address(int index, const char *address,
-                                                       int prefix_len)
+int connman_inet_clear_ipv6_address(int index,
+                                       struct connman_ipaddress *ipaddress)
 {
        int err;
+       int prefix_len;
+       const char *address;
+       bool is_p2p;
+
+       address = ipaddress->local;
+       prefix_len = ipaddress->prefixlen;
+       is_p2p = ipaddress->is_p2p;
 
        DBG("index %d address %s prefix_len %d", index, address, prefix_len);
 
@@ -512,7 +623,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address,
                return -EINVAL;
 
        err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
-                               address, NULL, prefix_len, NULL);
+                               address, NULL, prefix_len, NULL, is_p2p);
        if (err < 0) {
                connman_error("%s: %s", __func__, strerror(-err));
                return err;
@@ -526,11 +637,13 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
        int err;
        unsigned char prefix_len;
        const char *address, *broadcast, *peer;
+       bool is_p2p;
 
        prefix_len = ipaddress->prefixlen;
        address = ipaddress->local;
        broadcast = ipaddress->broadcast;
        peer = ipaddress->peer;
+       is_p2p = ipaddress->is_p2p;
 
        DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
                address, prefix_len, peer, broadcast);
@@ -539,7 +652,7 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
                return -EINVAL;
 
        err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
-                               address, peer, prefix_len, broadcast);
+                               address, peer, prefix_len, broadcast, is_p2p);
        if (err < 0) {
                connman_error("%s: %s", __func__, strerror(-err));
                return err;
@@ -590,7 +703,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,7 +819,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
 
        rt.rtmsg_dst_len = prefix_len;
 
-       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
                err = -errno;
                goto out;
        }
@@ -746,17 +869,24 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
 
        rt.rtmsg_dst_len = prefix_len;
 
-       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+       if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
                err = -errno;
                goto out;
        }
 
        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) == 1)
                rt.rtmsg_flags |= RTF_GATEWAY;
-               inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
-       }
 
        rt.rtmsg_metric = 1;
        rt.rtmsg_ifindex = index;
@@ -798,7 +928,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
 
        memset(&rt, 0, sizeof(rt));
 
-       if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+       if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) != 1) {
                err = -errno;
                goto out;
        }
@@ -1094,54 +1224,198 @@ out:
        return err;
 }
 
+#define ADDR_TYPE_MAX 4
+
+struct interface_address {
+       int index;
+       int family;
+       bool allow_unspec;
+        /* Applies only to ADDR_TYPE_IPADDR in ipaddrs */
+       bool require_ll;
+       /* Real types must be in_addr for AF_INET and in6_addr for AF_INET6 */
+       void *ipaddrs[ADDR_TYPE_MAX];
+};
+
+enum ipaddr_type {
+       ADDR_TYPE_IPADDR = 0,
+       ADDR_TYPE_NETMASK,
+       ADDR_TYPE_BRDADDR,
+       ADDR_TYPE_DSTADDR
+};
+
+static int get_interface_addresses(struct interface_address *if_addr)
+{
+       struct ifaddrs *ifaddr;
+       struct ifaddrs *ifa;
+       struct sockaddr *addrs[ADDR_TYPE_MAX] = { 0 };
+       struct sockaddr_in *addr_in;
+       struct sockaddr_in6 *addr_in6;
+       char name[IF_NAMESIZE] = { 0 };
+       size_t len;
+       int err = -ENOENT;
+       int i;
+
+       if (!if_addr)
+               return -EINVAL;
+
+       if (!if_indextoname(if_addr->index, name))
+               return -EINVAL;
+
+       DBG("index %d interface %s", if_addr->index, name);
+
+       if (getifaddrs(&ifaddr) < 0) {
+               connman_error("Cannot get addresses err %d/%s", errno,
+                                                       strerror(errno));
+               return -errno;
+       }
+
+       for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+               if (!ifa->ifa_addr)
+                       continue;
+
+               if (g_strcmp0(ifa->ifa_name, name) ||
+                                       ifa->ifa_addr->sa_family !=
+                                               if_addr->family)
+                       continue;
+
+
+               if (if_addr->ipaddrs[ADDR_TYPE_IPADDR]) {
+                       if (!if_addr->allow_unspec && is_addr_unspec(
+                                               if_addr->family,
+                                               ifa->ifa_addr))
+                               continue;
+
+                       if (if_addr->require_ll && !is_addr_ll(if_addr->family,
+                                               ifa->ifa_addr))
+                               continue;
+
+                       addrs[ADDR_TYPE_IPADDR] = ifa->ifa_addr;
+               }
+
+               if (if_addr->ipaddrs[ADDR_TYPE_NETMASK]) {
+                       if (!if_addr->allow_unspec && is_addr_unspec(
+                                               if_addr->family,
+                                               ifa->ifa_netmask))
+                               continue;
+
+                       addrs[ADDR_TYPE_NETMASK] = ifa->ifa_netmask;
+               }
+
+               if (if_addr->ipaddrs[ADDR_TYPE_BRDADDR] &&
+                                       (ifa->ifa_flags & IFF_BROADCAST)) {
+                       if (!if_addr->allow_unspec && is_addr_unspec(
+                                               if_addr->family,
+                                               ifa->ifa_ifu.ifu_broadaddr))
+                               continue;
+
+                       addrs[ADDR_TYPE_BRDADDR] = ifa->ifa_ifu.ifu_broadaddr;
+               }
+
+               if (if_addr->ipaddrs[ADDR_TYPE_DSTADDR] &&
+                                       (ifa->ifa_flags & IFF_POINTOPOINT)) {
+                       if (!if_addr->allow_unspec && is_addr_unspec(
+                                               if_addr->family,
+                                               ifa->ifa_ifu.ifu_dstaddr))
+                               continue;
+
+                       addrs[ADDR_TYPE_DSTADDR] = ifa->ifa_ifu.ifu_dstaddr;
+               }
+
+               err = 0;
+
+               break;
+       }
+
+       if (err)
+               goto out;
+
+       for (i = 0; i < ADDR_TYPE_MAX; i++) {
+               if (!addrs[i])
+                       continue;
+
+               switch (if_addr->family) {
+               case AF_INET:
+                       len = sizeof(struct in_addr);
+                       addr_in = (struct sockaddr_in*) addrs[i];
+                       memcpy(if_addr->ipaddrs[i], &addr_in->sin_addr, len);
+                       break;
+               case AF_INET6:
+                       len = sizeof(struct in6_addr);
+                       addr_in6 = (struct sockaddr_in6*) addrs[i];
+                       memcpy(if_addr->ipaddrs[i], &addr_in6->sin6_addr, len);
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+out:
+       freeifaddrs(ifaddr);
+       return err;
+}
+
 bool connman_inet_compare_subnet(int index, const char *host)
 {
-       struct ifreq ifr;
-       struct in_addr _host_addr;
-       in_addr_t host_addr, netmask_addr, if_addr;
-       struct sockaddr_in *netmask, *addr;
-       int sk;
+       struct interface_address if_addr = { 0 };
+       struct in_addr iaddr = { 0 };
+       struct in_addr imask = { 0 };
+       struct in_addr haddr = { 0 };
 
        DBG("host %s", host);
 
        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 | SOCK_CLOEXEC, 0);
-       if (sk < 0)
+       if (inet_pton(AF_INET, host, &haddr) != 1)
                return false;
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = index;
+       if_addr.index = index;
+       if_addr.family = AF_INET;
+       if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+       if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
 
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               close(sk);
+       if (get_interface_addresses(&if_addr))
                return false;
-       }
 
-       if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
-               close(sk);
-               return false;
+       return (iaddr.s_addr & imask.s_addr) == (haddr.s_addr & imask.s_addr);
+}
+
+static bool mem_mask_equal(const void *a, const void *b,
+                                       const void *mask, size_t n)
+{
+       const unsigned char *addr1 = a;
+       const unsigned char *addr2 = b;
+       const unsigned char *bitmask = mask;
+       size_t i;
+
+       for (i = 0; i < n; i++) {
+               if ((addr1[i] ^ addr2[i]) & bitmask[i])
+                       return false;
        }
 
-       netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
-       netmask_addr = netmask->sin_addr.s_addr;
+       return true;
+}
 
-       if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
-               close(sk);
+bool connman_inet_compare_ipv6_subnet(int index, const char *host)
+{
+       struct interface_address addr = { 0 };
+       struct in6_addr iaddr = { 0 };
+       struct in6_addr imask = { 0 };
+       struct in6_addr haddr = { 0 };
+
+       if (inet_pton(AF_INET6, host, &haddr) != 1)
                return false;
-       }
 
-       close(sk);
+       addr.index = index;
+       addr.family = AF_INET6;
+       addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+       addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
 
-       addr = (struct sockaddr_in *)&ifr.ifr_addr;
-       if_addr = addr->sin_addr.s_addr;
+       if (get_interface_addresses(&addr))
+               return false;
 
-       return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
+       return mem_mask_equal(&iaddr, &haddr, &imask, sizeof(haddr));
 }
 
 int connman_inet_remove_from_bridge(int index, const char *bridge)
@@ -2150,98 +2424,156 @@ GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
        return prefixes;
 }
 
-static int get_dest_addr(int family, int index, char *buf, int len)
+int connman_inet_get_dest_addr(int index, char **dest)
 {
-       struct ifreq ifr;
-       void *addr;
-       int sk;
+       struct interface_address if_addr = { 0 };
+       struct in_addr dstaddr = { 0 };
+       char buf[INET_ADDRSTRLEN] = { 0 };
+       int err;
 
-       sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-       if (sk < 0)
-               return -errno;
+       if (!dest)
+               return -EINVAL;
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = index;
+       if_addr.index = index;
+       if_addr.family = AF_INET;
+       if_addr.allow_unspec = true;
+       if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
 
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
-               close(sk);
-               return -errno;
-       }
+       err = get_interface_addresses(&if_addr);
+       if (err)
+               return err;
 
-       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
-               DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
-               close(sk);
-               return -errno;
-       }
+       if (inet_ntop(AF_INET, &dstaddr, buf, INET_ADDRSTRLEN))
+               *dest = g_strdup(buf);
 
-       if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
-               close(sk);
-               errno = EINVAL;
-               return -errno;
-       }
+       DBG("destination %s", *dest);
 
-       DBG("index %d %s", index, ifr.ifr_name);
+       return *dest && **dest ? 0 : -ENOENT;
+}
 
-       if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
-               connman_error("Get destination address failed (%s)",
-                                                       strerror(errno));
-               close(sk);
-               return -errno;
-       }
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+       struct interface_address if_addr = { 0 };
+       struct in_addr dstaddr = { 0 };
+       char buf[INET6_ADDRSTRLEN] = { 0 };
+       int err;
 
-       close(sk);
+       if (!dest)
+               return -EINVAL;
 
-       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_addr.index = index;
+       if_addr.family = AF_INET6;
+       if_addr.allow_unspec = true;
+       if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
 
-       if (!inet_ntop(family, addr, buf, len)) {
-               DBG("error %d/%s", errno, strerror(errno));
-               return -errno;
-       }
+       err = get_interface_addresses(&if_addr);
+       if (err)
+               return err;
 
-       return 0;
+       if (inet_ntop(AF_INET6, &dstaddr, buf, INET6_ADDRSTRLEN))
+               *dest = g_strdup(buf);
+
+       DBG("destination %s", *dest);
+
+       return *dest && **dest ? 0 : -ENOENT;
 }
 
-int connman_inet_get_dest_addr(int index, char **dest)
+/* destination is optional */
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+                                                       char **destination)
 {
-       char addr[INET_ADDRSTRLEN];
-       int ret;
+       struct interface_address if_addr = { 0 };
+       struct in_addr addr = { 0 };
+       struct in_addr mask = { 0 };
+       struct in_addr dest = { 0 };
+       struct in_addr nw_addr = { 0 };
+       char buf[INET_ADDRSTRLEN] = { 0 };
+       int err;
 
-       ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
-       if (ret < 0)
-               return ret;
+       if (!network || !netmask)
+               return -EINVAL;
 
-       *dest = g_strdup(addr);
+       if_addr.index = index;
+       if_addr.family = AF_INET;
+       if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+       if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+       if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
 
-       DBG("destination %s", *dest);
+       err = get_interface_addresses(&if_addr);
+       if (err)
+               return err;
 
-       return 0;
+       nw_addr.s_addr = (addr.s_addr & mask.s_addr);
+
+       if (inet_ntop(AF_INET, &nw_addr, buf, INET_ADDRSTRLEN))
+               *network = g_strdup(buf);
+
+       memset(&buf, 0, INET_ADDRSTRLEN);
+
+       if (inet_ntop(AF_INET, &mask, buf, INET_ADDRSTRLEN))
+               *netmask = g_strdup(buf);
+
+       if (destination) {
+               memset(&buf, 0, INET_ADDRSTRLEN);
+
+               if (inet_ntop(AF_INET, &dest, buf, INET_ADDRSTRLEN))
+                       *destination = g_strdup(buf);
+       }
+
+       DBG("network %s netmask %s destination %s", *network, *netmask,
+                               destination ? *destination : NULL);
+
+       return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
 }
 
-int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+                                       char **netmask, char **destination)
 {
-       char addr[INET6_ADDRSTRLEN];
-       int ret;
+       struct interface_address if_addr = { 0 };
+       struct in6_addr addr = { 0 };
+       struct in6_addr mask = { 0 };
+       struct in6_addr dest = { 0 };
+       struct in6_addr nw_addr  = { 0 };
+       char buf[INET6_ADDRSTRLEN] = { 0 };
+       int err;
 
-       ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
-       if (ret < 0)
-               return ret;
+       if (!network)
+               return -EINVAL;
 
-       *dest = g_strdup(addr);
+       if_addr.index = index;
+       if_addr.family = AF_INET6;
+       if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+       if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+       if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
 
-       DBG("destination %s", *dest);
+       err = get_interface_addresses(&if_addr);
+       if (err)
+               return err;
 
-       return 0;
+       ipv6_addr_set(&nw_addr, addr.s6_addr32[0] & mask.s6_addr32[0],
+                               addr.s6_addr32[1] & mask.s6_addr32[1],
+                               addr.s6_addr32[2] & mask.s6_addr32[2],
+                               addr.s6_addr32[3] & mask.s6_addr32[3]);
+
+       if (inet_ntop(AF_INET6, &nw_addr, buf, INET6_ADDRSTRLEN))
+               *network = g_strdup(buf);
+
+       memset(&buf, 0, INET6_ADDRSTRLEN);
+
+       if (inet_ntop(AF_INET6, &mask, buf, INET6_ADDRSTRLEN))
+               *netmask = g_strdup(buf);
+
+       if (destination) {
+               memset(&buf, 0, INET6_ADDRSTRLEN);
+
+               if (inet_ntop(AF_INET6, &dest, buf, INET6_ADDRSTRLEN))
+                       *destination = g_strdup(buf);
+       }
+
+       DBG("network %s netmask %s destination %s", *network, *netmask,
+                               destination ? *destination : NULL);
+
+       return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
 }
 
 int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
@@ -2433,6 +2765,7 @@ static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond,
                return TRUE;
 
 cleanup:
+       rtnl_data->callback(NULL, rtnl_data->user_data);
        inet_rtnl_cleanup(rtnl_data);
        return TRUE;
 }
@@ -2585,8 +2918,6 @@ out:
                data->callback(addr, index, data->user_data);
 
        g_free(data);
-
-       return;
 }
 
 /*
@@ -2628,11 +2959,21 @@ int __connman_inet_get_route(const char *dest_address,
        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);
+       if (rp->ai_family == AF_INET) {
+               struct sockaddr_in *sin = (struct sockaddr_in *)rp->ai_addr;
+
+               rth->req.u.r.rt.rtm_dst_len = 32;
+               __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req),
+                       RTA_DST, &sin->sin_addr, sizeof(sin->sin_addr));
+       } else if (rp->ai_family == AF_INET6) {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rp->ai_addr;
+
+               rth->req.u.r.rt.rtm_dst_len = 128;
+               __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req),
+                       RTA_DST, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+       }
 
        freeaddrinfo(rp);
 
@@ -2678,9 +3019,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;
 }
@@ -2799,8 +3141,7 @@ 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) {
@@ -2829,58 +3170,65 @@ bool connman_inet_is_ipv6_supported()
        return true;
 }
 
+/*
+ * Omits checking of the gateway matching the actual gateway IP since both
+ * connmand and vpnd use inet.c, getting the route is via ipconfig and ipconfig
+ * is different for both. Gateway is left here for possible future use.
+ *
+ * Gateway can be NULL and connection.c then assigns 0.0.0.0 address or ::,
+ * depending on IP family.
+ */
+bool connman_inet_is_default_route(int family, const char *host,
+                               const char *gateway, const char *netmask)
+{
+       return __connman_inet_is_any_addr(host, family) &&
+                               __connman_inet_is_any_addr(netmask, family);
+}
+
 int __connman_inet_get_interface_address(int index, int family, void *address)
 {
-       struct ifaddrs *ifaddr, *ifa;
-       int err = -ENOENT;
-       char name[IF_NAMESIZE];
+       struct interface_address if_addr = { 0 };
 
-       if (!if_indextoname(index, name))
-               return -EINVAL;
+       if_addr.index = index;
+       if_addr.family = family;
+       if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
 
-       DBG("index %d interface %s", index, name);
+       return get_interface_addresses(&if_addr);
+}
 
-       if (getifaddrs(&ifaddr) < 0) {
-               err = -errno;
-               DBG("Cannot get addresses err %d/%s", err, strerror(-err));
-               return err;
-       }
+int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
+{
+       struct ifreq ifr;
+       int sk, err;
+       int ret = -EINVAL;
 
-       for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
-               if (!ifa->ifa_addr)
-                       continue;
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               DBG("Open socket error");
+               return ret;
+       }
 
-               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));
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
 
-                       } else {
-                               err = -EINVAL;
-                               goto out;
-                       }
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               DBG("Get interface name error");
+               goto done;
+       }
 
-                       err = 0;
-                       break;
-               }
+       err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+       if (err < 0) {
+               DBG("Get MAC address error");
+               goto done;
        }
 
-out:
-       freeifaddrs(ifaddr);
-       return err;
+       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,
@@ -2945,12 +3293,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:
@@ -2963,8 +3314,20 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
                return -EINVAL;
        }
 
-       ret = inet_pton(family, gateway, buf);
-       if (ret <= 0)
+       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 != 1)
                return -EINVAL;
 
        memset(&rth, 0, sizeof(rth));
@@ -2978,9 +3341,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 {
@@ -3009,7 +3374,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,
@@ -3017,67 +3389,27 @@ 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,
                                                                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);
+       struct interface_address if_addr = { 0 };
 
-       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;
-               }
-       }
+       if_addr.index = index;
+       if_addr.family = family;
+       if_addr.require_ll = true;
+       if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
 
-out:
-       freeifaddrs(ifaddr);
-       return err;
+       return get_interface_addresses(&if_addr);
 }
 
 int __connman_inet_get_address_netmask(int ifindex,
@@ -3152,10 +3484,8 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
                                                  pnp_file, error->message);
                        goto out;
                }
-       } else {
-               connman_error("%s: File %s doesn't exist\n", __func__, pnp_file);
+       } else
                goto out;
-       }
 
        len = strlen(cmdline);
        if (len <= 1) {
@@ -3166,7 +3496,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
        if (cmdline[len - 1] == '\n')
                cmdline[--len] = '\0';
 
-       /* split in arguments (seperated by space) */
+       /* split in arguments (separated by space) */
        args = g_strsplit(cmdline, " ", 0);
        if (!args) {
                connman_error("%s: Cannot split cmdline \"%s\"\n", __func__,
@@ -3233,7 +3563,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
        addrstr[len] = '\0';
 
        err = inet_pton(AF_INET, addrstr, addr);
-       if (err <= 0) {
+       if (err != 1) {
                connman_error("%s: Cannot convert to numeric addr \"%s\"\n",
                                __func__, addrstr);
                err = -1;
@@ -3351,6 +3681,9 @@ char **__connman_inet_get_pnp_nameservers(const char *pnp_file)
        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);
@@ -3366,7 +3699,7 @@ char **__connman_inet_get_pnp_nameservers(const char *pnp_file)
        }
 
        /*
-        * Perform two passes to retreive a char ** array of
+        * Perform two passes to retrieve a char ** array of
         * nameservers that are not 0.0.0.0
         *
         * The first pass counts them, the second fills in the