*
* Connection Manager
*
- * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2012 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 <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/sockios.h>
+#include <netdb.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <net/ethernet.h>
#include <netinet/icmp6.h>
#include <fcntl.h>
#include <linux/if_tun.h>
+#include <ctype.h>
#include "connman.h"
((struct rtattr *) (((uint8_t*) (nmsg)) + \
NLMSG_ALIGN((nmsg)->nlmsg_len)))
-static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type,
- const void *data, size_t data_length)
+int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length,
+ int type, const void *data, size_t data_length)
{
size_t length;
struct rtattr *rta;
struct in_addr ipv4_addr, ipv4_dest, ipv4_bcast;
int sk, err;
- DBG("");
+ DBG("cmd %#x flags %#x index %d family %d address %s peer %s "
+ "prefixlen %hhu broadcast %s", cmd, flags, index, family,
+ address, peer, prefixlen, broadcast);
if (address == NULL)
- return -1;
+ return -EINVAL;
if (family != AF_INET && family != AF_INET6)
- return -1;
+ return -EINVAL;
memset(&request, 0, sizeof(request));
if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
return -1;
- if ((err = add_rtattr(header, sizeof(request),
- IFA_ADDRESS,
- &ipv4_dest, sizeof(ipv4_dest))) < 0)
- return err;
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_ADDRESS,
+ &ipv4_dest,
+ sizeof(ipv4_dest));
+ if (err < 0)
+ return err;
}
- if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL,
- &ipv4_addr, sizeof(ipv4_addr))) < 0)
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_LOCAL,
+ &ipv4_addr,
+ sizeof(ipv4_addr));
+ if (err < 0)
return err;
- if ((err = add_rtattr(header, sizeof(request), IFA_BROADCAST,
- &ipv4_bcast, sizeof(ipv4_bcast))) < 0)
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_BROADCAST,
+ &ipv4_bcast,
+ sizeof(ipv4_bcast));
+ if (err < 0)
return err;
} else if (family == AF_INET6) {
if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
return -1;
- if ((err = add_rtattr(header, sizeof(request), IFA_LOCAL,
- &ipv6_addr, sizeof(ipv6_addr))) < 0)
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_LOCAL,
+ &ipv6_addr,
+ sizeof(ipv6_addr));
+ if (err < 0)
return err;
}
- sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ sk = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sk < 0)
- return -1;
+ return -errno;
memset(&nl_addr, 0, sizeof(nl_addr));
nl_addr.nl_family = AF_NETLINK;
if (name == NULL)
return -1;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
if (index < 0)
return NULL;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return NULL;
struct ifreq ifr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -errno;
struct ifreq ifr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -errno;
struct ifreq ifr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -errno;
if (index < 0)
return NULL;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return NULL;
if (index < 0)
return NULL;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return NULL;
struct ifreq ifr;
int sk;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return FALSE;
if (devname == NULL)
return NULL;
- if (__connman_element_device_isfiltered(devname) == TRUE) {
+ if (__connman_device_isfiltered(devname) == TRUE) {
connman_info("Ignoring interface %s (filtered)", devname);
free(devname);
return NULL;
int connman_inet_set_ipv6_address(int index,
struct connman_ipaddress *ipaddress)
{
+ int err;
unsigned char prefix_len;
const char *address;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if ((__connman_inet_modify_address(RTM_NEWADDR,
- NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
- address, NULL, prefix_len, NULL)) < 0) {
- connman_error("Set IPv6 address error");
- return -1;
+ err = __connman_inet_modify_address(RTM_NEWADDR,
+ NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
+ address, NULL, prefix_len, NULL);
+ if (err < 0) {
+ connman_error("%s: %s", __func__, strerror(-err));
+ return err;
}
return 0;
int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
{
+ int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if ((__connman_inet_modify_address(RTM_NEWADDR,
- NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- address, peer, prefix_len, broadcast)) < 0) {
- DBG("address setting failed");
- return -1;
+ err = __connman_inet_modify_address(RTM_NEWADDR,
+ NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+ address, peer, prefix_len, broadcast);
+ if (err < 0) {
+ connman_error("%s: %s", __func__, strerror(-err));
+ return err;
}
return 0;
int connman_inet_clear_ipv6_address(int index, const char *address,
int prefix_len)
{
+ int err;
+
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if ((__connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
- address, NULL, prefix_len, NULL)) < 0) {
- connman_error("Clear IPv6 address error");
- return -1;
+ err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
+ address, NULL, prefix_len, NULL);
+ if (err < 0) {
+ connman_error("%s: %s", __func__, strerror(-err));
+ return err;
}
return 0;
int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
{
+ int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
- if ((__connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
- address, peer, prefix_len, broadcast)) < 0) {
- DBG("address removal failed");
- return -1;
+ err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
+ address, peer, prefix_len, broadcast);
+ if (err < 0) {
+ connman_error("%s: %s", __func__, strerror(-err));
+ return err;
}
return 0;
struct sockaddr_in addr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ DBG("index %d host %s gateway %s netmask %s", index,
+ host, gateway, netmask);
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
struct sockaddr_in addr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ DBG("index %d host %s", index, host);
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
rt.rtmsg_metric = 1;
rt.rtmsg_ifindex = index;
- sk = socket(AF_INET6, SOCK_DGRAM, 0);
+ sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
err = -1;
goto out;
close(sk);
out:
if (err < 0)
- connman_error("Del IPv6 host route error");
+ connman_error("Del IPv6 host route error (%s)",
+ strerror(errno));
return err;
}
int connman_inet_add_ipv6_network_route(int index, const char *host,
const char *gateway,
- unsigned char prefix_len)
+ unsigned char prefix_len)
{
struct in6_rtmsg rt;
int sk, err;
rt.rtmsg_metric = 1;
rt.rtmsg_ifindex = index;
- sk = socket(AF_INET6, SOCK_DGRAM, 0);
+ sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
err = -1;
goto out;
close(sk);
out:
if (err < 0)
- connman_error("Set IPv6 host route error");
+ connman_error("Set IPv6 host route error (%s)",
+ strerror(errno));
return err;
}
struct in6_rtmsg rt;
int sk, err;
- DBG("index %d, gateway %s", index, gateway);
+ DBG("index %d gateway %s", index, gateway);
if (gateway == NULL)
return -EINVAL;
rt.rtmsg_dst_len = 0;
rt.rtmsg_ifindex = index;
- sk = socket(AF_INET6, SOCK_DGRAM, 0);
+ sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
err = -1;
goto out;
close(sk);
out:
if (err < 0)
- connman_error("Set default IPv6 gateway error");
+ connman_error("Set default IPv6 gateway error (%s)",
+ strerror(errno));
return err;
}
struct in6_rtmsg rt;
int sk, err;
- DBG("index %d, gateway %s", index, gateway);
+ DBG("index %d gateway %s", index, gateway);
if (gateway == NULL)
return -EINVAL;
rt.rtmsg_dst_len = 0;
rt.rtmsg_ifindex = index;
- sk = socket(AF_INET6, SOCK_DGRAM, 0);
+ sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
err = -1;
goto out;
close(sk);
out:
if (err < 0)
- connman_error("Clear default IPv6 gateway error");
+ connman_error("Clear default IPv6 gateway error (%s)",
+ strerror(errno));
return err;
}
struct sockaddr_in addr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ DBG("index %d gateway %s", index, gateway);
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
struct sockaddr_in addr;
int sk, err;
- DBG("");
+ DBG("index %d", index);
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
return err;
}
+int connman_inet_set_ipv6_gateway_interface(int index)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+ struct sockaddr_in6 addr;
+ const struct in6_addr any = IN6ADDR_ANY_INIT;
+ int sk, err;
+
+ DBG("index %d", index);
+
+ sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ DBG("ifname %s", ifr.ifr_name);
+
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_flags = RTF_UP;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = any;
+
+ memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+ memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+ memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+ rt.rt_dev = ifr.ifr_name;
+
+ err = ioctl(sk, SIOCADDRT, &rt);
+ if (err < 0)
+ connman_error("Setting default interface route failed (%s)",
+ strerror(errno));
+ close(sk);
+
+ return err;
+}
+
int connman_inet_clear_gateway_address(int index, const char *gateway)
{
struct ifreq ifr;
struct sockaddr_in addr;
int sk, err;
- DBG("");
+ DBG("index %d gateway %s", index, gateway);
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
struct sockaddr_in addr;
int sk, err;
- DBG("");
+ DBG("index %d", index);
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
return err;
}
+int connman_inet_clear_ipv6_gateway_interface(int index)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+ struct sockaddr_in6 addr;
+ const struct in6_addr any = IN6ADDR_ANY_INIT;
+ int sk, err;
+
+ DBG("index %d", index);
+
+ sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ DBG("ifname %s", ifr.ifr_name);
+
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_flags = RTF_UP;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = any;
+
+ memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+ memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+ memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+ rt.rt_dev = ifr.ifr_name;
+
+ err = ioctl(sk, SIOCDELRT, &rt);
+ if (err < 0)
+ connman_error("Removing default interface route failed (%s)",
+ strerror(errno));
+ close(sk);
+
+ return err;
+}
+
connman_bool_t connman_inet_compare_subnet(int index, const char *host)
{
struct ifreq ifr;
return -1;
host_addr = _host_addr.s_addr;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return FALSE;
close(sk);
return FALSE;
}
+
+ close(sk);
+
addr = (struct sockaddr_in *)&ifr.ifr_addr;
if_addr = addr->sin_addr.s_addr;
if (bridge == NULL)
return -EINVAL;
- sk = socket(AF_INET, SOCK_STREAM, 0);
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return sk;
if (bridge == NULL)
return -EINVAL;
- sk = socket(AF_INET, SOCK_STREAM, 0);
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return sk;
struct ifreq ifr;
int sk, err;
- sk = socket(AF_INET, SOCK_DGRAM, 0);
+ sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return sk;
if (tunnel == NULL)
return -EINVAL;
- sk = socket(AF_INET, SOCK_DGRAM, 0);
+ sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return sk;
struct ifreq ifr;
int i, fd;
- fd = open("/dev/net/tun", O_RDWR);
+ fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
if (fd < 0) {
i = -errno;
connman_error("Failed to open /dev/net/tun: %s",
__connman_inet_rs_cb_t callback;
struct sockaddr_in6 addr;
guint rs_timeout;
+ guint watch_id;
void *user_data;
};
static const struct in6_addr in6addr_all_routers_mc =
IN6ADDR_ALL_ROUTERS_MC_INIT;
-/* from netinet/in.h */
-struct in6_pktinfo {
- struct in6_addr ipi6_addr; /* src/dst IPv6 address */
- unsigned int ipi6_ifindex; /* send/recv interface index */
-};
-
static void rs_cleanup(struct rs_cb_data *data)
{
- g_io_channel_shutdown(data->channel, TRUE, NULL);
- g_io_channel_unref(data->channel);
- data->channel = 0;
+ if (data->channel != NULL) {
+ g_io_channel_shutdown(data->channel, TRUE, NULL);
+ g_io_channel_unref(data->channel);
+ data->channel = NULL;
+ }
if (data->rs_timeout > 0)
g_source_remove(data->rs_timeout);
+ if (data->watch_id > 0)
+ g_source_remove(data->watch_id);
+
g_free(data);
}
return FALSE;
if (data->callback != NULL)
- data->callback(NULL, data->user_data);
+ data->callback(NULL, 0, data->user_data);
data->rs_timeout = 0;
rs_cleanup(data);
len = recvmsg(fd, &mhdr, 0);
if (len < 0) {
- data->callback(NULL, data->user_data);
+ data->callback(NULL, 0, data->user_data);
+ rs_cleanup(data);
return -errno;
}
hdr = (struct nd_router_advert *)buf;
+ DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len,
+ sizeof(struct nd_router_advert));
if (hdr->nd_ra_code != 0)
return 0;
- data->callback(hdr, data->user_data);
+ data->callback(hdr, len, data->user_data);
rs_cleanup(data);
return len;
DBG("");
- fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+ fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
if (fd < 0)
return -errno;
data->user_data = user_data;
data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data);
- sk = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
if (sk < 0)
return -errno;
g_io_channel_set_encoding(data->channel, NULL, NULL);
g_io_channel_set_buffered(data->channel, FALSE);
- g_io_add_watch(data->channel,
+ data->watch_id = g_io_add_watch(data->channel,
G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
icmpv6_event, data);
return 0;
}
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+ unsigned int length)
+{
+ GSList *prefixes = NULL;
+ uint8_t *pos;
+ int len;
+
+ if (length <= sizeof(struct nd_router_advert))
+ return NULL;
+
+ len = length - sizeof(struct nd_router_advert);
+ pos = (uint8_t *)hdr + sizeof(struct nd_router_advert);
+
+ while (len > 0) {
+ struct nd_opt_prefix_info *pinfo;
+ char prefix_str[INET6_ADDRSTRLEN+1], *str;
+ const char *prefix;
+ int optlen;
+
+ if (len < 2)
+ break;
+
+ optlen = pos[1] << 3;
+ if (optlen == 0 || optlen > len)
+ break;
+
+ switch (pos[0]) {
+ case ND_OPT_PREFIX_INFORMATION:
+ pinfo = (struct nd_opt_prefix_info *)pos;
+ prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefix_str, INET6_ADDRSTRLEN);
+ if (prefix == NULL)
+ break;
+
+ str = g_strdup_printf("%s/%d", prefix,
+ pinfo->nd_opt_pi_prefix_len);
+ prefixes = g_slist_append(prefixes, str);
+
+ DBG("prefix %s", str);
+
+ break;
+ }
+
+ len -= optlen;
+ pos += optlen;
+ }
+
+ return prefixes;
+}
+
+static int get_dest_addr(int family, int index, char *buf, int len)
+{
+ struct ifreq ifr;
+ void *addr;
+ int sk;
+
+ sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -errno;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
+ close(sk);
+ return -errno;
+ }
+
+ if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+ DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
+ close(sk);
+ return -errno;
+ }
+
+ if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+ close(sk);
+ errno = EINVAL;
+ return -errno;
+ }
+
+ DBG("index %d %s", index, ifr.ifr_name);
+
+ if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
+ connman_error("Get destination address failed (%s)",
+ strerror(errno));
+ close(sk);
+ return -errno;
+ }
+
+ close(sk);
+
+ switch (family) {
+ case AF_INET:
+ addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
+ break;
+ case AF_INET6:
+ addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
+ break;
+ default:
+ errno = EINVAL;
+ return -errno;
+ }
+
+ if (inet_ntop(family, addr, buf, len) == NULL) {
+ DBG("error %d/%s", errno, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+int connman_inet_get_dest_addr(int index, char **dest)
+{
+ char addr[INET_ADDRSTRLEN];
+ int ret;
+
+ ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
+ if (ret < 0)
+ return ret;
+
+ *dest = g_strdup(addr);
+
+ DBG("destination %s", *dest);
+
+ return 0;
+}
+
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+ char addr[INET6_ADDRSTRLEN];
+ int ret;
+
+ ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
+ if (ret < 0)
+ return ret;
+
+ *dest = g_strdup(addr);
+
+ DBG("destination %s", *dest);
+
+ return 0;
+}
+
+int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
+{
+ int sndbuf = 1024;
+ int rcvbuf = 1024 * 4;
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (rth->fd < 0) {
+ connman_error("Can not open netlink socket: %s",
+ strerror(errno));
+ return -errno;
+ }
+
+ if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
+ sizeof(sndbuf)) < 0) {
+ connman_error("SO_SNDBUF: %s", strerror(errno));
+ return -errno;
+ }
+
+ if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+ sizeof(rcvbuf)) < 0) {
+ connman_error("SO_RCVBUF: %s", strerror(errno));
+ return -errno;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = 0;
+
+ if (bind(rth->fd, (struct sockaddr *)&rth->local,
+ sizeof(rth->local)) < 0) {
+ connman_error("Can not bind netlink socket: %s",
+ strerror(errno));
+ return -errno;
+ }
+
+ rth->seq = time(NULL);
+
+ DBG("fd %d", rth->fd);
+
+ return 0;
+}
+
+struct inet_rtnl_cb_data {
+ GIOChannel *channel;
+ __connman_inet_rtnl_cb_t callback;
+ guint rtnl_timeout;
+ guint watch_id;
+ struct __connman_inet_rtnl_handle *rtnl;
+ void *user_data;
+};
+
+static void inet_rtnl_cleanup(struct inet_rtnl_cb_data *data)
+{
+ struct __connman_inet_rtnl_handle *rth = data->rtnl;
+
+ if (data->channel != NULL) {
+ g_io_channel_shutdown(data->channel, TRUE, NULL);
+ g_io_channel_unref(data->channel);
+ data->channel = NULL;
+ }
+
+ DBG("data %p", data);
+
+ if (data->rtnl_timeout > 0)
+ g_source_remove(data->rtnl_timeout);
+
+ if (data->watch_id > 0)
+ g_source_remove(data->watch_id);
+
+ if (rth != NULL) {
+ __connman_inet_rtnl_close(rth);
+ g_free(rth);
+ }
+
+ g_free(data);
+}
+
+static gboolean inet_rtnl_timeout_cb(gpointer user_data)
+{
+ struct inet_rtnl_cb_data *data = user_data;
+
+ DBG("user data %p", user_data);
+
+ if (data == NULL)
+ return FALSE;
+
+ if (data->callback != NULL)
+ data->callback(NULL, data->user_data);
+
+ data->rtnl_timeout = 0;
+ inet_rtnl_cleanup(data);
+ return FALSE;
+}
+
+static int inet_rtnl_recv(GIOChannel *chan, gpointer user_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;
+ socklen_t addr_len = sizeof(nladdr);
+ unsigned char buf[4096];
+ void *ptr = buf;
+ gsize len;
+ int status, fd;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&nladdr, 0, sizeof(nladdr));
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ status = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *) &nladdr, &addr_len);
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return -1;
+ }
+
+ if (status == 0)
+ return -1;
+
+ if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+ DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+ return 0;
+ }
+
+ len = status;
+
+ while (len > 0) {
+ struct nlmsgerr *err;
+
+ h = ptr;
+
+ if (!NLMSG_OK(h, len)) {
+ return -1;
+ break;
+ }
+
+ if (h->nlmsg_seq != rth->seq) {
+ /* Skip this msg */
+ DBG("skip %d/%d len %d", rth->seq,
+ h->nlmsg_seq, h->nlmsg_len);
+
+ len -= h->nlmsg_len;
+ ptr += h->nlmsg_len;
+ continue;
+ }
+
+ switch (h->nlmsg_type) {
+ case NLMSG_NOOP:
+ case NLMSG_OVERRUN:
+ return -1;
+
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(h);
+ connman_error("RTNETLINK answers %s (%d)",
+ strerror(-err->error), -err->error);
+ return err->error;
+ }
+
+ break;
+ }
+
+ if (h->nlmsg_seq == rth->seq) {
+ DBG("received %d seq %d", h->nlmsg_len, h->nlmsg_seq);
+
+ rtnl_data->callback(h, rtnl_data->user_data);
+
+ if (rtnl_data->rtnl_timeout > 0) {
+ g_source_remove(rtnl_data->rtnl_timeout);
+ rtnl_data->rtnl_timeout = 0;
+ }
+
+ __connman_inet_rtnl_close(rth);
+ g_free(rth);
+ }
+
+ return 0;
+}
+
+static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ int ret;
+
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ ret = inet_rtnl_recv(chan, user_data);
+ if (ret != 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
+ struct nlmsghdr *n, int timeout,
+ __connman_inet_rtnl_cb_t callback, void *user_data)
+{
+ struct sockaddr_nl nladdr;
+ struct inet_rtnl_cb_data *data;
+ unsigned seq;
+ int err;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (callback != NULL) {
+ data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->callback = callback;
+ data->user_data = user_data;
+ data->rtnl = rtnl;
+ data->rtnl_timeout = g_timeout_add_seconds(timeout,
+ inet_rtnl_timeout_cb, data);
+
+ data->channel = g_io_channel_unix_new(rtnl->fd);
+ g_io_channel_set_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,
+ inet_rtnl_event, data);
+ } else
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ err = sendto(rtnl->fd, &rtnl->req.n, rtnl->req.n.nlmsg_len, 0,
+ (struct sockaddr *) &nladdr, sizeof(nladdr));
+ DBG("handle %p len %d err %d", rtnl, rtnl->req.n.nlmsg_len, err);
+ if (err < 0) {
+ connman_error("Can not talk to rtnetlink");
+ return -errno;
+ }
+
+ if ((unsigned int)err != rtnl->req.n.nlmsg_len) {
+ connman_error("Sent %d bytes, msg truncated", err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth)
+{
+ DBG("handle %p", rth);
+
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, int type,
+ __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ DBG("Error! max allowed bound %zd exceeded", maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+ return 0;
+}
+
+/* Check routine modified from ics-dhcp 4.2.3-P2 */
+connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len)
+{
+ const char *p;
+
+ /*
+ * Not empty or complete length not over 255 characters.
+ */
+ if ((len == 0) || (len > 256))
+ return FALSE;
+
+ /*
+ * Consists of [[:alnum:]-]+ labels separated by [.]
+ * a [_] is against RFC but seems to be "widely used"
+ */
+ for (p = ptr; (*p != 0) && (len-- > 0); p++) {
+
+ if ((*p == '-') || (*p == '_')) {
+ /*
+ * Not allowed at begin or end of a label.
+ */
+ if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
+ return FALSE;
+
+ } else if (*p == '.') {
+ /*
+ * Each label has to be 1-63 characters;
+ * we allow [.] at the end ('foo.bar.')
+ */
+ size_t d = p - ptr;
+
+ if ((d <= 0) || (d >= 64))
+ return FALSE;
+
+ ptr = p + 1; /* Jump to the next label */
+
+ } else if (isalnum((unsigned char)*p) == 0) {
+ /*
+ * Also numbers at the begin are fine
+ */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}