*
* Connection Manager
*
- * Copyright (C) 2007 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2008 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 <config.h>
#endif
-#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <linux/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include "connman.h"
+static GStaticRWLock rtnl_lock = G_STATIC_RW_LOCK_INIT;
+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);
+
+ 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;
+}
+
+/**
+ * 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);
+
+ 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_link_flags(int index, short flags)
+{
+ 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->link_flags)
+ rtnl->link_flags(index, flags);
+ }
+
+ g_static_rw_lock_reader_unlock(&rtnl_lock);
+}
+
+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));
+ } else
+ DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
+}
+
static inline void print_char(struct rtattr *attr, const char *name)
{
- printf(" attr %s (len %d) %s", name, RTA_PAYLOAD(attr),
+ DBG(" attr %s (len %jd) %s\n", name, 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),
+ *((unsigned char *) RTA_DATA(attr)));
+}
+
static inline void print_attr(struct rtattr *attr, const char *name)
{
if (name)
- printf(" attr %s (len %d)", name, RTA_PAYLOAD(attr));
+ DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
else
- printf(" attr %d (len %d)", attr->rta_type, RTA_PAYLOAD(attr));
+ DBG(" attr %d (len %jd)\n",
+ attr->rta_type, RTA_PAYLOAD(attr));
}
static void rtnl_link(struct nlmsghdr *hdr)
{
- struct connman_iface *iface;
struct ifinfomsg *msg;
struct rtattr *attr;
int bytes;
msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
bytes = IFLA_PAYLOAD(hdr);
- DBG("ifi_index %d ifi_flags %d", msg->ifi_index, msg->ifi_flags);
-
- iface = __connman_iface_find(msg->ifi_index);
- if (iface == NULL)
- return;
-
- if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
- return;
+ DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
attr = RTA_NEXT(attr, bytes)) {
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");
print_attr(attr, "weight");
break;
case IFLA_OPERSTATE:
- print_attr(attr, "operstate");
+ print_byte(attr, "operstate");
break;
case IFLA_LINKMODE:
- print_attr(attr, "linkmode");
+ print_byte(attr, "linkmode");
break;
default:
print_attr(attr, NULL);
break;
}
}
+
+ process_link_flags(msg->ifi_index, msg->ifi_flags);
}
static void rtnl_addr(struct nlmsghdr *hdr)
{
- struct connman_iface *iface;
struct ifaddrmsg *msg;
struct rtattr *attr;
int bytes;
DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
- iface = __connman_iface_find(msg->ifa_index);
- if (iface == NULL)
- return;
-
- if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
- return;
-
for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case IFA_ADDRESS:
- print_attr(attr, "address");
- if (msg->ifa_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "address", msg->ifa_family);
break;
case IFA_LOCAL:
- print_attr(attr, "local");
- if (msg->ifa_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "local", msg->ifa_family);
break;
case IFA_LABEL:
print_char(attr, "label");
break;
case IFA_BROADCAST:
- print_attr(attr, "broadcast");
- if (msg->ifa_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "broadcast", msg->ifa_family);
break;
case IFA_ANYCAST:
print_attr(attr, "anycast");
msg = (struct rtmsg *) NLMSG_DATA(hdr);
bytes = RTM_PAYLOAD(hdr);
- DBG("rtm_family %d rtm_flags %d", msg->rtm_family, msg->rtm_flags);
+ DBG("rtm_family %d rtm_flags 0x%04x", msg->rtm_family, msg->rtm_flags);
for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case RTA_DST:
- print_attr(attr, "dst");
- if (msg->rtm_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "dst", msg->rtm_family);
break;
case RTA_SRC:
- print_attr(attr, "src");
- if (msg->rtm_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "src", msg->rtm_family);
break;
case RTA_IIF:
print_char(attr, "iif");
print_attr(attr, "oif");
break;
case RTA_GATEWAY:
- print_attr(attr, "gateway");
- if (msg->rtm_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "gateway", msg->rtm_family);
break;
case RTA_PRIORITY:
print_attr(attr, "priority");
break;
case RTA_PREFSRC:
- print_attr(attr, "prefsrc");
- if (msg->rtm_family == AF_INET) {
- struct in_addr addr;
- addr = *((struct in_addr *) RTA_DATA(attr));
- DBG(" address %s", inet_ntoa(addr));
- }
+ print_inet(attr, "prefsrc", msg->rtm_family);
break;
case RTA_METRICS:
print_attr(attr, "metrics");
}
}
-static void rtnl_message(unsigned char *buf, size_t size)
+static void rtnl_message(void *buf, size_t len)
{
- struct nlmsghdr *hdr = (void *) buf;
-
- if (!NLMSG_OK(hdr, size))
- return;
-
- switch (hdr->nlmsg_type) {
- case NLMSG_DONE:
- DBG("done");
- return;
- case NLMSG_NOOP:
- DBG("noop");
- return;
- case NLMSG_OVERRUN:
- DBG("overrun");
- return;
- case NLMSG_ERROR:
- DBG("error");
- return;
- case RTM_NEWLINK:
- rtnl_link(hdr);
- break;
- case RTM_DELLINK:
- rtnl_link(hdr);
- break;
- case RTM_NEWADDR:
- rtnl_addr(hdr);
- break;
- case RTM_DELADDR:
- rtnl_addr(hdr);
- break;
- case RTM_NEWROUTE:
- rtnl_route(hdr);
- break;
- case RTM_DELROUTE:
- rtnl_route(hdr);
- break;
- default:
- DBG("type %d", hdr->nlmsg_type);
- break;
+ DBG("buf %p len %zd", buf, len);
+
+ while (len > 0) {
+ struct nlmsghdr *hdr = buf;
+ struct nlmsgerr *err;
+
+ if (!NLMSG_OK(hdr, len))
+ break;
+
+ DBG("len %d type %d flags 0x%04x seq %d",
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
+
+ switch (hdr->nlmsg_type) {
+ case NLMSG_NOOP:
+ DBG("NOOP");
+ return;
+ case NLMSG_ERROR:
+ err = NLMSG_DATA(hdr);
+ 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);
+ break;
+ case RTM_DELLINK:
+ DBG("DELLINK");
+ rtnl_link(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);
+ break;
+ case RTM_DELROUTE:
+ DBG("DELROUTE");
+ rtnl_route(hdr);
+ break;
+ default:
+ DBG("type %d", hdr->nlmsg_type);
+ break;
+ }
+
+ len -= hdr->nlmsg_len;
+ buf += hdr->nlmsg_len;
}
}
gsize len;
GIOError err;
- if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
- g_io_channel_unref(chan);
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
return FALSE;
- }
memset(buf, 0, sizeof(buf));
if (err) {
if (err == G_IO_ERROR_AGAIN)
return TRUE;
- g_io_channel_unref(chan);
return FALSE;
}
static GIOChannel *channel = NULL;
+int __connman_rtnl_send(const void *buf, size_t len)
+{
+ struct sockaddr_nl addr;
+ int sk;
+
+ DBG("buf %p len %zd", buf, len);
+
+ sk = g_io_channel_unix_get_fd(channel);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+
+ return sendto(sk, buf, len, 0,
+ (struct sockaddr *) &addr, sizeof(addr));
+}
+
int __connman_rtnl_init(void)
{
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK;
+ //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
//addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
- addr.nl_pid = getpid();
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(sk);
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_channel_unref(channel);
+ g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ netlink_event, NULL);
return 0;
}
{
DBG("");
+ g_io_channel_shutdown(channel, TRUE, NULL);
g_io_channel_unref(channel);
channel = NULL;