*
* 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
#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)
{
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;
}
{
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,
{
GSList *list;
- DBG("idex %d", index);
-
- g_static_rw_lock_reader_lock(&rtnl_lock);
-
for (list = rtnl_list; list; list = list->next) {
struct connman_rtnl *rtnl = list->data;
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,
{
GSList *list;
- DBG("idex %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);
}
+}
- g_static_rw_lock_reader_unlock(&rtnl_lock);
+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_link_flags(int index, short flags)
+static void process_newgateway(struct rtmsg *msg, int bytes)
{
+ int index = -1;
+ char *gateway;
GSList *list;
- DBG("idex %d", index);
+ 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);
+}
- g_static_rw_lock_reader_lock(&rtnl_lock);
+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->link_flags)
- rtnl->link_flags(index, flags);
+ 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)
static void rtnl_link(struct nlmsghdr *hdr)
{
+#if 0
struct ifinfomsg *msg;
struct rtattr *attr;
int bytes;
break;
}
}
-
- process_link_flags(msg->ifi_index, msg->ifi_flags);
+#endif
}
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);
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);
static void rtnl_route(struct nlmsghdr *hdr)
{
+#if 0
struct rtmsg *msg;
struct rtattr *attr;
int bytes;
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)
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;
}
static gboolean netlink_event(GIOChannel *chan,
GIOCondition cond, gpointer data)
{
- unsigned char buf[256];
+ unsigned char buf[4096];
gsize len;
GIOError err;
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;
+
+ 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;
- __connman_rtnl_send(&req, sizeof(req));
+ return queue_request(req);
}
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;
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);