X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Frtnl.c;h=70e8c3cbc52e4b3adc3cd68b108fa81f4214efa4;hb=a922337a31fab30b2f6af5dd96efb9cb338169fb;hp=952f09b3128be465992632a0087606c9b7becf0e;hpb=5892fc367065eb79210359008da69887322327fa;p=connman diff --git a/src/rtnl.c b/src/rtnl.c index 952f09b..70e8c3c 100644 --- a/src/rtnl.c +++ b/src/rtnl.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007 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 @@ -23,7 +23,6 @@ #include #endif -#include #include #include #include @@ -37,19 +36,212 @@ #include "connman.h" -struct rtnl_data { - unsigned ifi_flags; +struct watch_data { + unsigned int id; + int index; + connman_rtnl_link_cb_t newlink; + void *user_data; }; -static struct rtnl_data *get_rtnl_data(struct connman_iface *iface) +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) +{ + const struct connman_rtnl *rtnl1 = a; + const struct connman_rtnl *rtnl2 = b; + + return rtnl2->priority - rtnl1->priority; +} + +/** + * connman_rtnl_register: + * @rtnl: RTNL module + * + * Register a new RTNL module + * + * Returns: %0 on success + */ +int connman_rtnl_register(struct connman_rtnl *rtnl) +{ + DBG("rtnl %p name %s", rtnl, rtnl->name); + + rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl, + compare_priority); + + return 0; +} + +/** + * connman_rtnl_unregister: + * @rtnl: RTNL module + * + * Remove a previously registered RTNL module + */ +void connman_rtnl_unregister(struct connman_rtnl *rtnl) +{ + DBG("rtnl %p name %s", rtnl, rtnl->name); + + rtnl_list = g_slist_remove(rtnl_list, rtnl); +} + +static void process_newlink(unsigned short type, int index, + unsigned flags, unsigned change) +{ + GSList *list; + + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; + + if (rtnl->newlink) + rtnl->newlink(type, index, flags, change); + } + + 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, + unsigned flags, unsigned change) +{ + GSList *list; + + 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) { - if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0) - return NULL; + int index = -1; + char *gateway; + GSList *list; + + gateway = extract_gateway(msg, bytes, &index); + if (gateway == NULL || index < 0) + return; - if (iface->rtnl_data == NULL) - iface->rtnl_data = g_try_new0(struct rtnl_data, 1); + for (list = rtnl_list; list; list = list->next) { + struct connman_rtnl *rtnl = list->data; - return iface->rtnl_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_free(gateway); } static inline void print_inet(struct rtattr *attr, const char *name, int family) @@ -57,37 +249,36 @@ 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)); - printf(" 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 - printf(" 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) { - printf(" 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) { - printf(" 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) - printf(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr)); + DBG(" attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr)); else - printf(" 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) { - struct connman_iface *iface; - struct rtnl_data *data; +#if 0 struct ifinfomsg *msg; struct rtattr *attr; int bytes; @@ -97,30 +288,6 @@ static void rtnl_link(struct nlmsghdr *hdr) DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags); - iface = __connman_iface_find(msg->ifi_index); - if (iface == NULL) - return; - - data = get_rtnl_data(iface); - if (data == NULL) - return; - - if ((data->ifi_flags & IFF_RUNNING) != (msg->ifi_flags & IFF_RUNNING)) { - if (msg->ifi_flags & IFF_RUNNING) - connman_iface_indicate_carrier_on(iface); - else - connman_iface_indicate_carrier_off(iface); - } - - if ((data->ifi_flags & IFF_UP) != (msg->ifi_flags & IFF_UP)) { - if (msg->ifi_flags & IFF_UP) - connman_iface_indicate_enabled(iface); - else - connman_iface_indicate_disabled(iface); - } - - data->ifi_flags = msg->ifi_flags; - for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { @@ -155,9 +322,7 @@ static void rtnl_link(struct nlmsghdr *hdr) print_attr(attr, "master"); break; case IFLA_WIRELESS: - if (iface->driver->rtnl_wireless) - iface->driver->rtnl_wireless(iface, - RTA_DATA(attr), RTA_PAYLOAD(attr)); + print_attr(attr, "wireless"); break; case IFLA_PROTINFO: print_attr(attr, "protinfo"); @@ -182,12 +347,43 @@ static void rtnl_link(struct nlmsghdr *hdr) break; } } +#endif +} + +static void rtnl_newlink(struct nlmsghdr *hdr) +{ + struct ifinfomsg *msg; + + msg = (struct ifinfomsg *) NLMSG_DATA(hdr); + + 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); + + rtnl_link(hdr); +} + +static void rtnl_dellink(struct nlmsghdr *hdr) +{ + struct ifinfomsg *msg; + + msg = (struct ifinfomsg *) NLMSG_DATA(hdr); + + 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); + + rtnl_link(hdr); } static void rtnl_addr(struct nlmsghdr *hdr) { - struct connman_iface *iface; - struct rtnl_data *data; struct ifaddrmsg *msg; struct rtattr *attr; int bytes; @@ -197,14 +393,6 @@ static void rtnl_addr(struct nlmsghdr *hdr) DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index); - iface = __connman_iface_find(msg->ifa_index); - if (iface == NULL) - return; - - data = get_rtnl_data(iface); - if (data == NULL) - return; - for (attr = IFA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) { switch (attr->rta_type) { @@ -238,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; @@ -282,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) @@ -295,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_link(hdr); + rtnl_newlink(hdr); break; case RTM_DELLINK: - DBG("DELLINK"); - rtnl_link(hdr); + 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; } @@ -351,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; @@ -372,22 +688,44 @@ static gboolean netlink_event(GIOChannel *chan, return TRUE; } -static GIOChannel *channel = NULL; +int connman_rtnl_send_getlink(void) +{ + struct rtnl_request *req; + + DBG(""); + + req = g_try_malloc0(RTNL_REQUEST_SIZE); + if (req == NULL) + return -ENOMEM; + + 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; -int __connman_rtnl_send(const void *buf, size_t len) + return queue_request(req); +} + +int connman_rtnl_send_getroute(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_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 sendto(sk, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); + return queue_request(req); } int __connman_rtnl_init(void) @@ -403,7 +741,8 @@ int __connman_rtnl_init(void) memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + 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; if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { @@ -414,17 +753,45 @@ int __connman_rtnl_init(void) channel = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(channel, TRUE); - g_io_add_watch(channel, - G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, - netlink_event, NULL); + g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + netlink_event, NULL); return 0; } 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);