summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/6to4.c215
-rw-r--r--src/connman.h40
-rw-r--r--src/inet.c311
3 files changed, 375 insertions, 191 deletions
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 <connman/resolver.h>
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 <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>
@@ -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;
+}