X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Frtnl.c;h=70e8c3cbc52e4b3adc3cd68b108fa81f4214efa4;hb=a922337a31fab30b2f6af5dd96efb9cb338169fb;hp=61205b30b327330b35ccfe284a8f27b34748bb91;hpb=ad74ad47566ed5565744d91e0f090b3c4d662c90;p=connman diff --git a/src/rtnl.c b/src/rtnl.c index 61205b3..70e8c3c 100644 --- a/src/rtnl.c +++ b/src/rtnl.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -36,7 +36,74 @@ #include "connman.h" -static GStaticRWLock rtnl_lock = G_STATIC_RW_LOCK_INIT; +struct watch_data { + unsigned int id; + int index; + connman_rtnl_link_cb_t newlink; + void *user_data; +}; + +static GSList *watch_list = NULL; +static unsigned int watch_id = 0; + +/** + * connman_rtnl_add_newlink_watch: + * @index: network device index + * @callback: callback function + * @user_data: callback data; + * + * Add a new RTNL watch for newlink events + * + * Returns: %0 on failure and a unique id on success + */ +unsigned int connman_rtnl_add_newlink_watch(int index, + connman_rtnl_link_cb_t callback, void *user_data) +{ + struct watch_data *watch; + + watch = g_try_new0(struct watch_data, 1); + if (watch == NULL) + return 0; + + watch->id = ++watch_id; + watch->index = index; + + watch->newlink = callback; + watch->user_data = user_data; + + watch_list = g_slist_prepend(watch_list, watch); + + DBG("id %d", watch->id); + + return watch->id; +} + +/** + * connman_rtnl_remove_watch: + * @id: watch identifier + * + * Remove the RTNL watch for the identifier + */ +void connman_rtnl_remove_watch(unsigned int id) +{ + GSList *list; + + DBG("id %d", id); + + if (id == 0) + return; + + for (list = watch_list; list; list = list->next) { + struct watch_data *watch = list->data; + + if (watch->id == id) { + watch_list = g_slist_remove(watch_list, watch); + g_free(watch); + break; + } + } +} + static GSList *rtnl_list = NULL; static gint compare_priority(gconstpointer a, gconstpointer b) @@ -59,13 +126,9 @@ int connman_rtnl_register(struct connman_rtnl *rtnl) { DBG("rtnl %p name %s", rtnl, rtnl->name); - g_static_rw_lock_writer_lock(&rtnl_lock); - rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl, compare_priority); - g_static_rw_lock_writer_unlock(&rtnl_lock); - return 0; } @@ -79,11 +142,7 @@ void connman_rtnl_unregister(struct connman_rtnl *rtnl) { DBG("rtnl %p name %s", rtnl, rtnl->name); - g_static_rw_lock_writer_lock(&rtnl_lock); - rtnl_list = g_slist_remove(rtnl_list, rtnl); - - g_static_rw_lock_writer_unlock(&rtnl_lock); } static void process_newlink(unsigned short type, int index, @@ -91,10 +150,6 @@ static void process_newlink(unsigned short type, int index, { GSList *list; - DBG("index %d", index); - - g_static_rw_lock_reader_lock(&rtnl_lock); - for (list = rtnl_list; list; list = list->next) { struct connman_rtnl *rtnl = list->data; @@ -102,7 +157,15 @@ static void process_newlink(unsigned short type, int index, rtnl->newlink(type, index, flags, change); } - g_static_rw_lock_reader_unlock(&rtnl_lock); + for (list = watch_list; list; list = list->next) { + struct watch_data *watch = list->data; + + if (watch->index != index) + continue; + + if (watch->newlink) + watch->newlink(flags, change, watch->user_data); + } } static void process_dellink(unsigned short type, int index, @@ -110,18 +173,75 @@ static void process_dellink(unsigned short type, int index, { GSList *list; - DBG("index %d", index); - - g_static_rw_lock_reader_lock(&rtnl_lock); - for (list = rtnl_list; list; list = list->next) { struct connman_rtnl *rtnl = list->data; if (rtnl->dellink) rtnl->dellink(type, index, flags, change); } +} + +static char *extract_gateway(struct rtmsg *msg, int bytes, int *index) +{ + char *gateway = NULL; + struct in_addr addr; + struct rtattr *attr; + + for (attr = RTM_RTA(msg); RTA_OK(attr, bytes); + attr = RTA_NEXT(attr, bytes)) { + switch (attr->rta_type) { + case RTA_GATEWAY: + addr = *((struct in_addr *) RTA_DATA(attr)); + g_free(gateway); + gateway = g_strdup(inet_ntoa(addr)); + break; + case RTA_OIF: + *index = *((int *) RTA_DATA(attr)); + break; + } + } + + return gateway; +} + +static void process_newgateway(struct rtmsg *msg, int bytes) +{ + int index = -1; + char *gateway; + GSList *list; + + gateway = extract_gateway(msg, bytes, &index); + if (gateway == NULL || index < 0) + return; + + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; + + if (rtnl->newgateway) + rtnl->newgateway(index, gateway); + } + + g_free(gateway); +} + +static void process_delgateway(struct rtmsg *msg, int bytes) +{ + int index = -1; + char *gateway; + GSList *list; + + gateway = extract_gateway(msg, bytes, &index); + if (gateway == NULL || index < 0) + return; + + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; + + if (rtnl->delgateway) + rtnl->delgateway(index, gateway); + } - g_static_rw_lock_reader_unlock(&rtnl_lock); + g_free(gateway); } static inline void print_inet(struct rtattr *attr, const char *name, int family) @@ -129,31 +249,31 @@ static inline void print_inet(struct rtattr *attr, const char *name, int family) if (family == AF_INET) { struct in_addr addr; addr = *((struct in_addr *) RTA_DATA(attr)); - DBG(" attr %s (len %jd) %s\n", - name, RTA_PAYLOAD(attr), inet_ntoa(addr)); + DBG(" attr %s (len %d) %s\n", name, + (int) RTA_PAYLOAD(attr), inet_ntoa(addr)); } else - DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr)); + DBG(" attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr)); } static inline void print_char(struct rtattr *attr, const char *name) { - DBG(" attr %s (len %jd) %s\n", name, RTA_PAYLOAD(attr), + DBG(" attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr), (char *) RTA_DATA(attr)); } static inline void print_byte(struct rtattr *attr, const char *name) { - DBG(" attr %s (len %jd) 0x%02x\n", name, RTA_PAYLOAD(attr), + DBG(" attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr), *((unsigned char *) RTA_DATA(attr))); } static inline void print_attr(struct rtattr *attr, const char *name) { if (name) - DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr)); + DBG(" attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr)); else - DBG(" attr %d (len %jd)\n", - attr->rta_type, RTA_PAYLOAD(attr)); + DBG(" attr %d (len %d)\n", + attr->rta_type, (int) RTA_PAYLOAD(attr)); } static void rtnl_link(struct nlmsghdr *hdr) @@ -236,7 +356,9 @@ static void rtnl_newlink(struct nlmsghdr *hdr) msg = (struct ifinfomsg *) NLMSG_DATA(hdr); - DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags); + DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x", + msg->ifi_type, msg->ifi_index, + msg->ifi_flags, msg->ifi_change); process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags, msg->ifi_change); @@ -250,7 +372,9 @@ static void rtnl_dellink(struct nlmsghdr *hdr) msg = (struct ifinfomsg *) NLMSG_DATA(hdr); - DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags); + DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x", + msg->ifi_type, msg->ifi_index, + msg->ifi_flags, msg->ifi_change); process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags, msg->ifi_change); @@ -302,6 +426,7 @@ static void rtnl_addr(struct nlmsghdr *hdr) static void rtnl_route(struct nlmsghdr *hdr) { +#if 0 struct rtmsg *msg; struct rtattr *attr; int bytes; @@ -346,6 +471,146 @@ static void rtnl_route(struct nlmsghdr *hdr) break; } } +#endif +} + +static void rtnl_newroute(struct nlmsghdr *hdr) +{ + struct rtmsg *msg; + + msg = (struct rtmsg *) NLMSG_DATA(hdr); + + if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN && + msg->rtm_scope == RT_SCOPE_UNIVERSE) { + DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x", + msg->rtm_table, msg->rtm_scope, + msg->rtm_type, msg->rtm_flags); + process_newgateway(msg, RTM_PAYLOAD(hdr)); + } + + rtnl_route(hdr); +} + +static void rtnl_delroute(struct nlmsghdr *hdr) +{ + struct rtmsg *msg; + + msg = (struct rtmsg *) NLMSG_DATA(hdr); + + if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN && + msg->rtm_scope == RT_SCOPE_UNIVERSE) { + DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x", + msg->rtm_table, msg->rtm_scope, + msg->rtm_type, msg->rtm_flags); + process_delgateway(msg, RTM_PAYLOAD(hdr)); + } + + rtnl_route(hdr); +} + +static const char *type2string(uint16_t type) +{ + switch (type) { + case NLMSG_NOOP: + return "NOOP"; + case NLMSG_ERROR: + return "ERROR"; + case NLMSG_DONE: + return "DONE"; + case NLMSG_OVERRUN: + return "OVERRUN"; + case RTM_GETLINK: + return "GETLINK"; + case RTM_NEWLINK: + return "NEWLINK"; + case RTM_DELLINK: + return "DELLINK"; + case RTM_NEWADDR: + return "NEWADDR"; + case RTM_DELADDR: + return "DELADDR"; + case RTM_GETROUTE: + return "GETROUTE"; + case RTM_NEWROUTE: + return "NEWROUTE"; + case RTM_DELROUTE: + return "DELROUTE"; + default: + return "UNKNOWN"; + } +} + +static GIOChannel *channel = NULL; + +struct rtnl_request { + struct nlmsghdr hdr; + struct rtgenmsg msg; +}; +#define RTNL_REQUEST_SIZE (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg)) + +static GSList *request_list = NULL; +static guint32 request_seq = 0; + +static struct rtnl_request *find_request(guint32 seq) +{ + GSList *list; + + for (list = request_list; list; list = list->next) { + struct rtnl_request *req = list->data; + + if (req->hdr.nlmsg_seq == seq) + return req; + } + + return NULL; +} + +static int send_request(struct rtnl_request *req) +{ + struct sockaddr_nl addr; + int sk; + + DBG("%s len %d type %d flags 0x%04x seq %d", + type2string(req->hdr.nlmsg_type), + req->hdr.nlmsg_len, req->hdr.nlmsg_type, + req->hdr.nlmsg_flags, req->hdr.nlmsg_seq); + + sk = g_io_channel_unix_get_fd(channel); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + return sendto(sk, req, req->hdr.nlmsg_len, 0, + (struct sockaddr *) &addr, sizeof(addr)); +} + +static int queue_request(struct rtnl_request *req) +{ + request_list = g_slist_append(request_list, req); + + if (g_slist_length(request_list) > 1) + return 0; + + return send_request(req); +} + +static int process_response(guint32 seq) +{ + struct rtnl_request *req; + + DBG("seq %d", seq); + + req = find_request(seq); + if (req != NULL) { + request_list = g_slist_remove(request_list, req); + g_free(req); + } + + req = g_slist_nth_data(request_list, 0); + if (req == NULL) + return 0; + + return send_request(req); } static void rtnl_message(void *buf, size_t len) @@ -359,51 +624,38 @@ static void rtnl_message(void *buf, size_t len) if (!NLMSG_OK(hdr, len)) break; - DBG("len %d type %d flags 0x%04x seq %d", + DBG("%s len %d type %d flags 0x%04x seq %d", + type2string(hdr->nlmsg_type), hdr->nlmsg_len, hdr->nlmsg_type, hdr->nlmsg_flags, hdr->nlmsg_seq); switch (hdr->nlmsg_type) { case NLMSG_NOOP: - DBG("NOOP"); + case NLMSG_OVERRUN: + return; + case NLMSG_DONE: + process_response(hdr->nlmsg_seq); return; case NLMSG_ERROR: err = NLMSG_DATA(hdr); - DBG("ERROR %d (%s)", -err->error, + DBG("error %d (%s)", -err->error, strerror(-err->error)); return; - case NLMSG_DONE: - DBG("DONE"); - return; - case NLMSG_OVERRUN: - DBG("OVERRUN"); - return; case RTM_NEWLINK: - DBG("NEWLINK"); rtnl_newlink(hdr); break; case RTM_DELLINK: - DBG("DELLINK"); rtnl_dellink(hdr); break; case RTM_NEWADDR: - DBG("NEWADDR"); - rtnl_addr(hdr); - break; case RTM_DELADDR: - DBG("DELADDR"); rtnl_addr(hdr); break; case RTM_NEWROUTE: - DBG("NEWROUTE"); - rtnl_route(hdr); + rtnl_newroute(hdr); break; case RTM_DELROUTE: - DBG("DELROUTE"); - rtnl_route(hdr); - break; - default: - DBG("type %d", hdr->nlmsg_type); + rtnl_delroute(hdr); break; } @@ -415,7 +667,7 @@ static void rtnl_message(void *buf, size_t len) static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data) { - unsigned char buf[256]; + unsigned char buf[4096]; gsize len; GIOError err; @@ -436,42 +688,44 @@ static gboolean netlink_event(GIOChannel *chan, return TRUE; } -static GIOChannel *channel = NULL; - -int __connman_rtnl_send(const void *buf, size_t len) +int connman_rtnl_send_getlink(void) { - struct sockaddr_nl addr; - int sk; + struct rtnl_request *req; - DBG("buf %p len %zd", buf, len); + DBG(""); - sk = g_io_channel_unix_get_fd(channel); + req = g_try_malloc0(RTNL_REQUEST_SIZE); + if (req == NULL) + return -ENOMEM; - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; + req->hdr.nlmsg_len = RTNL_REQUEST_SIZE; + req->hdr.nlmsg_type = RTM_GETLINK; + req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req->hdr.nlmsg_pid = 0; + req->hdr.nlmsg_seq = request_seq++; + req->msg.rtgen_family = AF_INET; - return sendto(sk, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); + return queue_request(req); } -int connman_rtnl_send_getlink(void) +int connman_rtnl_send_getroute(void) { - struct { - struct nlmsghdr hdr; - struct rtgenmsg msg; - } req; + struct rtnl_request *req; DBG(""); - memset(&req, 0, sizeof(req)); - req.hdr.nlmsg_len = sizeof(req.hdr) + sizeof(req.msg); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_pid = 0; - req.hdr.nlmsg_seq = 42; - req.msg.rtgen_family = AF_INET; + req = g_try_malloc0(RTNL_REQUEST_SIZE); + if (req == NULL) + return -ENOMEM; - return __connman_rtnl_send(&req, sizeof(req)); + req->hdr.nlmsg_len = RTNL_REQUEST_SIZE; + req->hdr.nlmsg_type = RTM_GETROUTE; + req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req->hdr.nlmsg_pid = 0; + req->hdr.nlmsg_seq = request_seq++; + req->msg.rtgen_family = AF_INET; + + return queue_request(req); } int __connman_rtnl_init(void) @@ -487,7 +741,7 @@ int __connman_rtnl_init(void) memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_LINK; + addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE; //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; @@ -507,8 +761,37 @@ int __connman_rtnl_init(void) void __connman_rtnl_cleanup(void) { + GSList *list; + DBG(""); + for (list = watch_list; list; list = list->next) { + struct watch_data *watch = list->data; + + DBG("removing watch %d", watch->id); + + g_free(watch); + list->data = NULL; + } + + g_slist_free(watch_list); + watch_list = NULL; + + for (list = request_list; list; list = list->next) { + struct rtnl_request *req = list->data; + + DBG("%s len %d type %d flags 0x%04x seq %d", + type2string(req->hdr.nlmsg_type), + req->hdr.nlmsg_len, req->hdr.nlmsg_type, + req->hdr.nlmsg_flags, req->hdr.nlmsg_seq); + + g_free(req); + list->data = NULL; + } + + g_slist_free(request_list); + request_list = NULL; + g_io_channel_shutdown(channel, TRUE, NULL); g_io_channel_unref(channel);