*
* Connection Manager
*
- * Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
* Copyright (C) 2003-2005 Go-Core Project
* Copyright (C) 2003-2006 Helsinki University of Technology
*
#include <config.h>
#endif
-#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <ctype.h>
+#include <ifaddrs.h>
+#include <linux/fib_rules.h>
#include "connman.h"
+#include <gdhcp/gdhcp.h>
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t*) (nmsg)) + \
"prefixlen %hhu broadcast %s", cmd, flags, index, family,
address, peer, prefixlen, broadcast);
- if (address == NULL)
+ if (!address)
return -EINVAL;
if (family != AF_INET && family != AF_INET6)
if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
return -1;
- if (broadcast != NULL)
+ if (broadcast)
inet_pton(AF_INET, broadcast, &ipv4_bcast);
else
ipv4_bcast.s_addr = ipv4_addr.s_addr |
htonl(0xfffffffflu >> prefixlen);
- if (peer != NULL) {
+ if (peer) {
if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
return -1;
return err;
}
-int connman_inet_ifindex(const char *name)
+#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;
- if (name == NULL)
- return -1;
-
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ return NULL;
- err = ioctl(sk, SIOCGIFINDEX, &ifr);
+ strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
close(sk);
if (err < 0)
- return -1;
+ return NULL;
- return ifr.ifr_ifindex;
+ str = g_malloc(18);
+ if (!str)
+ return NULL;
+
+ memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+ snprintf(str, 13, "%02x%02x%02x%02x%02x%02x",
+ eth.ether_addr_octet[0],
+ eth.ether_addr_octet[1],
+ eth.ether_addr_octet[2],
+ eth.ether_addr_octet[3],
+ eth.ether_addr_octet[4],
+ eth.ether_addr_octet[5]);
+
+ return str;
}
-char *connman_inet_ifname(int index)
+char *connman_inet_ifname2addr(const char *name)
{
struct ifreq ifr;
+ struct ether_addr eth;
+ char *str;
int sk, err;
- if (index < 0)
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return NULL;
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+ close(sk);
+
+ if (err < 0)
return NULL;
+ str = g_malloc(18);
+ if (!str)
+ return NULL;
+
+ memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+ snprintf(str, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
+ eth.ether_addr_octet[0],
+ eth.ether_addr_octet[1],
+ eth.ether_addr_octet[2],
+ eth.ether_addr_octet[3],
+ eth.ether_addr_octet[4],
+ eth.ether_addr_octet[5]);
+
+ return str;
+}
+#endif
+
+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;
+ int sk, err;
+
+ if (!name)
+ return -1;
+
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
- return NULL;
+ return -1;
memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
- err = ioctl(sk, SIOCGIFNAME, &ifr);
+ err = ioctl(sk, SIOCGIFINDEX, &ifr);
close(sk);
if (err < 0)
- return NULL;
+ return -1;
- return strdup(ifr.ifr_name);
+ return ifr.ifr_ifindex;
}
-short int connman_inet_ifflags(int index)
+char *connman_inet_ifname(int index)
{
struct ifreq ifr;
int sk, err;
+ if (index < 0)
+ return NULL;
+
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
- return -errno;
+ return NULL;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- err = -errno;
- goto done;
- }
-
- if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
- err = -errno;
- goto done;
- }
-
- err = ifr.ifr_flags;
+ err = ioctl(sk, SIOCGIFNAME, &ifr);
-done:
close(sk);
- return err;
+ if (err < 0)
+ return NULL;
+
+ return g_strdup(ifr.ifr_name);
}
int connman_inet_ifup(int index)
}
memset(&addr_ifr, 0, sizeof(addr_ifr));
- memcpy(&addr_ifr.ifr_name, &ifr.ifr_name, sizeof(ifr.ifr_name));
+ memcpy(&addr_ifr.ifr_name, &ifr.ifr_name, sizeof(ifr.ifr_name) - 1);
addr = (struct sockaddr_in *)&addr_ifr.ifr_addr;
addr->sin_family = AF_INET;
if (ioctl(sk, SIOCSIFADDR, &addr_ifr) < 0)
return err;
}
-connman_bool_t connman_inet_is_cfg80211(int index)
+#if defined TIZEN_EXT
+void connman_inet_update_device_ident(struct connman_device *device)
+{
+ int index;
+ enum connman_device_type type;
+ char *ident = NULL, *addr = NULL;
+
+ index = connman_device_get_index(device);
+ type = connman_device_get_type(device);
+
+ switch (type) {
+ case CONNMAN_DEVICE_TYPE_UNKNOWN:
+ return;
+ case CONNMAN_DEVICE_TYPE_ETHERNET:
+ case CONNMAN_DEVICE_TYPE_GADGET:
+ case CONNMAN_DEVICE_TYPE_WIFI:
+ addr = index2addr(index);
+ ident = index2ident(index, NULL);
+ break;
+ case CONNMAN_DEVICE_TYPE_CELLULAR:
+ ident = index2ident(index, NULL);
+ break;
+ case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+ case CONNMAN_DEVICE_TYPE_GPS:
+ case CONNMAN_DEVICE_TYPE_VENDOR:
+ break;
+ }
+
+ if (ident != NULL) {
+ connman_device_set_ident(device, ident);
+ g_free(ident);
+ }
+
+ if (addr != NULL) {
+ connman_device_set_string(device, "Address", addr);
+ g_free(addr);
+ }
+}
+#endif
+
+bool connman_inet_is_ifup(int index)
{
- connman_bool_t result = FALSE;
- char phy80211_path[PATH_MAX];
- struct stat st;
- struct ifreq ifr;
int sk;
+ struct ifreq ifr;
+ bool ret = false;
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return FALSE;
+ 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)
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ connman_warn("Failed to get interface name for interface %d", index);
goto done;
+ }
- snprintf(phy80211_path, PATH_MAX,
- "/sys/class/net/%s/phy80211", ifr.ifr_name);
+ if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+ connman_warn("Failed to get interface flags for index %d", index);
+ goto done;
+ }
- if (stat(phy80211_path, &st) == 0 && (st.st_mode & S_IFDIR))
- result = TRUE;
+ if (ifr.ifr_flags & IFF_UP)
+ ret = true;
done:
close(sk);
- return result;
+ return ret;
}
struct in6_ifreq {
unsigned char prefix_len;
const char *address;
- if (ipaddress->local == NULL)
+ if (!ipaddress->local)
return 0;
prefix_len = ipaddress->prefixlen;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
- if (ipaddress->local == NULL)
+ if (!ipaddress->local)
return -1;
prefix_len = ipaddress->prefixlen;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+ if (!address)
+ return -EINVAL;
+
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
address, NULL, prefix_len, NULL);
if (err < 0) {
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
- DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+ DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
+ address, prefix_len, peer, broadcast);
+
+ if (!address)
+ return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
address, peer, prefix_len, broadcast);
struct ifreq ifr;
struct rtentry rt;
struct sockaddr_in addr;
- int sk, err;
+ int sk, err = 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;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
memset(&rt, 0, sizeof(rt));
rt.rt_flags = RTF_UP;
- if (gateway != NULL)
+
+ /*
+ * 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 == NULL)
+ if (!netmask)
rt.rt_flags |= RTF_HOST;
memset(&addr, 0, sizeof(addr));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- if (gateway != NULL)
+ if (gateway)
addr.sin_addr.s_addr = inet_addr(gateway);
else
addr.sin_addr.s_addr = INADDR_ANY;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
- if (netmask != NULL)
+ if (netmask)
addr.sin_addr.s_addr = inet_addr(netmask);
else
addr.sin_addr.s_addr = INADDR_ANY;
rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCADDRT, &rt);
- if (err < 0)
- connman_error("Adding host route failed (%s)",
- strerror(errno));
+ if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+ err = -errno;
close(sk);
+out:
+ if (err < 0)
+ connman_error("Adding host route failed (%s)",
+ strerror(-err));
+
return err;
}
struct ifreq ifr;
struct rtentry rt;
struct sockaddr_in addr;
- int sk, err;
+ int sk, err = 0;
DBG("index %d host %s", index, host);
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCDELRT, &rt);
- if (err < 0)
- connman_error("Deleting host route failed (%s)",
- strerror(errno));
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
close(sk);
+out:
+ if (err < 0)
+ connman_error("Deleting host route failed (%s)",
+ strerror(-err));
+
return err;
}
unsigned char prefix_len)
{
struct in6_rtmsg rt;
- int sk, err;
+ int sk, err = 0;
DBG("index %d host %s", index, host);
- if (host == NULL)
+ if (!host)
return -EINVAL;
memset(&rt, 0, sizeof(rt));
rt.rtmsg_dst_len = prefix_len;
- err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
- if (err < 0)
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ err = -errno;
goto out;
+ }
rt.rtmsg_flags = RTF_UP | RTF_HOST;
sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
- err = -1;
+ err = -errno;
goto out;
}
- err = ioctl(sk, SIOCDELRT, &rt);
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
+
close(sk);
+
out:
if (err < 0)
connman_error("Del IPv6 host route error (%s)",
- strerror(errno));
+ strerror(-err));
return err;
}
unsigned char prefix_len)
{
struct in6_rtmsg rt;
- int sk, err;
+ int sk, err = 0;
DBG("index %d host %s gateway %s", index, host, gateway);
- if (host == NULL)
+ if (!host)
return -EINVAL;
memset(&rt, 0, sizeof(rt));
rt.rtmsg_dst_len = prefix_len;
- err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
- if (err < 0)
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ err = -errno;
goto out;
+ }
rt.rtmsg_flags = RTF_UP | RTF_HOST;
- if (gateway != NULL) {
+ /*
+ * 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;
sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
- err = -1;
+ err = -errno;
goto out;
}
- err = ioctl(sk, SIOCADDRT, &rt);
+ if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+ err = -errno;
+
close(sk);
+
out:
if (err < 0)
connman_error("Set IPv6 host route error (%s)",
- strerror(errno));
+ strerror(-err));
return err;
}
return connman_inet_add_ipv6_network_route(index, host, gateway, 128);
}
-int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
-{
- struct in6_rtmsg rt;
- int sk, err;
-
- DBG("index %d gateway %s", index, gateway);
-
- if (gateway == NULL)
- return -EINVAL;
-
- memset(&rt, 0, sizeof(rt));
-
- err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
- if (err < 0)
- goto out;
-
- rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
- rt.rtmsg_metric = 1;
- rt.rtmsg_dst_len = 0;
- rt.rtmsg_ifindex = index;
-
- sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0) {
- err = -1;
- goto out;
- }
-
- err = ioctl(sk, SIOCADDRT, &rt);
- close(sk);
-out:
- if (err < 0)
- connman_error("Set default IPv6 gateway error (%s)",
- strerror(errno));
-
- return err;
-}
-
int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
{
struct in6_rtmsg rt;
- int sk, err;
+ int sk, err = 0;
DBG("index %d gateway %s", index, gateway);
- if (gateway == NULL)
+ if (!gateway)
return -EINVAL;
memset(&rt, 0, sizeof(rt));
- err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
- if (err < 0)
+ if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+ err = -errno;
goto out;
+ }
rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
rt.rtmsg_metric = 1;
sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
- err = -1;
+ err = -errno;
goto out;
}
- err = ioctl(sk, SIOCDELRT, &rt);
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
+
close(sk);
+
out:
if (err < 0)
connman_error("Clear default IPv6 gateway error (%s)",
- strerror(errno));
+ strerror(-err));
return err;
}
-int connman_inet_set_gateway_address(int index, const char *gateway)
+int connman_inet_set_gateway_interface(int index)
{
struct ifreq ifr;
struct rtentry rt;
struct sockaddr_in addr;
- int sk, err;
+ int sk, err = 0;
- DBG("index %d gateway %s", index, gateway);
+ DBG("index %d", index);
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
memset(&rt, 0, sizeof(rt));
- rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ rt.rt_flags = RTF_UP;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(gateway);
+ memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+ memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+ rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCADDRT, &rt);
- if (err < 0)
- connman_error("Setting default gateway route failed (%s)",
- strerror(errno));
+ if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+ err = -errno;
close(sk);
+out:
+ if (err < 0)
+ connman_error("Setting default interface route failed (%s)",
+ strerror(-err));
+
return err;
}
-int connman_inet_set_gateway_interface(int index)
-{
- struct ifreq ifr;
- struct rtentry rt;
- struct sockaddr_in addr;
- int sk, err;
-
- DBG("index %d", index);
-
- sk = socket(PF_INET, 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.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_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_set_ipv6_gateway_interface(int index)
+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;
+ int sk, err = 0;
DBG("index %d", index);
sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCADDRT, &rt);
+ if (ioctl(sk, SIOCADDRT, &rt) < 0 && errno != EEXIST)
+ err = -errno;
+
+ close(sk);
+
+out:
if (err < 0)
connman_error("Setting default interface route failed (%s)",
- strerror(errno));
- close(sk);
+ strerror(-err));
return err;
}
struct ifreq ifr;
struct rtentry rt;
struct sockaddr_in addr;
- int sk, err;
+ int sk, err = 0;
DBG("index %d gateway %s", index, gateway);
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
addr.sin_addr.s_addr = INADDR_ANY;
memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
- err = ioctl(sk, SIOCDELRT, &rt);
- if (err < 0)
- connman_error("Removing default gateway route failed (%s)",
- strerror(errno));
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
close(sk);
+out:
+ if (err < 0)
+ connman_error("Removing default gateway route failed (%s)",
+ strerror(-err));
+
return err;
}
struct ifreq ifr;
struct rtentry rt;
struct sockaddr_in addr;
- int sk, err;
+ int sk, err = 0;
DBG("index %d", index);
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCDELRT, &rt);
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
+
+ close(sk);
+
+out:
if (err < 0)
connman_error("Removing default interface route failed (%s)",
- strerror(errno));
- close(sk);
+ strerror(-err));
return err;
}
struct rtentry rt;
struct sockaddr_in6 addr;
const struct in6_addr any = IN6ADDR_ANY_INIT;
- int sk, err;
+ int sk, err = 0;
DBG("index %d", index);
sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -1;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
close(sk);
- return -1;
+ goto out;
}
DBG("ifname %s", ifr.ifr_name);
rt.rt_dev = ifr.ifr_name;
- err = ioctl(sk, SIOCDELRT, &rt);
+ if (ioctl(sk, SIOCDELRT, &rt) < 0 && errno != ESRCH)
+ err = -errno;
+
+ close(sk);
+
+out:
if (err < 0)
connman_error("Removing default interface route failed (%s)",
- strerror(errno));
- close(sk);
+ strerror(-err));
return err;
}
-connman_bool_t connman_inet_compare_subnet(int index, const char *host)
+bool connman_inet_compare_subnet(int index, const char *host)
{
struct ifreq ifr;
struct in_addr _host_addr;
DBG("host %s", host);
- if (host == NULL)
- return FALSE;
+ if (!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);
if (sk < 0)
- return FALSE;
+ return false;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = index;
if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
close(sk);
- return FALSE;
+ return false;
}
if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
close(sk);
- return FALSE;
+ return false;
}
netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
close(sk);
- return FALSE;
+ return false;
}
close(sk);
return ((if_addr & netmask_addr) == (host_addr & netmask_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;
+ }
+
+ return true;
+}
+
+bool connman_inet_compare_ipv6_subnet(int index, const char *host)
+{
+ struct ifaddrs *ifaddr, *ifa;
+ bool rv = false;
+ char name[IF_NAMESIZE];
+ struct in6_addr haddr;
+
+ if (inet_pton(AF_INET6, host, &haddr) <= 0)
+ return false;
+
+ if (!if_indextoname(index, name))
+ return false;
+
+ DBG("index %d interface %s", index, name);
+
+ if (getifaddrs(&ifaddr) < 0) {
+ DBG("Cannot get addresses err %d/%s", errno, strerror(errno));
+ return false;
+ }
+
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr_in6 *iaddr;
+ struct sockaddr_in6 *imask;
+
+ if (!ifa->ifa_addr)
+ continue;
+
+ if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 ||
+ ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+
+ iaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
+ imask = (struct sockaddr_in6 *)ifa->ifa_netmask;
+
+ rv = mem_mask_equal(&iaddr->sin6_addr, &haddr,
+ &imask->sin6_addr,
+ sizeof(haddr));
+ goto out;
+ }
+
+out:
+ freeifaddrs(ifaddr);
+ return rv;
+}
+
int connman_inet_remove_from_bridge(int index, const char *bridge)
{
struct ifreq ifr;
- int sk, err;
+ int sk, err = 0;
- if (bridge == NULL)
+ if (!bridge)
return -EINVAL;
sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return sk;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+ strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1);
ifr.ifr_ifindex = index;
- err = ioctl(sk, SIOCBRDELIF, &ifr);
+ if (ioctl(sk, SIOCBRDELIF, &ifr) < 0)
+ err = -errno;
close(sk);
- if (err < 0) {
+out:
+ if (err < 0)
connman_error("Remove interface from bridge error %s",
- strerror(errno));
- return err;
- }
+ strerror(-err));
- return 0;
+ return err;
}
int connman_inet_add_to_bridge(int index, const char *bridge)
{
struct ifreq ifr;
- int sk, err;
+ int sk, err = 0;
- if (bridge == NULL)
+ if (!bridge)
return -EINVAL;
sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return sk;
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+ strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1);
ifr.ifr_ifindex = index;
- err = ioctl(sk, SIOCBRADDIF, &ifr);
+ if (ioctl(sk, SIOCBRADDIF, &ifr) < 0)
+ err = -errno;
close(sk);
- if (err < 0) {
+out:
+ if (err < 0)
connman_error("Add interface to bridge error %s",
- strerror(errno));
- return err;
- }
+ strerror(-err));
- return 0;
+ return err;
}
int connman_inet_set_mtu(int index, int mtu)
__u32 mask;
__u32 flags;
- if (tunnel == NULL)
+ if (!tunnel)
return -EINVAL;
sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
goto done;
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, tunnel, IFNAMSIZ);
+ strncpy(ifr.ifr_name, tunnel, sizeof(ifr.ifr_name) - 1);
err = ioctl(sk, SIOCGIFFLAGS, &ifr);
if (err)
goto done;
return fd;
}
-struct rs_cb_data {
+/*
+ * This callback struct is used when sending router and neighbor
+ * solicitation and advertisement messages.
+ */
+struct xs_cb_data {
GIOChannel *channel;
- __connman_inet_rs_cb_t callback;
+ void *callback;
struct sockaddr_in6 addr;
- guint rs_timeout;
+ guint timeout;
guint watch_id;
void *user_data;
};
static const struct in6_addr in6addr_all_routers_mc =
IN6ADDR_ALL_ROUTERS_MC_INIT;
-static void rs_cleanup(struct rs_cb_data *data)
+static void xs_cleanup(struct xs_cb_data *data)
{
- if (data->channel != NULL) {
+ if (data->channel) {
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->timeout > 0)
+ g_source_remove(data->timeout);
if (data->watch_id > 0)
g_source_remove(data->watch_id);
static gboolean rs_timeout_cb(gpointer user_data)
{
- struct rs_cb_data *data = user_data;
+ struct xs_cb_data *data = user_data;
DBG("user data %p", user_data);
- if (data == NULL)
+ if (!data)
return FALSE;
- if (data->callback != NULL)
- data->callback(NULL, 0, data->user_data);
+ if (data->callback) {
+ __connman_inet_rs_cb_t cb = data->callback;
+ cb(NULL, 0, data->user_data);
+ }
- data->rs_timeout = 0;
- rs_cleanup(data);
+ data->timeout = 0;
+ xs_cleanup(data);
return FALSE;
}
-static int icmpv6_recv(int fd, gpointer user_data)
+static int icmpv6_recv(int fd, struct xs_cb_data *data)
{
struct msghdr mhdr;
struct iovec iov;
unsigned char chdr[CMSG_BUF_LEN];
unsigned char buf[1540];
- struct rs_cb_data *data = user_data;
struct nd_router_advert *hdr;
struct sockaddr_in6 saddr;
ssize_t len;
+ __connman_inet_rs_cb_t cb = data->callback;
DBG("");
mhdr.msg_name = (void *)&saddr;
mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ mhdr.msg_flags = 0;
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = (void *)chdr;
len = recvmsg(fd, &mhdr, 0);
if (len < 0) {
- data->callback(NULL, 0, data->user_data);
- rs_cleanup(data);
+ cb(NULL, 0, data->user_data);
return -errno;
}
+#if defined TIZEN_EXT
+ /* Set Received Source Address from router as IPv6 Gateway Address */
+ char src_addr[INET6_ADDRSTRLEN];
+ if(inet_ntop(AF_INET6, &(saddr.sin6_addr), src_addr, INET6_ADDRSTRLEN)
+ == NULL)
+ return -errno;
+
+ DBG("Received Source Address %s from router", src_addr);
+
+ /* icmpv6_recv() function can be called in two scenarios :
+ * 1. When __connman_inet_ipv6_send_rs() is called from check_dhcpv6()
+ * 2. When __connman_inet_ipv6_send_rs() is called from
+ * __connman_6to4_probe()
+ * In the second case it is not required to set DHCPv6 Gateway Address
+ * as DHCPv6 was not started and network structure was not passed as
+ * user_data. If it is tried to add Source Address as Gateway Address
+ * then it will lead to crash because of user_data being ip_address
+ * instead of network structure. So Adding Gateway Address in case 1st
+ * case only.
+ */
+ char *address = data->user_data;
+ int err = 0;
+ unsigned char buffer[sizeof(struct in6_addr)] = {0, };
+ /* Checking if user_data is an ip_address */
+ err = inet_pton(AF_INET, address, buffer);
+ /* Setting Received Source Address from
+ * router as Gateway Address */
+ if(err <= 0)
+ __connman_network_set_auto_ipv6_gateway(src_addr, data->user_data);
+#endif
hdr = (struct nd_router_advert *)buf;
DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len,
sizeof(struct nd_router_advert));
if (hdr->nd_ra_code != 0)
return 0;
- data->callback(hdr, len, data->user_data);
- rs_cleanup(data);
+ cb(hdr, len, data->user_data);
return len;
}
-static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond,
- gpointer data)
+static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond, gpointer data)
{
int fd, ret;
+ struct xs_cb_data *xs_data = data;
DBG("");
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
- return FALSE;
+ goto cleanup;
fd = g_io_channel_unix_get_fd(chan);
- ret = icmpv6_recv(fd, data);
+ ret = icmpv6_recv(fd, xs_data);
if (ret == 0)
return TRUE;
- return FALSE;
+cleanup:
+ xs_cleanup(xs_data);
+ return TRUE;
}
/* Adapted from RFC 1071 "C" Implementation Example */
-static uint16_t csum(const void *phdr, const void *data, socklen_t datalen)
+static uint16_t csum(const void *phdr, const void *data, socklen_t datalen,
+ const void *extra_data, socklen_t extra_datalen)
{
register unsigned long sum = 0;
socklen_t count;
count -= 2;
}
+ if (extra_data) {
+ count = extra_datalen;
+ addr = (uint16_t *)extra_data;
+
+ while (count > 1) {
+ sum += *(addr++);
+ count -= 2;
+ }
+ }
+
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}
-static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest)
+static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest,
+ const struct in6_addr *source,
+ unsigned char *buf, size_t len, uint16_t lifetime)
{
struct _phdr {
struct in6_addr src;
struct icmp6_hdr icmp;
struct nd_neighbor_solicit ns;
struct nd_router_solicit rs;
+ struct nd_router_advert ra;
} i;
} frame;
struct msghdr msgh;
struct cmsghdr *cmsg;
struct in6_pktinfo *pinfo;
- struct sockaddr_in6 dst;
+ struct sockaddr_in6 dst, src;
char cbuf[CMSG_SPACE(sizeof(*pinfo))];
- struct iovec iov;
- int fd, datalen, ret;
+ struct iovec iov[2];
+ int fd, datalen, ret, iovlen = 1;
+#if defined TIZEN_EXT
+ char ebuf[256];
+#endif
DBG("");
memset(&frame, 0, sizeof(frame));
memset(&dst, 0, sizeof(dst));
- datalen = sizeof(frame.i.rs); /* 8, csum() safe */
+ if (type == ND_ROUTER_SOLICIT)
+ datalen = sizeof(frame.i.rs); /* 8, csum() safe */
+ else if (type == ND_ROUTER_ADVERT) {
+ datalen = sizeof(frame.i.ra); /* 16, csum() safe */
+ frame.i.ra.nd_ra_router_lifetime = htons(lifetime);
+ } else if (type == ND_NEIGHBOR_SOLICIT) {
+ datalen = sizeof(frame.i.ns); /* 24, csum() safe */
+ memcpy(&frame.i.ns.nd_ns_target, buf, sizeof(struct in6_addr));
+ } else {
+ close(fd);
+ return -EINVAL;
+ }
+
dst.sin6_addr = *dest;
+ if (source)
+ src.sin6_addr = *source;
+ else
+ src.sin6_addr = in6addr_any;
+
/* Fill in the IPv6 header */
frame.ip.ip6_vfc = 0x60;
- frame.ip.ip6_plen = htons(datalen);
+ frame.ip.ip6_plen = htons(datalen + len);
frame.ip.ip6_nxt = IPPROTO_ICMPV6;
frame.ip.ip6_hlim = 255;
frame.ip.ip6_dst = dst.sin6_addr;
+ frame.ip.ip6_src = src.sin6_addr;
/* all other fields are already set to zero */
/* Prepare pseudo header for csum */
memset(&phdr, 0, sizeof(phdr));
phdr.dst = dst.sin6_addr;
- phdr.plen = htonl(datalen);
+ phdr.src = src.sin6_addr;
+ phdr.plen = htonl(datalen + len);
phdr.nxt = IPPROTO_ICMPV6;
/* Fill in remaining ICMP header fields */
frame.i.icmp.icmp6_type = type;
- frame.i.icmp.icmp6_cksum = csum(&phdr, &frame.i, datalen);
+ frame.i.icmp.icmp6_cksum = csum(&phdr, &frame.i, datalen, buf, len);
+
+ iov[0].iov_base = &frame;
+ iov[0].iov_len = sizeof(frame.ip) + datalen;
- iov.iov_base = &frame;
- iov.iov_len = sizeof(frame.ip) + datalen;
+ if (buf) {
+ iov[1].iov_base = buf;
+ iov[1].iov_len = len;
+ iovlen = 2;
+ }
dst.sin6_family = AF_INET6;
msgh.msg_name = &dst;
msgh.msg_namelen = sizeof(dst);
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = iovlen;
msgh.msg_flags = 0;
memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo)));
msgh.msg_controllen = cmsg->cmsg_len;
ret = sendmsg(fd, &msgh, 0);
+#if defined TIZEN_EXT
+ DBG("sendmsg errno: %d/%s", errno, strerror_r(errno, ebuf, sizeof(ebuf)));
+#endif
close(fd);
return ret;
ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&val, sizeof(int));
+ if (ret < 0) {
+ ret = -errno;
+ DBG("Cannot set IPV6_MULTICAST_LOOP %d/%s", ret,
+ strerror(-ret));
+ return ret;
+ }
- if (ret < 0)
+ ret = setsockopt(sock, IPPROTO_IPV6, cmd, &mreq, sizeof(mreq));
+ if (ret < 0) {
+ ret = -errno;
+ DBG("Cannot set option %d %d/%s", cmd, ret, strerror(-ret));
return ret;
+ }
- return setsockopt(sock, IPPROTO_IPV6, cmd, &mreq, sizeof(mreq));
+ return 0;
}
int __connman_inet_ipv6_send_rs(int index, int timeout,
__connman_inet_rs_cb_t callback, void *user_data)
{
- struct rs_cb_data *data;
+ struct xs_cb_data *data;
struct icmp6_filter filter;
struct in6_addr solicit;
struct in6_addr dst = in6addr_all_routers_mc;
int sk;
- DBG("");
-
if (timeout <= 0)
return -EINVAL;
- data = g_try_malloc0(sizeof(struct rs_cb_data));
- if (data == NULL)
+ data = g_try_malloc0(sizeof(struct xs_cb_data));
+ if (!data)
return -ENOMEM;
data->callback = callback;
data->user_data = user_data;
- data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data);
+ data->timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data);
sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
if (sk < 0)
return -errno;
+ DBG("sock %d", sk);
+
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
icmpv6_event, data);
- ndisc_send_unspec(ND_ROUTER_SOLICIT, index, &dst);
+ ndisc_send_unspec(ND_ROUTER_SOLICIT, index, &dst, NULL, NULL, 0, 0);
return 0;
}
-GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
- unsigned int length)
+static inline void ipv6_addr_advert_mult(const struct in6_addr *addr,
+ struct in6_addr *advert)
{
- GSList *prefixes = NULL;
- uint8_t *pos;
- int len;
+ ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2),
+ htonl(0xFF000000) | addr->s6_addr32[3]);
+}
- if (length <= sizeof(struct nd_router_advert))
- return NULL;
+#define MSG_SIZE_SEND 1452
- len = length - sizeof(struct nd_router_advert);
- pos = (uint8_t *)hdr + sizeof(struct nd_router_advert);
+static int inc_len(int len, int inc)
+{
+ if (len > MSG_SIZE_SEND)
+ return -EINVAL;
- while (len > 0) {
- struct nd_opt_prefix_info *pinfo;
- char prefix_str[INET6_ADDRSTRLEN+1], *str;
- const char *prefix;
- int optlen;
+ len += inc;
+ return len;
+}
- if (len < 2)
- break;
+int __connman_inet_ipv6_send_ra(int index, struct in6_addr *src_addr,
+ GSList *prefixes, int router_lifetime)
+{
+ GSList *list;
+ struct in6_addr src, *source;
+ struct in6_addr dst = in6addr_all_nodes_mc;
+ GDHCPIAPrefix *prefix;
+ unsigned char buf[MSG_SIZE_SEND];
+ char addr_str[INET6_ADDRSTRLEN];
+ int sk, err = 0;
+ int len, count = 0;
+
+ if (!prefixes)
+ return -EINVAL;
- optlen = pos[1] << 3;
- if (optlen == 0 || optlen > len)
- break;
+ sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+ if (sk < 0)
+ return -errno;
- 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;
+ if (!src_addr) {
+ __connman_inet_get_interface_ll_address(index, AF_INET6, &src);
+ source = &src;
+ } else
+ source = src_addr;
- str = g_strdup_printf("%s/%d", prefix,
- pinfo->nd_opt_pi_prefix_len);
- prefixes = g_slist_prepend(prefixes, str);
+ DBG("sock %d index %d prefixes %p src %s lifetime %d", sk, index,
+ prefixes, inet_ntop(AF_INET6, source, addr_str,
+ INET6_ADDRSTRLEN),
+ router_lifetime);
- DBG("prefix %s", str);
+ memset(buf, 0, MSG_SIZE_SEND);
+ len = 0;
- break;
+ for (list = prefixes; list; list = list->next) {
+ struct nd_opt_prefix_info *pinfo;
+
+ prefix = list->data;
+ pinfo = (struct nd_opt_prefix_info *)(buf + len);
+
+ len = inc_len(len, sizeof(*pinfo));
+ if (len < 0) {
+ err = len;
+ goto out;
}
- len -= optlen;
+ pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ pinfo->nd_opt_pi_len = 4;
+ pinfo->nd_opt_pi_prefix_len = prefix->prefixlen;
+ pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK;
+ pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
+ if (router_lifetime > 0) {
+ pinfo->nd_opt_pi_valid_time = htonl(prefix->valid);
+ pinfo->nd_opt_pi_preferred_time =
+ htonl(prefix->preferred);
+ }
+ pinfo->nd_opt_pi_reserved2 = 0;
+
+ memcpy(&pinfo->nd_opt_pi_prefix, &prefix->prefix,
+ sizeof(struct in6_addr));
+
+ DBG("[%d] index %d prefix %s/%d", count, index,
+ inet_ntop(AF_INET6, &prefix->prefix, addr_str,
+ INET6_ADDRSTRLEN), prefix->prefixlen);
+
+ count++;
+ }
+
+ if (count > 0) {
+ err = ndisc_send_unspec(ND_ROUTER_ADVERT, index, &dst, source,
+ buf, len, router_lifetime);
+ if (err < 0)
+ DBG("cannot send RA %d/%s", err, strerror(-err));
+ }
+
+out:
+ close(sk);
+ return err;
+}
+
+void __connman_inet_ipv6_stop_recv_rs(void *context)
+{
+ if (!context)
+ return;
+
+ xs_cleanup(context);
+}
+
+static int icmpv6_rs_recv(int fd, struct xs_cb_data *data)
+{
+ struct msghdr mhdr;
+ struct iovec iov;
+ unsigned char chdr[CMSG_BUF_LEN];
+ unsigned char buf[1540];
+ struct nd_router_solicit *hdr;
+ struct sockaddr_in6 saddr;
+ ssize_t len;
+ __connman_inet_recv_rs_cb_t cb = data->callback;
+
+ DBG("");
+
+ iov.iov_len = sizeof(buf);
+ iov.iov_base = buf;
+
+ mhdr.msg_name = (void *)&saddr;
+ mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ mhdr.msg_flags = 0;
+ mhdr.msg_iov = &iov;
+ mhdr.msg_iovlen = 1;
+ mhdr.msg_control = (void *)chdr;
+ mhdr.msg_controllen = CMSG_BUF_LEN;
+
+ len = recvmsg(fd, &mhdr, 0);
+ if (len < 0) {
+ cb(NULL, 0, data->user_data);
+ return -errno;
+ }
+
+ hdr = (struct nd_router_solicit *)buf;
+ DBG("code %d len %zd hdr %zd", hdr->nd_rs_code, len,
+ sizeof(struct nd_router_solicit));
+ if (hdr->nd_rs_code != 0)
+ return 0;
+
+ cb(hdr, len, data->user_data);
+ return len;
+}
+
+static gboolean icmpv6_rs_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ int fd, ret;
+ struct xs_cb_data *xs_data = data;
+
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ goto cleanup;
+
+ fd = g_io_channel_unix_get_fd(chan);
+ ret = icmpv6_rs_recv(fd, xs_data);
+ if (ret == 0)
+ return TRUE;
+
+cleanup:
+ xs_data->watch_id = 0;
+ return FALSE;
+}
+
+int __connman_inet_ipv6_start_recv_rs(int index,
+ __connman_inet_recv_rs_cb_t callback,
+ void *user_data,
+ void **context)
+{
+ struct xs_cb_data *data;
+ struct icmp6_filter filter;
+ char addr_str[INET6_ADDRSTRLEN];
+ int sk, err;
+
+ data = g_try_malloc0(sizeof(struct xs_cb_data));
+ if (!data)
+ return -ENOMEM;
+
+ data->callback = callback;
+ data->user_data = user_data;
+
+ sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+ if (sk < 0) {
+ g_free(data);
+ return -errno;
+ }
+
+ DBG("sock %d", sk);
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
+
+ setsockopt(sk, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+ sizeof(struct icmp6_filter));
+
+ err = if_mc_group(sk, index, &in6addr_all_routers_mc, IPV6_JOIN_GROUP);
+ if (err < 0)
+ DBG("Cannot join mc %s %d/%s", inet_ntop(AF_INET6,
+ &in6addr_all_routers_mc, addr_str, INET6_ADDRSTRLEN),
+ err, strerror(-err));
+
+ data->channel = g_io_channel_unix_new(sk);
+ g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+ g_io_channel_set_encoding(data->channel, NULL, NULL);
+ g_io_channel_set_buffered(data->channel, FALSE);
+
+ data->watch_id = g_io_add_watch(data->channel,
+ G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ icmpv6_rs_event, data);
+
+ *context = data;
+
+ return 0;
+}
+
+static gboolean ns_timeout_cb(gpointer user_data)
+{
+ struct xs_cb_data *data = user_data;
+
+ DBG("user data %p", user_data);
+
+ if (!data)
+ return FALSE;
+
+ if (data->callback) {
+ __connman_inet_ns_cb_t cb = data->callback;
+ cb(NULL, 0, &data->addr.sin6_addr, data->user_data);
+ }
+
+ data->timeout = 0;
+ xs_cleanup(data);
+ return FALSE;
+}
+
+static int icmpv6_nd_recv(int fd, struct xs_cb_data *data)
+{
+ struct msghdr mhdr;
+ struct iovec iov;
+ unsigned char chdr[CMSG_BUF_LEN];
+ unsigned char buf[1540];
+ struct nd_neighbor_advert *hdr;
+ struct sockaddr_in6 saddr;
+ ssize_t len;
+ __connman_inet_ns_cb_t cb = data->callback;
+
+ DBG("");
+
+ iov.iov_len = sizeof(buf);
+ iov.iov_base = buf;
+
+ mhdr.msg_name = (void *)&saddr;
+ mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ mhdr.msg_flags = 0;
+ mhdr.msg_iov = &iov;
+ mhdr.msg_iovlen = 1;
+ mhdr.msg_control = (void *)chdr;
+ mhdr.msg_controllen = CMSG_BUF_LEN;
+
+ len = recvmsg(fd, &mhdr, 0);
+ if (len < 0) {
+ cb(NULL, 0, &data->addr.sin6_addr, data->user_data);
+ return -errno;
+ }
+
+ hdr = (struct nd_neighbor_advert *)buf;
+ DBG("code %d len %zd hdr %zd", hdr->nd_na_code, len,
+ sizeof(struct nd_neighbor_advert));
+ if (hdr->nd_na_code != 0)
+ return 0;
+
+ /*
+ * We can receive any neighbor advertisement so we need to check if the
+ * packet was meant for us and ignore the packet otherwise.
+ */
+ if (memcmp(&data->addr.sin6_addr, &hdr->nd_na_target,
+ sizeof(struct in6_addr)))
+ return 0;
+
+ cb(hdr, len, &data->addr.sin6_addr, data->user_data);
+
+ return len;
+}
+
+static gboolean icmpv6_nd_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ int fd, ret;
+ struct xs_cb_data *xs_data = data;
+
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ goto cleanup;
+
+ fd = g_io_channel_unix_get_fd(chan);
+ ret = icmpv6_nd_recv(fd, xs_data);
+ if (ret == 0)
+ return TRUE;
+
+cleanup:
+ xs_cleanup(xs_data);
+ return TRUE;
+}
+
+int __connman_inet_ipv6_do_dad(int index, int timeout_ms,
+ struct in6_addr *addr,
+ __connman_inet_ns_cb_t callback,
+ void *user_data)
+{
+ struct xs_cb_data *data;
+ struct icmp6_filter filter;
+ struct in6_addr solicit;
+ int sk, err, val = 1;
+
+ if (timeout_ms <= 0)
+ return -EINVAL;
+
+ data = g_try_malloc0(sizeof(struct xs_cb_data));
+ if (!data)
+ return -ENOMEM;
+
+ data->callback = callback;
+ data->user_data = user_data;
+ data->timeout = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ (guint)timeout_ms,
+ ns_timeout_cb,
+ data,
+ NULL);
+ memcpy(&data->addr.sin6_addr, addr, sizeof(struct in6_addr));
+
+ sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+ if (sk < 0)
+ return -errno;
+
+ DBG("sock %d", sk);
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
+
+ setsockopt(sk, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+ sizeof(struct icmp6_filter));
+
+ if (setsockopt(sk, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &val, sizeof(val)) < 0) {
+ err = -errno;
+ DBG("Cannot set IPV6_RECVPKTINFO %d/%s", err,
+ strerror(-err));
+ close(sk);
+ return err;
+ }
+
+ if (setsockopt(sk, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &val, sizeof(val)) < 0) {
+ err = -errno;
+ DBG("Cannot set IPV6_RECVHOPLIMIT %d/%s", err,
+ strerror(-err));
+ close(sk);
+ return err;
+ }
+
+ val = 0;
+ setsockopt(sk, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val));
+
+ ipv6_addr_solict_mult(addr, &solicit);
+ if_mc_group(sk, index, &in6addr_all_nodes_mc, IPV6_JOIN_GROUP);
+ if_mc_group(sk, index, &solicit, IPV6_JOIN_GROUP);
+
+ data->channel = g_io_channel_unix_new(sk);
+ g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+ g_io_channel_set_encoding(data->channel, NULL, NULL);
+ g_io_channel_set_buffered(data->channel, FALSE);
+
+ data->watch_id = g_io_add_watch(data->channel,
+ G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ icmpv6_nd_event, data);
+
+ err = ndisc_send_unspec(ND_NEIGHBOR_SOLICIT, index, &solicit, NULL,
+ (unsigned char *)addr, 0, 0);
+ if (err < 0) {
+ DBG("Cannot send NS %d/%s", err, strerror(-err));
+ xs_cleanup(data);
+ }
+
+ return err;
+}
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+ unsigned int length)
+{
+ GSList *prefixes = NULL;
+ uint8_t *pos;
+ int len;
+
+ if (length <= sizeof(struct nd_router_advert))
+ return NULL;
+
+ len = length - sizeof(struct nd_router_advert);
+ pos = (uint8_t *)hdr + sizeof(struct nd_router_advert);
+
+ while (len > 0) {
+ struct nd_opt_prefix_info *pinfo;
+ char prefix_str[INET6_ADDRSTRLEN+1], *str;
+ const char *prefix;
+ int optlen;
+
+ if (len < 2)
+ break;
+
+ optlen = pos[1] << 3;
+ if (optlen == 0 || optlen > len)
+ break;
+
+ switch (pos[0]) {
+ case ND_OPT_PREFIX_INFORMATION:
+ pinfo = (struct nd_opt_prefix_info *)pos;
+ prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefix_str, INET6_ADDRSTRLEN);
+ if (!prefix)
+ break;
+
+ str = g_strdup_printf("%s/%d", prefix,
+ pinfo->nd_opt_pi_prefix_len);
+ prefixes = g_slist_prepend(prefixes, str);
+
+ DBG("prefix %s", str);
+
+ break;
+ }
+
+ len -= optlen;
pos += optlen;
}
return -errno;
}
- if (inet_ntop(family, addr, buf, len) == NULL) {
+ if (!inet_ntop(family, addr, buf, len)) {
DBG("error %d/%s", errno, strerror(errno));
return -errno;
}
{
struct __connman_inet_rtnl_handle *rth = data->rtnl;
- if (data->channel != NULL) {
+ if (data->channel) {
g_io_channel_shutdown(data->channel, TRUE, NULL);
g_io_channel_unref(data->channel);
data->channel = NULL;
if (data->watch_id > 0)
g_source_remove(data->watch_id);
- if (rth != NULL) {
+ if (rth) {
__connman_inet_rtnl_close(rth);
g_free(rth);
}
DBG("user data %p", user_data);
- if (data == NULL)
+ if (!data)
return FALSE;
- if (data->callback != NULL)
+ if (data->callback)
data->callback(NULL, data->user_data);
data->rtnl_timeout = 0;
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;
h = ptr;
- if (!NLMSG_OK(h, len)) {
+ if (!NLMSG_OK(h, len))
return -1;
- break;
- }
if (h->nlmsg_seq != rth->seq) {
/* Skip this msg */
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,
{
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 != NULL) {
+ if (callback) {
data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data));
- if (data == NULL)
+ if (!data)
return -ENOMEM;
data->callback = callback;
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);
err = sendto(rtnl->fd, &rtnl->req.n, rtnl->req.n.nlmsg_len, 0,
(struct sockaddr *) &nladdr, sizeof(nladdr));
- DBG("handle %p len %d err %d", rtnl, rtnl->req.n.nlmsg_len, err);
+ DBG("handle %p len %d", rtnl, rtnl->req.n.nlmsg_len);
if (err < 0) {
- connman_error("Can not talk to rtnetlink");
+ connman_error("Can not talk to rtnetlink err %d %s",
+ -errno, strerror(errno));
return -errno;
}
DBG("answer %p data %p", answer, user_data);
- if (answer == NULL)
+ if (!answer)
goto out;
len = answer->nlmsg_len;
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
- if (tb[RTA_OIF] != NULL)
+ if (tb[RTA_OIF])
index = *(int *)RTA_DATA(tb[RTA_OIF]);
- if (tb[RTA_GATEWAY] != NULL)
+ if (tb[RTA_GATEWAY])
addr = inet_ntop(r->rtm_family,
RTA_DATA(tb[RTA_GATEWAY]),
abuf, sizeof(abuf));
DBG("addr %s index %d user %p", addr, index, data->user_data);
out:
- if (data != NULL && data->callback != NULL)
+ if (data && data->callback)
data->callback(addr, index, data->user_data);
g_free(data);
-
- return;
}
/*
DBG("dest %s", dest_address);
- if (dest_address == NULL)
+ if (!dest_address)
return -EINVAL;
memset(&hints, 0, sizeof(hints));
return -EINVAL;
rth = g_try_malloc0(sizeof(struct __connman_inet_rtnl_handle));
- if (rth == NULL) {
+ if (!rth) {
freeaddrinfo(rp);
return -ENOMEM;
}
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);
goto fail;
data = g_try_malloc(sizeof(struct get_route_cb_data));
- if (data == NULL) {
+ if (!data) {
err = -ENOMEM;
goto done;
}
addr = NULL;
result = getaddrinfo(host, NULL, &hints, &addr);
- if (result == 0)
+ if (result == 0) {
result = addr->ai_family;
- freeaddrinfo(addr);
+ freeaddrinfo(addr);
+ }
return result;
}
/* Check routine modified from ics-dhcp 4.2.3-P2 */
-connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len)
+bool connman_inet_check_hostname(const char *ptr, size_t len)
{
const char *p;
* Not empty or complete length not over 255 characters.
*/
if ((len == 0) || (len > 256))
- return FALSE;
+ return false;
/*
* Consists of [[:alnum:]-]+ labels separated by [.]
* Not allowed at begin or end of a label.
*/
if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
- return FALSE;
+ return false;
} else if (*p == '.') {
/*
size_t d = p - ptr;
if ((d <= 0) || (d >= 64))
- return FALSE;
+ return false;
ptr = p + 1; /* Jump to the next label */
/*
* Also numbers at the begin are fine
*/
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
char **__connman_inet_get_running_interfaces(void)
* calls.
*/
ifr = g_try_malloc0(ifc.ifc_len * 2);
- if (ifr == NULL)
+ if (!ifr)
goto error;
ifc.ifc_req = ifr;
numif = ifc.ifc_len / sizeof(struct ifreq);
result = g_try_malloc0((numif + 1) * sizeof(char *));
- if (result == NULL)
+ if (!result)
goto error;
close(sk);
result[count++] = g_strdup(r->ifr_name);
}
- free(ifr);
+ 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);
+ return NULL;
+ }
+ }
return result;
error:
close(sk);
- free(ifr);
+ g_free(ifr);
return NULL;
}
-connman_bool_t connman_inet_is_ipv6_supported()
+bool connman_inet_is_ipv6_supported()
{
int sk;
sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
- return FALSE;
+ return false;
close(sk);
- return TRUE;
+ return true;
+}
+
+int __connman_inet_get_interface_address(int index, int family, void *address)
+{
+ struct ifaddrs *ifaddr, *ifa;
+ int err = -ENOENT;
+ char name[IF_NAMESIZE];
+
+ if (!if_indextoname(index, name))
+ return -EINVAL;
+
+ DBG("index %d interface %s", index, name);
+
+ if (getifaddrs(&ifaddr) < 0) {
+ err = -errno;
+ DBG("Cannot get addresses err %d/%s", err, strerror(-err));
+ return err;
+ }
+
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+
+ if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
+ ifa->ifa_addr->sa_family == family) {
+ if (family == AF_INET) {
+ struct sockaddr_in *in4 = (struct sockaddr_in *)
+ ifa->ifa_addr;
+ if (in4->sin_addr.s_addr == INADDR_ANY)
+ continue;
+ memcpy(address, &in4->sin_addr,
+ sizeof(struct in_addr));
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6 *in6 =
+ (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (memcmp(&in6->sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr)) == 0)
+ continue;
+ memcpy(address, &in6->sin6_addr,
+ sizeof(struct in6_addr));
+
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = 0;
+ break;
+ }
+ }
+
+out:
+ freeifaddrs(ifaddr);
+ return err;
+}
+
+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)
+{
+ struct __connman_inet_rtnl_handle rth;
+ int ret;
+
+ memset(&rth, 0, sizeof(rth));
+
+ rth.req.n.nlmsg_type = cmd;
+ rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ rth.req.n.nlmsg_flags = NLM_F_REQUEST;
+ rth.req.u.r.rt.rtm_family = family;
+ rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
+ rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ rth.req.u.r.rt.rtm_table = table_id;
+ rth.req.u.r.rt.rtm_type = RTN_UNSPEC;
+ rth.req.u.r.rt.rtm_flags = 0;
+
+ if (cmd == RTM_NEWRULE) {
+ rth.req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ rth.req.u.r.rt.rtm_type = RTN_UNICAST;
+ }
+
+ __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+ FRA_FWMARK, fwmark);
+
+ if (table_id < 256) {
+ rth.req.u.r.rt.rtm_table = table_id;
+ } else {
+ rth.req.u.r.rt.rtm_table = RT_TABLE_UNSPEC;
+ __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+ FRA_TABLE, table_id);
+ }
+
+ if (rth.req.u.r.rt.rtm_family == AF_UNSPEC)
+ rth.req.u.r.rt.rtm_family = AF_INET;
+
+ ret = __connman_inet_rtnl_open(&rth);
+ if (ret < 0)
+ goto done;
+
+ ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
+
+done:
+ __connman_inet_rtnl_close(&rth);
+
+ return ret;
+}
+
+int __connman_inet_add_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark)
+{
+ /* ip rule add fwmark 9876 table 1234 */
+
+ return iprule_modify(RTM_NEWRULE, family, table_id, fwmark);
+}
+
+int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark)
+{
+ return iprule_modify(RTM_DELRULE, family, table_id, fwmark);
+}
+
+static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
+ const char *gateway, 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:
+ len = 4;
+ break;
+ case AF_INET6:
+ len = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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;
+
+ memset(&rth, 0, sizeof(rth));
+
+ rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+ rth.req.n.nlmsg_type = cmd;
+ rth.req.u.r.rt.rtm_family = family;
+ rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN;
+ rth.req.u.r.rt.rtm_scope = RT_SCOPE_NOWHERE;
+ rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
+ rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ rth.req.u.r.rt.rtm_type = RTN_UNICAST;
+ 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);
+
+ if (table_id < 256) {
+ rth.req.u.r.rt.rtm_table = table_id;
+ } else {
+ rth.req.u.r.rt.rtm_table = RT_TABLE_UNSPEC;
+ __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+ RTA_TABLE, table_id);
+ }
+
+ __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+ RTA_OIF, ifindex);
+
+ ret = __connman_inet_rtnl_open(&rth);
+ if (ret < 0)
+ goto done;
+
+ ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
+
+done:
+ __connman_inet_rtnl_close(&rth);
+
+ return ret;
+}
+
+int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex,
+ const char *gateway)
+{
+ /* ip route add default via 1.2.3.4 dev wlan0 table 1234 */
+
+ return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, 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,
+ const char *gateway)
+{
+ /* ip route del default via 1.2.3.4 dev wlan0 table 1234 */
+
+ return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, 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);
+
+ if (getifaddrs(&ifaddr) < 0) {
+ err = -errno;
+ DBG("Cannot get addresses err %d/%s", err, strerror(-err));
+ return err;
+ }
+
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+
+ if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
+ ifa->ifa_addr->sa_family == family) {
+ if (family == AF_INET) {
+ struct sockaddr_in *in4 = (struct sockaddr_in *)
+ ifa->ifa_addr;
+ if (in4->sin_addr.s_addr == INADDR_ANY)
+ continue;
+ if ((in4->sin_addr.s_addr & IN_CLASSB_NET) !=
+ ((in_addr_t) 0xa9fe0000))
+ continue;
+ memcpy(address, &in4->sin_addr,
+ sizeof(struct in_addr));
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6 *in6 =
+ (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (memcmp(&in6->sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr)) == 0)
+ continue;
+ if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
+ continue;
+
+ memcpy(address, &in6->sin6_addr,
+ sizeof(struct in6_addr));
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = 0;
+ break;
+ }
+ }
+
+out:
+ freeifaddrs(ifaddr);
+ return err;
+}
+
+int __connman_inet_get_address_netmask(int ifindex,
+ struct sockaddr_in *address,
+ struct sockaddr_in *netmask)
+{
+ int sk, ret = -EINVAL;
+ struct ifreq ifr;
+
+ DBG("index %d", ifindex);
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EINVAL;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = ifindex;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0)
+ goto out;
+
+ if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0)
+ goto out;
+
+ memcpy(netmask, (struct sockaddr_in *)&ifr.ifr_netmask,
+ sizeof(struct sockaddr_in));
+
+ if (ioctl(sk, SIOCGIFADDR, &ifr) < 0)
+ goto out;
+
+ memcpy(address, (struct sockaddr_in *)&ifr.ifr_addr,
+ sizeof(struct sockaddr_in));
+ ret = 0;
+
+out:
+ close(sk);
+ return ret;
+}
+
+static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
+ struct in_addr *addr)
+{
+ char *s, *nfsargs;
+ size_t len;
+ char addrstr[INET_ADDRSTRLEN];
+ struct in_addr taddr;
+ GError *error = NULL;
+ char *cmdline = NULL;
+ char *pnp = NULL;
+ char **args = NULL;
+ char **pnpent = NULL;
+ char **pp = NULL;
+ int err = -1;
+
+ if (!cmdline_file)
+ cmdline_file = "/proc/cmdline";
+ if (!pnp_file)
+ pnp_file = "/proc/net/pnp";
+ if (!addr)
+ addr = &taddr;
+ addr->s_addr = INADDR_NONE;
+
+ if (!g_file_get_contents(cmdline_file, &cmdline, NULL, &error)) {
+ connman_error("%s: Cannot read %s %s\n", __func__,
+ cmdline_file, error->message);
+ goto out;
+ }
+
+ if (g_file_test(pnp_file, G_FILE_TEST_EXISTS)) {
+ if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) {
+ connman_error("%s: Cannot read %s %s\n", __func__,
+ pnp_file, error->message);
+ goto out;
+ }
+ } else
+ 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 (separated 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 retrieve 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;
}