X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Finet.c;h=17d58d5c2d19b719c1158466b0e03878588c9365;hb=091f5fa9229897a52424cd442b325f97d361dbc3;hp=56f29fdb777c453756c5048235994f208e93ecba;hpb=04589cdd53183bc2e3102bae95cb558dc29078ee;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/inet.c b/src/inet.c index 56f29fd..17d58d5 100644 --- a/src/inet.c +++ b/src/inet.c @@ -2,7 +2,7 @@ * * 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 * @@ -25,6 +25,7 @@ #include #endif +#define _GNU_SOURCE #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +44,7 @@ #include #include #include +#include #include "connman.h" @@ -49,8 +52,8 @@ ((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; @@ -127,30 +130,45 @@ int __connman_inet_modify_address(int cmd, int flags, 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 -errno; @@ -177,7 +195,7 @@ int connman_inet_ifindex(const char *name) if (name == NULL) return -1; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -202,7 +220,7 @@ char *connman_inet_ifname(int index) if (index < 0) return NULL; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return NULL; @@ -224,7 +242,7 @@ short int connman_inet_ifflags(int index) struct ifreq ifr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -254,7 +272,7 @@ int connman_inet_ifup(int index) struct ifreq ifr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -296,7 +314,7 @@ int connman_inet_ifdown(int index) struct ifreq ifr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -341,7 +359,7 @@ static char *index2addr(int index) if (index < 0) return NULL; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return NULL; @@ -384,7 +402,7 @@ static char *index2ident(int index, const char *prefix) if (index < 0) return NULL; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return NULL; @@ -428,7 +446,7 @@ connman_bool_t connman_inet_is_cfg80211(int index) struct ifreq ifr; int sk; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return FALSE; @@ -653,7 +671,10 @@ int connman_inet_add_network_route(int index, const char *host, struct sockaddr_in addr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + DBG("index %d host %s gateway %s netmask %s", index, + host, gateway, netmask); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -715,7 +736,9 @@ int connman_inet_del_network_route(int index, const char *host) struct sockaddr_in addr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + DBG("index %d host %s", index, host); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -773,7 +796,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host, rt.rtmsg_metric = 1; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { err = -1; goto out; @@ -824,7 +847,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host, rt.rtmsg_metric = 1; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { err = -1; goto out; @@ -851,7 +874,7 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway) struct in6_rtmsg rt; int sk, err; - DBG("index %d, gateway %s", index, gateway); + DBG("index %d gateway %s", index, gateway); if (gateway == NULL) return -EINVAL; @@ -867,7 +890,7 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway) rt.rtmsg_dst_len = 0; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { err = -1; goto out; @@ -888,7 +911,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway) struct in6_rtmsg rt; int sk, err; - DBG("index %d, gateway %s", index, gateway); + DBG("index %d gateway %s", index, gateway); if (gateway == NULL) return -EINVAL; @@ -904,7 +927,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway) rt.rtmsg_dst_len = 0; rt.rtmsg_ifindex = index; - sk = socket(AF_INET6, SOCK_DGRAM, 0); + sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { err = -1; goto out; @@ -927,7 +950,9 @@ int connman_inet_set_gateway_address(int index, const char *gateway) struct sockaddr_in addr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + DBG("index %d gateway %s", index, gateway); + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -976,9 +1001,9 @@ int connman_inet_set_gateway_interface(int index) struct sockaddr_in addr; int sk, err; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -1022,9 +1047,9 @@ int connman_inet_set_ipv6_gateway_interface(int index) const struct in6_addr any = IN6ADDR_ANY_INIT; int sk, err; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET6, SOCK_DGRAM, 0); + sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -1067,9 +1092,9 @@ int connman_inet_clear_gateway_address(int index, const char *gateway) struct sockaddr_in addr; int sk, err; - DBG(""); + DBG("index %d gateway %s", index, gateway); - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -1118,9 +1143,9 @@ int connman_inet_clear_gateway_interface(int index) struct sockaddr_in addr; int sk, err; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -1164,9 +1189,9 @@ int connman_inet_clear_ipv6_gateway_interface(int index) const struct in6_addr any = IN6ADDR_ANY_INIT; int sk, err; - DBG(""); + DBG("index %d", index); - sk = socket(PF_INET6, SOCK_DGRAM, 0); + sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -1; @@ -1219,7 +1244,7 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host) return -1; host_addr = _host_addr.s_addr; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return FALSE; @@ -1243,6 +1268,9 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host) close(sk); return FALSE; } + + close(sk); + addr = (struct sockaddr_in *)&ifr.ifr_addr; if_addr = addr->sin_addr.s_addr; @@ -1257,7 +1285,7 @@ int connman_inet_remove_from_bridge(int index, const char *bridge) if (bridge == NULL) return -EINVAL; - sk = socket(AF_INET, SOCK_STREAM, 0); + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; @@ -1286,7 +1314,7 @@ int connman_inet_add_to_bridge(int index, const char *bridge) if (bridge == NULL) return -EINVAL; - sk = socket(AF_INET, SOCK_STREAM, 0); + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; @@ -1312,7 +1340,7 @@ int connman_inet_set_mtu(int index, int mtu) struct ifreq ifr; int sk, err; - sk = socket(AF_INET, SOCK_DGRAM, 0); + sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; @@ -1339,7 +1367,7 @@ int connman_inet_setup_tunnel(char *tunnel, int mtu) if (tunnel == NULL) return -EINVAL; - sk = socket(AF_INET, SOCK_DGRAM, 0); + sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return sk; @@ -1379,7 +1407,7 @@ int connman_inet_create_tunnel(char **iface) 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", @@ -1413,6 +1441,7 @@ struct rs_cb_data { __connman_inet_rs_cb_t callback; struct sockaddr_in6 addr; guint rs_timeout; + guint watch_id; void *user_data; }; @@ -1426,21 +1455,20 @@ static const struct in6_addr in6addr_all_nodes_mc = IN6ADDR_ALL_NODES_MC_INIT; static const struct in6_addr in6addr_all_routers_mc = IN6ADDR_ALL_ROUTERS_MC_INIT; -/* from netinet/in.h */ -struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - unsigned int ipi6_ifindex; /* send/recv interface index */ -}; - static void rs_cleanup(struct rs_cb_data *data) { - g_io_channel_shutdown(data->channel, TRUE, NULL); - g_io_channel_unref(data->channel); - data->channel = 0; + if (data->channel != NULL) { + g_io_channel_shutdown(data->channel, TRUE, NULL); + g_io_channel_unref(data->channel); + data->channel = NULL; + } if (data->rs_timeout > 0) g_source_remove(data->rs_timeout); + if (data->watch_id > 0) + g_source_remove(data->watch_id); + g_free(data); } @@ -1454,7 +1482,7 @@ static gboolean rs_timeout_cb(gpointer user_data) return FALSE; if (data->callback != NULL) - data->callback(NULL, data->user_data); + data->callback(NULL, 0, data->user_data); data->rs_timeout = 0; rs_cleanup(data); @@ -1486,16 +1514,18 @@ static int icmpv6_recv(int fd, gpointer user_data) len = recvmsg(fd, &mhdr, 0); if (len < 0) { - data->callback(NULL, data->user_data); + data->callback(NULL, 0, data->user_data); rs_cleanup(data); return -errno; } hdr = (struct nd_router_advert *)buf; + DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len, + sizeof(struct nd_router_advert)); if (hdr->nd_ra_code != 0) return 0; - data->callback(hdr, data->user_data); + data->callback(hdr, len, data->user_data); rs_cleanup(data); return len; @@ -1576,7 +1606,7 @@ static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest) DBG(""); - fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); if (fd < 0) return -errno; @@ -1690,7 +1720,7 @@ int __connman_inet_ipv6_send_rs(int index, int timeout, data->user_data = user_data; data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data); - sk = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); if (sk < 0) return -errno; @@ -1710,7 +1740,7 @@ int __connman_inet_ipv6_send_rs(int index, int timeout, g_io_channel_set_encoding(data->channel, NULL, NULL); g_io_channel_set_buffered(data->channel, FALSE); - g_io_add_watch(data->channel, + data->watch_id = g_io_add_watch(data->channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, icmpv6_event, data); @@ -1718,3 +1748,495 @@ int __connman_inet_ipv6_send_rs(int index, int timeout, return 0; } + +GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr, + unsigned int length) +{ + GSList *prefixes = NULL; + uint8_t *pos; + int len; + + if (length <= sizeof(struct nd_router_advert)) + return NULL; + + len = length - sizeof(struct nd_router_advert); + pos = (uint8_t *)hdr + sizeof(struct nd_router_advert); + + while (len > 0) { + struct nd_opt_prefix_info *pinfo; + char prefix_str[INET6_ADDRSTRLEN+1], *str; + const char *prefix; + int optlen; + + if (len < 2) + break; + + optlen = pos[1] << 3; + if (optlen == 0 || optlen > len) + break; + + switch (pos[0]) { + case ND_OPT_PREFIX_INFORMATION: + pinfo = (struct nd_opt_prefix_info *)pos; + prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, + prefix_str, INET6_ADDRSTRLEN); + if (prefix == NULL) + break; + + str = g_strdup_printf("%s/%d", prefix, + pinfo->nd_opt_pi_prefix_len); + prefixes = g_slist_append(prefixes, str); + + DBG("prefix %s", str); + + break; + } + + len -= optlen; + pos += optlen; + } + + return prefixes; +} + +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; +} + +int connman_inet_check_ipaddress(const char *host) +{ + struct addrinfo hints; + struct addrinfo *addr; + int result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + addr = NULL; + + result = getaddrinfo(host, NULL, &hints, &addr); + if (result == 0) + result = addr->ai_family; + else + result = -result; + 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) +{ + 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; +}