Add experimental netlink support
authorMarcel Holtmann <marcel@holtmann.org>
Tue, 25 Dec 2007 08:45:07 +0000 (09:45 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 25 Dec 2007 08:45:07 +0000 (09:45 +0100)
include/Makefile.am
include/rtnl.h [new file with mode: 0644]
src/Makefile.am
src/connman.h
src/main.c
src/rtnl.c [new file with mode: 0644]

index 627ab0a..2d28a82 100644 (file)
@@ -1,7 +1,7 @@
 
 includedir = @includedir@/connman
 
-noinst_HEADERS = plugin.h iface.h dhcp.h
+noinst_HEADERS = plugin.h iface.h rtnl.h dhcp.h
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/include/rtnl.h b/include/rtnl.h
new file mode 100644 (file)
index 0000000..4b3e692
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007  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
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_RTNL_H
+#define __CONNMAN_RTNL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RTNL_H */
index d693035..65636af 100644 (file)
@@ -5,7 +5,7 @@ dbus_DATA = connman.conf
 
 sbin_PROGRAMS = connmand
 
-connmand_SOURCES = main.c connman.h plugin.c iface.c dhcp.c
+connmand_SOURCES = main.c connman.h plugin.c iface.c rtnl.c dhcp.c
 
 connmand_LDADD = @HAL_LIBS@ @GDBUS_LIBS@ @GMODULE_LIBS@
  
index 1cd6c16..b1b6cbe 100644 (file)
@@ -44,6 +44,11 @@ void __connman_plugin_cleanup(void);
 int __connman_iface_init(DBusConnection *conn);
 void __connman_iface_cleanup(void);
 
+#include <connman/rtnl.h>
+
+int __connman_rtnl_init(void);
+void __connman_rtnl_cleanup(void);
+
 #include <connman/dhcp.h>
 
 int __connman_dhcp_request(struct connman_iface *iface);
index 245dece..40d6e07 100644 (file)
@@ -63,6 +63,8 @@ int main(int argc, char *argv[])
 
        __connman_iface_init(conn);
 
+       __connman_rtnl_init();
+
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = sig_term;
        sigaction(SIGINT, &sa, NULL);
@@ -70,6 +72,8 @@ int main(int argc, char *argv[])
 
        g_main_loop_run(main_loop);
 
+       __connman_rtnl_cleanup();
+
        __connman_iface_cleanup();
 
        __connman_plugin_cleanup();
diff --git a/src/rtnl.c b/src/rtnl.c
new file mode 100644 (file)
index 0000000..c88aad1
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007  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
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+static void parse_link(struct nlmsghdr *hdr)
+{
+       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);
+
+       for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               int len = RTA_PAYLOAD(attr);
+
+               switch (attr->rta_type) {
+               case IFLA_ADDRESS:
+                       DBG("  rta_type address len %d", len);
+                       break;
+               case IFLA_BROADCAST:
+                       DBG("  rta_type broadcast len %d", len);
+                       break;
+               case IFLA_IFNAME:
+                       DBG("  rta_type ifname %s", (char *) RTA_DATA(attr));
+                       break;
+               case IFLA_MTU:
+                       DBG("  rta_type mtu len %d", len);
+                       break;
+               case IFLA_LINK:
+                       DBG("  rta_type link len %d", len);
+                       break;
+               case IFLA_QDISC:
+                       DBG("  rta_type qdisc len %d", len);
+                       break;
+               case IFLA_STATS:
+                       DBG("  rta_type stats len %d", len);
+                       break;
+               case IFLA_COST:
+                       DBG("  rta_type cost len %d", len);
+                       break;
+               case IFLA_PRIORITY:
+                       DBG("  rta_type priority len %d", len);
+                       break;
+               case IFLA_MASTER:
+                       DBG("  rta_type master len %d", len);
+                       break;
+               case IFLA_WIRELESS:
+                       DBG("  rta_type wireless len %d", len);
+                       {
+                               unsigned char *data = RTA_DATA(attr);
+                               int i;
+                               for (i = 0; i < len; i++)
+                                       printf(" %02x", data[i]);
+                               printf("\n");
+                       }
+                       break;
+               case IFLA_PROTINFO:
+                       DBG("  rta_type protinfo len %d", len);
+                       break;
+               case IFLA_TXQLEN:
+                       DBG("  rta_type txqlen len %d", len);
+                       break;
+               case IFLA_MAP:
+                       DBG("  rta_type map len %d", len);
+                       break;
+               case IFLA_WEIGHT:
+                       DBG("  rta_type widght len %d", len);
+                       break;
+               case IFLA_OPERSTATE:
+                       DBG("  rta_type operstate len %d", len);
+                       break;
+               case IFLA_LINKMODE:
+                       DBG("  rta_type linkmode len %d", len);
+                       break;
+               default:
+                       DBG("  rta_type %d len %d", attr->rta_type, len);
+                       break;
+               }
+       }
+}
+
+static void parse_addr(struct nlmsghdr *hdr)
+{
+       struct ifaddrmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+       bytes = IFA_PAYLOAD(hdr);
+
+       DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
+
+       for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               int len = RTA_PAYLOAD(attr);
+
+               switch (attr->rta_type) {
+               case IFA_ADDRESS:
+                       DBG("  rta_type address len %d", len);
+                       if (msg->ifa_family == AF_INET) {
+                               struct in_addr addr;
+                               addr = *((struct in_addr *) RTA_DATA(attr));
+                               DBG("    address %s", inet_ntoa(addr));
+                       }
+                       break;
+               case IFA_LOCAL:
+                       DBG("  rta_type local len %d", len);
+                       if (msg->ifa_family == AF_INET) {
+                               struct in_addr addr;
+                               addr = *((struct in_addr *) RTA_DATA(attr));
+                               DBG("    address %s", inet_ntoa(addr));
+                       }
+                       break;
+               case IFA_LABEL:
+                       DBG("  rta_type label %s", (char *) RTA_DATA(attr));
+                       break;
+               case IFA_BROADCAST:
+                       DBG("  rta_type broadcast len %d", len);
+                       if (msg->ifa_family == AF_INET) {
+                               struct in_addr addr;
+                               addr = *((struct in_addr *) RTA_DATA(attr));
+                               DBG("    address %s", inet_ntoa(addr));
+                       }
+                       break;
+               case IFA_ANYCAST:
+                       DBG("  rta_type anycast len %d", len);
+                       break;
+               case IFA_CACHEINFO:
+                       DBG("  rta_type cacheinfo len %d", len);
+                       break;
+               case IFA_MULTICAST:
+                       DBG("  rta_type multicast len %d", len);
+                       break;
+               default:
+                       DBG("  rta_type %d len %d", attr->rta_type, len);
+                       break;
+               }
+       }
+}
+
+static void parse_route(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
+       bytes = IFA_PAYLOAD(hdr);
+
+       DBG("rtm_family %d rtm_flags %d", msg->rtm_family, msg->rtm_flags);
+
+       for (attr = RTA_DATA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               int len = RTA_PAYLOAD(attr);
+
+               switch (attr->rta_type) {
+               case RTA_DST:
+                       DBG("  rta_type dst len %d", len);
+                       break;
+               case RTA_SRC:
+                       DBG("  rta_type src len %d", len);
+                       break;
+               case RTA_IIF:
+                       DBG("  rta_type iff len %d", len);
+                       break;
+               case RTA_OIF:
+                       DBG("  rta_type oif len %d", len);
+                       break;
+               case RTA_GATEWAY:
+                       DBG("  rta_type gateway len %d", len);
+                       break;
+               default:
+                       DBG("  rta_type %d len %d", attr->rta_type, len);
+                       break;
+               }
+       }
+}
+
+static void parse_message(unsigned char *buf, size_t size)
+{
+       struct nlmsghdr *hdr = (void *) buf;
+
+       if (!NLMSG_OK(hdr, size))
+               return;
+
+       switch (hdr->nlmsg_type) {
+       case NLMSG_DONE:
+               DBG("nlmsg_type done");
+               return;
+       case NLMSG_NOOP:
+               DBG("nlmsg_type noop");
+               return;
+       case NLMSG_OVERRUN:
+               DBG("nlmsg_type overrun");
+               return;
+       case NLMSG_ERROR:
+               DBG("nlmsg_type error");
+               return;
+       case RTM_NEWLINK:
+               DBG("nlmsg_type RTM_NEWLINK");
+               parse_link(hdr);
+               break;
+       case RTM_DELLINK:
+               DBG("nlmsg_type RTM_DELLINK");
+               parse_link(hdr);
+               break;
+       case RTM_NEWADDR:
+               DBG("nlmsg_type RTM_NEWADDR");
+               parse_addr(hdr);
+               break;
+       case RTM_DELADDR:
+               DBG("nlmsg_type RTM_DELADDR");
+               parse_addr(hdr);
+               break;
+       case RTM_NEWROUTE:
+               DBG("nlmsg_type RTM_NEWROUTE");
+               parse_route(hdr);
+               break;
+       case RTM_DELROUTE:
+               DBG("nlmsg_type RTM_DELROUTE");
+               parse_route(hdr);
+               break;
+       default:
+               DBG("nlmsg_type %d", hdr->nlmsg_type);
+               break;
+       }
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[256];
+       gsize len;
+       GIOError err;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               g_io_channel_unref(chan);
+               return FALSE;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+       if (err) {
+               if (err == G_IO_ERROR_AGAIN)
+                       return TRUE;
+               g_io_channel_unref(chan);
+               return FALSE;
+       }
+
+       parse_message(buf, len);
+
+       return TRUE;
+}
+
+static GIOChannel *channel = NULL;
+
+int __connman_rtnl_init(void)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("");
+
+       sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (sk < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       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);
+               return -1;
+       }
+
+       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);
+
+       return 0;
+}
+
+void __connman_rtnl_cleanup(void)
+{
+       DBG("");
+
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+}