Remove obsolete file.
[connman] / src / rtnl.c
index 726d491..70e8c3c 100644 (file)
@@ -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 <config.h>
 #endif
 
-#include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/socket.h>
 
 #include "connman.h"
 
+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)
+{
+       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)
+{
+       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_free(gateway);
+}
+
+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 %d) %s\n", name,
+                               (int) RTA_PAYLOAD(attr), inet_ntoa(addr));
+       } else
+               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 %d) %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 %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 %d)\n", name, RTA_PAYLOAD(attr));
+               DBG("  attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr));
        else
-               printf("  attr %d (len %d)\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;
+#if 0
        struct ifinfomsg *msg;
        struct rtattr *attr;
        int bytes;
@@ -64,18 +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;
-
-       if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
-               return;
-
-       if (iface->carrier != (msg->ifi_flags & IFF_RUNNING)) {
-               iface->carrier = (msg->ifi_flags & IFF_RUNNING);
-               DBG("carrier %s", iface->carrier ? "on" : "off");
-       }
-
        for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
                                        attr = RTA_NEXT(attr, bytes)) {
                switch (attr->rta_type) {
@@ -110,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");
@@ -127,21 +337,53 @@ static void rtnl_link(struct nlmsghdr *hdr)
                        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;
                }
        }
+#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 ifaddrmsg *msg;
        struct rtattr *attr;
        int bytes;
@@ -151,42 +393,20 @@ 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;
-
-       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");
@@ -206,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;
@@ -219,20 +440,10 @@ static void rtnl_route(struct nlmsghdr *hdr)
                                        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");
@@ -241,23 +452,13 @@ static void rtnl_route(struct nlmsghdr *hdr)
                        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");
@@ -270,63 +471,208 @@ static void rtnl_route(struct nlmsghdr *hdr)
                        break;
                }
        }
+#endif
 }
 
-static void rtnl_message(unsigned char *buf, size_t size)
+static void rtnl_newroute(struct nlmsghdr *hdr)
 {
-       struct nlmsghdr *hdr = (void *) buf;
+       struct rtmsg *msg;
 
-       if (!NLMSG_OK(hdr, size))
-               return;
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
 
-       switch (hdr->nlmsg_type) {
-       case NLMSG_DONE:
-               DBG("done");
-               return;
+       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:
-               DBG("noop");
-               return;
-       case NLMSG_OVERRUN:
-               DBG("overrun");
-               return;
+               return "NOOP";
        case NLMSG_ERROR:
-               DBG("error");
-               return;
+               return "ERROR";
+       case NLMSG_DONE:
+               return "DONE";
+       case NLMSG_OVERRUN:
+               return "OVERRUN";
+       case RTM_GETLINK:
+               return "GETLINK";
        case RTM_NEWLINK:
-               rtnl_link(hdr);
-               break;
+               return "NEWLINK";
        case RTM_DELLINK:
-               rtnl_link(hdr);
-               break;
+               return "DELLINK";
        case RTM_NEWADDR:
-               rtnl_addr(hdr);
-               break;
+               return "NEWADDR";
        case RTM_DELADDR:
-               rtnl_addr(hdr);
-               break;
+               return "DELADDR";
+       case RTM_GETROUTE:
+               return "GETROUTE";
        case RTM_NEWROUTE:
-               rtnl_route(hdr);
-               break;
+               return "NEWROUTE";
        case RTM_DELROUTE:
-               rtnl_route(hdr);
-               break;
+               return "DELROUTE";
        default:
-               DBG("type %d", hdr->nlmsg_type);
-               break;
+               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)
+{
+       DBG("buf %p len %zd", buf, len);
+
+       while (len > 0) {
+               struct nlmsghdr *hdr = buf;
+               struct nlmsgerr *err;
+
+               if (!NLMSG_OK(hdr, len))
+                       break;
+
+               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:
+               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,
+                                               strerror(-err->error));
+                       return;
+               case RTM_NEWLINK:
+                       rtnl_newlink(hdr);
+                       break;
+               case RTM_DELLINK:
+                       rtnl_dellink(hdr);
+                       break;
+               case RTM_NEWADDR:
+               case RTM_DELADDR:
+                       rtnl_addr(hdr);
+                       break;
+               case RTM_NEWROUTE:
+                       rtnl_newroute(hdr);
+                       break;
+               case RTM_DELROUTE:
+                       rtnl_delroute(hdr);
+                       break;
+               }
+
+               len -= hdr->nlmsg_len;
+               buf += hdr->nlmsg_len;
        }
 }
 
 static gboolean netlink_event(GIOChannel *chan,
                                GIOCondition cond, gpointer data)
 {
-       unsigned char buf[256];
+       unsigned char buf[4096];
        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));
 
@@ -334,7 +680,6 @@ static gboolean netlink_event(GIOChannel *chan,
        if (err) {
                if (err == G_IO_ERROR_AGAIN)
                        return TRUE;
-               g_io_channel_unref(chan);
                return FALSE;
        }
 
@@ -343,7 +688,45 @@ 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;
+
+       return queue_request(req);
+}
+
+int connman_rtnl_send_getroute(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_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)
 {
@@ -358,9 +741,9 @@ 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;
-       addr.nl_pid = getpid();
 
        if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                close(sk);
@@ -370,19 +753,46 @@ 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_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;
 }
 
 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);
 
        channel = NULL;