From d803fbaf4ef630a63383449535af68440133e823 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 5 Apr 2012 12:04:05 +0300 Subject: inet: Refactor rtnl functions in 6to4.c The rtnl support functions are now in inet.c which is a more logical place for them and now other files can also use them. --- src/6to4.c | 215 +++++++--------------------------------- src/connman.h | 40 ++++++++ src/inet.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 375 insertions(+), 191 deletions(-) (limited to 'src') diff --git a/src/6to4.c b/src/6to4.c index 2d54f6d9..41b2dc95 100644 --- a/src/6to4.c +++ b/src/6to4.c @@ -51,146 +51,10 @@ static guint web_request_id; #define STATUS_URL "http://ipv6.connman.net/online/status.html" -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - #ifndef IP_DF #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ #endif -struct rtnl_handle { - int fd; - struct sockaddr_nl local; - struct sockaddr_nl peer; - __u32 seq; - __u32 dump; -}; - -static int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) -{ - int len = RTA_LENGTH(4); - struct rtattr *rta; - if (NLMSG_ALIGN(n->nlmsg_len) + len > (unsigned int)maxlen) { - DBG("Error! max allowed bound %d 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; -} - -static int addattr_l(struct nlmsghdr *n, int maxlen, int type, - const void *data, int alen) -{ - int len = RTA_LENGTH(alen); - struct rtattr *rta; - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > - (unsigned int)maxlen) { - DBG("addattr_l message exceeded bound of %d", maxlen); - return -1; - } - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - memcpy(RTA_DATA(rta), data, alen); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - - return 0; -} - -static void rtnl_close(struct rtnl_handle *rth) -{ - if (rth->fd >= 0) { - close(rth->fd); - rth->fd = -1; - } -} - -static int rtnl_open(struct rtnl_handle *rth) -{ - socklen_t addr_len; - int sndbuf = 1024; - - memset(rth, 0, sizeof(*rth)); - - 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 -1; - } - - if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, - sizeof(sndbuf)) < 0) { - connman_error("SO_SNDBUF: %s", strerror(errno)); - return -1; - } - - 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 -1; - } - addr_len = sizeof(rth->local); - if (getsockname(rth->fd, (struct sockaddr *)&rth->local, - &addr_len) < 0) { - connman_error("Can not getsockname: %s", strerror(errno)); - return -1; - } - if (addr_len != sizeof(rth->local)) { - connman_error("Wrong address length %d", addr_len); - return -1; - } - if (rth->local.nl_family != AF_NETLINK) { - connman_error("Wrong address family %d", rth->local.nl_family); - return -1; - } - rth->seq = time(NULL); - - return 0; -} - -static int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n) -{ - struct sockaddr_nl nladdr; - struct iovec iov = { - .iov_base = (void *)n, - .iov_len = n->nlmsg_len - }; - struct msghdr msg = { - .msg_name = &nladdr, - .msg_namelen = sizeof(nladdr), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - unsigned seq; - int err; - - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - n->nlmsg_seq = seq = ++rtnl->seq; - n->nlmsg_flags |= NLM_F_ACK; - - err = sendmsg(rtnl->fd, &msg, 0); - if (err < 0) { - connman_error("Can not talk to rtnetlink"); - return err; - } - - return 0; -} - static int tunnel_create(struct in_addr *addr) { struct ip_tunnel_parm p; @@ -267,17 +131,11 @@ static void tunnel_destroy() static int tunnel_add_route() { - struct rtnl_handle rth; + struct __connman_inet_rtnl_handle rth; struct in6_addr addr6; int index; int ret = 0; - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[1024]; - } req; - /* ip -6 route add ::/0 via ::192.88.99.1 dev tun6to4 metric 1 */ index = if_nametoindex("tun6to4"); @@ -286,60 +144,57 @@ static int tunnel_add_route() return -1; } - memset(&req, 0, sizeof(req)); + memset(&rth, 0, sizeof(rth)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - req.n.nlmsg_type = RTM_NEWROUTE; - req.r.rtm_family = AF_INET6; - req.r.rtm_table = RT_TABLE_MAIN; - req.r.rtm_protocol = RTPROT_BOOT; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; - req.r.rtm_type = RTN_UNICAST; - req.r.rtm_dst_len = 0; + 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 = RTM_NEWROUTE; + rth.req.u.r.rt.rtm_family = AF_INET6; + rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN; + 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 = 0; inet_pton(AF_INET6, "::192.88.99.1", &addr6); - addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr6.s6_addr, 16); - addattr32(&req.n, sizeof(req), RTA_OIF, index); - addattr32(&req.n, sizeof(req), RTA_PRIORITY, 1); + __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY, + &addr6.s6_addr, 16); + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), RTA_OIF, + index); + __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), + RTA_PRIORITY, 1); - ret = rtnl_open(&rth); + ret = __connman_inet_rtnl_open(&rth); if (ret < 0) goto done; - ret = rtnl_talk(&rth, &req.n); + ret = __connman_inet_rtnl_send(&rth, &rth.req.n); done: - rtnl_close(&rth); + __connman_inet_rtnl_close(&rth); return ret; } static int tunnel_set_addr(unsigned int a, unsigned int b, unsigned int c, unsigned int d) { - struct rtnl_handle rth; + struct __connman_inet_rtnl_handle rth; struct in6_addr addr6; char *ip6addr; int ret; - struct { - struct nlmsghdr n; - struct ifaddrmsg ifa; - char buf[256]; - } req; - /* ip -6 addr add dev tun6to4 2002:0102:0304::1/64 */ - memset(&req, 0, sizeof(req)); + memset(&rth, 0, sizeof(rth)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - req.n.nlmsg_type = RTM_NEWADDR; - req.ifa.ifa_family = AF_INET6; - req.ifa.ifa_prefixlen = 64; - req.ifa.ifa_index = if_nametoindex("tun6to4"); - if (req.ifa.ifa_index == 0) { + rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + rth.req.n.nlmsg_type = RTM_NEWADDR; + rth.req.u.i.ifa.ifa_family = AF_INET6; + rth.req.u.i.ifa.ifa_prefixlen = 64; + rth.req.u.i.ifa.ifa_index = if_nametoindex("tun6to4"); + if (rth.req.u.i.ifa.ifa_index == 0) { connman_error("Can not find device tun6to4"); ret = -1; goto done; @@ -350,17 +205,19 @@ static int tunnel_set_addr(unsigned int a, unsigned int b, DBG("ipv6 address %s", ip6addr); g_free(ip6addr); - addattr_l(&req.n, sizeof(req), IFA_LOCAL, &addr6.s6_addr, 16); - addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &addr6.s6_addr, 16); + __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_LOCAL, + &addr6.s6_addr, 16); + __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_ADDRESS, + &addr6.s6_addr, 16); - ret = rtnl_open(&rth); + ret = __connman_inet_rtnl_open(&rth); if (ret < 0) goto done; - ret = rtnl_talk(&rth, &req.n); + ret = __connman_inet_rtnl_send(&rth, &rth.req.n); done: - rtnl_close(&rth); + __connman_inet_rtnl_close(&rth); return ret; } diff --git a/src/connman.h b/src/connman.h index 800f1aef..501b5baf 100644 --- a/src/connman.h +++ b/src/connman.h @@ -152,6 +152,46 @@ int __connman_inet_ipv6_send_rs(int index, int timeout, GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr, unsigned int length); +struct __connman_inet_rtnl_handle { + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; + + struct { + struct nlmsghdr n; + union { + struct { + struct rtmsg rt; + } r; + struct { + struct ifaddrmsg ifa; + } i; + } u; + char buf[1024]; + } req; +}; + +int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth); +typedef void (*__connman_inet_rtnl_cb_t) (struct nlmsghdr *answer, + void *user_data); +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); +static inline +int __connman_inet_rtnl_send(struct __connman_inet_rtnl_handle *rtnl, + struct nlmsghdr *n) +{ + return __connman_inet_rtnl_talk(rtnl, n, 0, NULL, NULL); +} + +void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth); +int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length, + int type, const void *data, size_t data_length); +int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, + int type, __u32 data); + #include int __connman_resolver_init(connman_bool_t dnsproxy); diff --git a/src/inet.c b/src/inet.c index 6202021b..1fc636e3 100644 --- a/src/inet.c +++ b/src/inet.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -50,8 +51,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; @@ -128,26 +129,41 @@ 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; } @@ -1875,3 +1891,274 @@ int connman_inet_ipv6_get_dest_addr(int index, char **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; + unsigned char buf[4096]; + void *ptr = buf; + gsize len; + int status; + + memset(buf, 0, sizeof(buf)); + + status = g_io_channel_read_chars(chan, (gchar *) buf, + sizeof(buf), &len, NULL); + + DBG("status %d", status); + + switch (status) { + case G_IO_STATUS_NORMAL: + break; + case G_IO_STATUS_AGAIN: + return 0; + default: + return -1; + } + + 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 %d 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; +} -- cgit v1.2.3