*
* Connection Manager
*
- * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2010 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 <errno.h>
+#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#ifndef ARPHDR_PHONET_PIPE
+#define ARPHDR_PHONET_PIPE (821)
+#endif
+
+#define print(arg...) do { if (0) connman_info(arg); } while (0)
+//#define print(arg...) connman_info(arg)
+
+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;
+
+static GSList *update_list = NULL;
+static guint update_interval = G_MAXUINT;
+static guint update_timeout = 0;
+
+struct interface_data {
+ int index;
+ char *name;
+ char *ident;
+ enum connman_service_type service_type;
+ enum connman_device_type device_type;
+};
+
+static GHashTable *interface_list = NULL;
+
+static void free_interface(gpointer data)
+{
+ struct interface_data *interface = data;
+
+ __connman_technology_remove_interface(interface->service_type,
+ interface->index, interface->name, interface->ident);
+
+ g_free(interface->ident);
+ g_free(interface->name);
+ g_free(interface);
+}
+
+static connman_bool_t ether_blacklisted(const char *name)
+{
+ if (name == NULL)
+ return TRUE;
+
+ /* virtual interface from VMware */
+ if (g_str_has_prefix(name, "vmnet") == TRUE)
+ return TRUE;
+
+ /* virtual interface from VirtualBox */
+ if (g_str_has_prefix(name, "vboxnet") == TRUE)
+ return TRUE;
+
+ /* virtual interface from Virtual Machine Manager */
+ if (g_str_has_prefix(name, "virbr") == TRUE)
+ return TRUE;
+
+ return FALSE;
+}
+
+static connman_bool_t wext_interface(char *ifname)
+{
+ struct iwreq wrq;
+ int fd, err;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return FALSE;
+
+ memset(&wrq, 0, sizeof(wrq));
+ strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
+
+ err = ioctl(fd, SIOCGIWNAME, &wrq);
+
+ close(fd);
+
+ if (err < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void read_uevent(struct interface_data *interface)
+{
+ char *filename, line[128];
+ connman_bool_t found_devtype;
+ FILE *f;
+
+ if (ether_blacklisted(interface->name) == TRUE) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+ interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+ } else {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+ }
+
+ filename = g_strdup_printf("/sys/class/net/%s/uevent",
+ interface->name);
+
+ f = fopen(filename, "re");
+
+ g_free(filename);
+
+ if (f == NULL)
+ return;
+
+ found_devtype = FALSE;
+ while (fgets(line, sizeof(line), f)) {
+ char *pos;
+
+ pos = strchr(line, '\n');
+ if (pos == NULL)
+ continue;
+ pos[0] = '\0';
+
+ if (strncmp(line, "DEVTYPE=", 8) != 0)
+ continue;
+
+ found_devtype = TRUE;
+
+ if (strcmp(line + 8, "wlan") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_WIFI;
+ interface->device_type = CONNMAN_DEVICE_TYPE_WIFI;
+ } else if (strcmp(line + 8, "wwan") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
+ interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
+ } else if (strcmp(line + 8, "bluetooth") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
+ interface->device_type = CONNMAN_DEVICE_TYPE_BLUETOOTH;
+ } else if (strcmp(line + 8, "wimax") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_WIMAX;
+ interface->device_type = CONNMAN_DEVICE_TYPE_WIMAX;
+ } else if (strcmp(line + 8, "gadget") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_GADGET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_GADGET;
+
+ } else {
+ interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+ interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+ }
+ }
+
+ fclose(f);
+
+ if (found_devtype)
+ return;
+
+ /* We haven't got a DEVTYPE, let's check if it's a wireless device */
+ if (wext_interface(interface->name)) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_WIFI;
+ interface->device_type = CONNMAN_DEVICE_TYPE_WIFI;
+
+ connman_error("%s runs an unsupported 802.11 driver",
+ interface->name);
+ }
+}
+
+enum connman_device_type __connman_rtnl_get_device_type(int index)
+{
+ struct interface_data *interface;
+
+ interface = g_hash_table_lookup(interface_list,
+ GINT_TO_POINTER(index));
+ if (interface == NULL)
+ return CONNMAN_DEVICE_TYPE_UNKNOWN;
+
+ return interface->device_type;
+}
+
+/**
+ * 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);
+
+ if (callback) {
+ unsigned int flags = __connman_ipconfig_get_flags_from_index(index);
+
+ if (flags > 0)
+ callback(flags, 0, user_data);
+ }
+
+ 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 void trigger_rtnl(int index, void *user_data)
+{
+ struct connman_rtnl *rtnl = user_data;
+
+ if (rtnl->newlink) {
+ unsigned short type = __connman_ipconfig_get_type_from_index(index);
+ unsigned int flags = __connman_ipconfig_get_flags_from_index(index);
+
+ rtnl->newlink(type, index, flags, 0);
+ }
+
+ if (rtnl->newgateway) {
+ const char *gateway = __connman_ipconfig_get_gateway_from_index(index);
+
+ if (gateway != NULL)
+ rtnl->newgateway(index, gateway);
+ }
+}
+
+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);
+
+ __connman_ipconfig_foreach(trigger_rtnl, rtnl);
+
+ 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 const char *operstate2str(unsigned char operstate)
+{
+ switch (operstate) {
+ case IF_OPER_UNKNOWN:
+ return "UNKNOWN";
+ case IF_OPER_NOTPRESENT:
+ return "NOT-PRESENT";
+ case IF_OPER_DOWN:
+ return "DOWN";
+ case IF_OPER_LOWERLAYERDOWN:
+ return "LOWER-LAYER-DOWN";
+ case IF_OPER_TESTING:
+ return "TESTING";
+ case IF_OPER_DORMANT:
+ return "DORMANT";
+ case IF_OPER_UP:
+ return "UP";
+ }
+
+ return "";
+}
+
+static void extract_link(struct ifinfomsg *msg, int bytes,
+ struct ether_addr *address, const char **ifname,
+ unsigned int *mtu, unsigned char *operstate,
+ struct rtnl_link_stats *stats)
+{
+ struct rtattr *attr;
+
+ for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+ attr = RTA_NEXT(attr, bytes)) {
+ switch (attr->rta_type) {
+ case IFLA_ADDRESS:
+ if (address != NULL)
+ memcpy(address, RTA_DATA(attr), ETH_ALEN);
+ break;
+ case IFLA_IFNAME:
+ if (ifname != NULL)
+ *ifname = RTA_DATA(attr);
+ break;
+ case IFLA_MTU:
+ if (mtu != NULL)
+ *mtu = *((unsigned int *) RTA_DATA(attr));
+ break;
+ case IFLA_STATS:
+ if (stats != NULL)
+ memcpy(stats, RTA_DATA(attr),
+ sizeof(struct rtnl_link_stats));
+ break;
+ case IFLA_OPERSTATE:
+ if (operstate != NULL)
+ *operstate = *((unsigned char *) RTA_DATA(attr));
+ break;
+ case IFLA_LINKMODE:
+ break;
+ }
+ }
+}
+
+static void process_newlink(unsigned short type, int index, unsigned flags,
+ unsigned change, struct ifinfomsg *msg, int bytes)
+{
+ struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }};
+ struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }};
+ struct rtnl_link_stats stats;
+ unsigned char operstate = 0xff;
+ struct interface_data *interface;
+ const char *ifname = NULL;
+ unsigned int mtu = 0;
+ char ident[13], str[18];
+ GSList *list;
+
+ memset(&stats, 0, sizeof(stats));
+ extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats);
+
+ snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+ address.ether_addr_octet[0],
+ address.ether_addr_octet[1],
+ address.ether_addr_octet[2],
+ address.ether_addr_octet[3],
+ address.ether_addr_octet[4],
+ address.ether_addr_octet[5]);
+
+ snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+ address.ether_addr_octet[0],
+ address.ether_addr_octet[1],
+ address.ether_addr_octet[2],
+ address.ether_addr_octet[3],
+ address.ether_addr_octet[4],
+ address.ether_addr_octet[5]);
+
+ switch (type) {
+ case ARPHRD_ETHER:
+ case ARPHRD_LOOPBACK:
+ case ARPHDR_PHONET_PIPE:
+ case ARPHRD_NONE:
+ __connman_ipconfig_newlink(index, type, flags,
+ str, mtu, &stats);
+ break;
+ }
+
+ if (memcmp(&address, &compare, ETH_ALEN) != 0)
+ connman_info("%s {newlink} index %d address %s mtu %u",
+ ifname, index, str, mtu);
+
+ if (operstate != 0xff)
+ connman_info("%s {newlink} index %d operstate %u <%s>",
+ ifname, index, operstate,
+ operstate2str(operstate));
+
+ interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index));
+ if (interface == NULL) {
+ interface = g_new0(struct interface_data, 1);
+ interface->index = index;
+ interface->name = g_strdup(ifname);
+ interface->ident = g_strdup(ident);
+
+ g_hash_table_insert(interface_list,
+ GINT_TO_POINTER(index), interface);
+
+ if (type == ARPHRD_ETHER)
+ read_uevent(interface);
+
+ __connman_technology_add_interface(interface->service_type,
+ interface->index, interface->name, interface->ident);
+ }
+
+ 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, struct ifinfomsg *msg, int bytes)
+{
+ struct rtnl_link_stats stats;
+ unsigned char operstate = 0xff;
+ const char *ifname = NULL;
+ GSList *list;
+
+ memset(&stats, 0, sizeof(stats));
+ extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats);
+
+ if (operstate != 0xff)
+ connman_info("%s {dellink} index %d operstate %u <%s>",
+ ifname, index, operstate,
+ operstate2str(operstate));
+
+ for (list = rtnl_list; list; list = list->next) {
+ struct connman_rtnl *rtnl = list->data;
+
+ if (rtnl->dellink)
+ rtnl->dellink(type, index, flags, change);
+ }
+
+ switch (type) {
+ case ARPHRD_ETHER:
+ case ARPHRD_LOOPBACK:
+ case ARPHRD_NONE:
+ __connman_ipconfig_dellink(index, &stats);
+ break;
+ }
+
+ g_hash_table_remove(interface_list, GINT_TO_POINTER(index));
+}
+
+static void extract_ipv4_addr(struct ifaddrmsg *msg, int bytes,
+ const char **label,
+ struct in_addr *local,
+ struct in_addr *address,
+ struct in_addr *broadcast)
+{
+ struct rtattr *attr;
+
+ for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+ attr = RTA_NEXT(attr, bytes)) {
+ switch (attr->rta_type) {
+ case IFA_ADDRESS:
+ if (address != NULL)
+ *address = *((struct in_addr *) RTA_DATA(attr));
+ break;
+ case IFA_LOCAL:
+ if (local != NULL)
+ *local = *((struct in_addr *) RTA_DATA(attr));
+ break;
+ case IFA_BROADCAST:
+ if (broadcast != NULL)
+ *broadcast = *((struct in_addr *) RTA_DATA(attr));
+ break;
+ case IFA_LABEL:
+ if (label != NULL)
+ *label = RTA_DATA(attr);
+ break;
+ }
+ }
+}
+
+static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes,
+ struct in6_addr *addr,
+ struct in6_addr *local)
+{
+ struct rtattr *attr;
+
+ for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+ attr = RTA_NEXT(attr, bytes)) {
+ switch (attr->rta_type) {
+ case IFA_ADDRESS:
+ if (addr != NULL)
+ *addr = *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ case IFA_LOCAL:
+ if (local != NULL)
+ *local = *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ }
+ }
+}
+
+static void process_newaddr(unsigned char family, unsigned char prefixlen,
+ int index, struct ifaddrmsg *msg, int bytes)
+{
+ const char *label = NULL;
+ void *src;
+ char ip_string[INET6_ADDRSTRLEN];
+
+ if (family == AF_INET) {
+ struct in_addr ipv4_addr = { INADDR_ANY };
+
+ extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
+ src = &ipv4_addr;
+ } else if (family == AF_INET6) {
+ struct in6_addr ipv6_address, ipv6_local;
+
+ extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
+ if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
+ return;
+
+ src = &ipv6_address;
+ } else {
+ return;
+ }
+
+ if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL)
+ return;
+
+ __connman_ipconfig_newaddr(index, family, label,
+ prefixlen, ip_string);
+}
+
+static void process_deladdr(unsigned char family, unsigned char prefixlen,
+ int index, struct ifaddrmsg *msg, int bytes)
+{
+ const char *label = NULL;
+ void *src;
+ char ip_string[INET6_ADDRSTRLEN];
+
+ if (family == AF_INET) {
+ struct in_addr ipv4_addr = { INADDR_ANY };
+
+ extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
+ src = &ipv4_addr;
+ } else if (family == AF_INET6) {
+ struct in6_addr ipv6_address, ipv6_local;
+
+ extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
+ if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
+ return;
+
+ src = &ipv6_address;
+ } else {
+ return;
+ }
+
+ if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL)
+ return;
+
+ __connman_ipconfig_deladdr(index, family, label,
+ prefixlen, ip_string);
+}
+
+static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index,
+ struct in_addr *dst,
+ struct in_addr *gateway)
+{
+ struct rtattr *attr;
+
+ for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+ attr = RTA_NEXT(attr, bytes)) {
+ switch (attr->rta_type) {
+ case RTA_DST:
+ if (dst != NULL)
+ *dst = *((struct in_addr *) RTA_DATA(attr));
+ break;
+ case RTA_GATEWAY:
+ if (gateway != NULL)
+ *gateway = *((struct in_addr *) RTA_DATA(attr));
+ break;
+ case RTA_OIF:
+ if (index != NULL)
+ *index = *((int *) RTA_DATA(attr));
+ break;
+ }
+ }
+}
+
+static void extract_ipv6_route(struct rtmsg *msg, int bytes, int *index,
+ struct in6_addr *dst,
+ struct in6_addr *gateway)
+{
+ struct rtattr *attr;
+
+ for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+ attr = RTA_NEXT(attr, bytes)) {
+ switch (attr->rta_type) {
+ case RTA_DST:
+ if (dst != NULL)
+ *dst = *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ case RTA_GATEWAY:
+ if (gateway != NULL)
+ *gateway =
+ *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ case RTA_OIF:
+ if (index != NULL)
+ *index = *((int *) RTA_DATA(attr));
+ break;
+ }
+ }
+}
+
+static void process_newroute(unsigned char family, unsigned char scope,
+ struct rtmsg *msg, int bytes)
+{
+ GSList *list;
+ char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+ int index = -1;
+
+ if (family == AF_INET) {
+ struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+ extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+ __connman_ipconfig_newroute(index, family, scope, dststr,
+ gatewaystr);
+
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
+ !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+ return;
+
+ if (dst.s_addr != INADDR_ANY)
+ return;
+
+ } else if (family == AF_INET6) {
+ struct in6_addr dst = IN6ADDR_ANY_INIT,
+ gateway = IN6ADDR_ANY_INIT;
+
+ extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+ __connman_ipconfig_newroute(index, family, scope, dststr,
+ gatewaystr);
+
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
+ !(scope == RT_SCOPE_LINK &&
+ IN6_IS_ADDR_UNSPECIFIED(&dst)))
+ return;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+ return;
+ } else
+ return;
+
+ for (list = rtnl_list; list; list = list->next) {
+ struct connman_rtnl *rtnl = list->data;
+
+ if (rtnl->newgateway)
+ rtnl->newgateway(index, gatewaystr);
+ }
+}
+
+static void process_delroute(unsigned char family, unsigned char scope,
+ struct rtmsg *msg, int bytes)
+{
+ GSList *list;
+ char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+ int index = -1;
+
+ if (family == AF_INET) {
+ struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+ extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
-#include <linux/if.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
+ __connman_ipconfig_delroute(index, family, scope, dststr,
+ gatewaystr);
-#include <glib.h>
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
+ !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+ return;
-#include "connman.h"
+ if (dst.s_addr != INADDR_ANY)
+ return;
-struct rtnl_data {
- unsigned ifi_flags;
-};
+ } else if (family == AF_INET6) {
+ struct in6_addr dst = IN6ADDR_ANY_INIT,
+ gateway = IN6ADDR_ANY_INIT;
-static struct rtnl_data *get_rtnl_data(struct connman_iface *iface)
-{
- if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
- return NULL;
+ extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+ __connman_ipconfig_delroute(index, family, scope, dststr,
+ gatewaystr);
+
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
+ !(scope == RT_SCOPE_LINK &&
+ IN6_IS_ADDR_UNSPECIFIED(&dst)))
+ return;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+ return;
+ } else
+ 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->delgateway)
+ rtnl->delgateway(index, gatewaystr);
+ }
}
-static inline void print_inet(struct rtattr *attr, const char *name, int family)
+static inline void print_ether(struct rtattr *attr, const char *name)
{
- if (family == AF_INET) {
+ int len = (int) RTA_PAYLOAD(attr);
+
+ if (len == ETH_ALEN) {
+ struct ether_addr eth;
+ memcpy(ð, RTA_DATA(attr), ETH_ALEN);
+ print(" attr %s (len %d) %s\n", name, len, ether_ntoa(ð));
+ } else
+ print(" attr %s (len %d)\n", name, len);
+}
+
+static inline void print_inet(struct rtattr *attr, const char *name,
+ unsigned char family)
+{
+ int len = (int) RTA_PAYLOAD(attr);
+
+ if (family == AF_INET && len == sizeof(struct in_addr)) {
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));
+ print(" attr %s (len %d) %s\n", name, len, inet_ntoa(addr));
} else
- DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
+ print(" attr %s (len %d)\n", name, len);
}
-static inline void print_char(struct rtattr *attr, const char *name)
+static inline void print_string(struct rtattr *attr, const char *name)
{
- DBG(" attr %s (len %jd) %s\n", name, RTA_PAYLOAD(attr),
+ print(" 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 %jd) 0x%02x\n", name, RTA_PAYLOAD(attr),
+ print(" attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr),
*((unsigned char *) RTA_DATA(attr)));
}
+static inline void print_integer(struct rtattr *attr, const char *name)
+{
+ print(" attr %s (len %d) %d\n", name, (int) RTA_PAYLOAD(attr),
+ *((int *) RTA_DATA(attr)));
+}
+
static inline void print_attr(struct rtattr *attr, const char *name)
{
- if (name)
- DBG(" attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
+ int len = (int) RTA_PAYLOAD(attr);
+
+ if (name && len > 0)
+ print(" attr %s (len %d)\n", name, len);
else
- DBG(" attr %d (len %jd)\n",
- attr->rta_type, RTA_PAYLOAD(attr));
+ print(" attr %d (len %d)\n", attr->rta_type, len);
}
static void rtnl_link(struct nlmsghdr *hdr)
{
- struct connman_iface *iface;
- struct rtnl_data *data;
struct ifinfomsg *msg;
struct rtattr *attr;
int bytes;
msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
bytes = IFLA_PAYLOAD(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 (!(iface->flags & CONNMAN_IFACE_FLAG_NOCARRIER)) {
- 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_ifup(iface);
- else
- connman_iface_indicate_ifdown(iface);
- }
-
- data->ifi_flags = msg->ifi_flags;
-
- return;
+ print("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)) {
switch (attr->rta_type) {
case IFLA_ADDRESS:
- print_attr(attr, "address");
+ print_ether(attr, "address");
break;
case IFLA_BROADCAST:
- print_attr(attr, "broadcast");
+ print_ether(attr, "broadcast");
break;
case IFLA_IFNAME:
- print_char(attr, "ifname");
+ print_string(attr, "ifname");
break;
case IFLA_MTU:
- print_attr(attr, "mtu");
+ print_integer(attr, "mtu");
break;
case IFLA_LINK:
print_attr(attr, "link");
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");
break;
case IFLA_TXQLEN:
- print_attr(attr, "txqlen");
+ print_integer(attr, "txqlen");
break;
case IFLA_MAP:
print_attr(attr, "map");
}
}
+static void rtnl_newlink(struct nlmsghdr *hdr)
+{
+ struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+ rtnl_link(hdr);
+
+ process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+ msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_dellink(struct nlmsghdr *hdr)
+{
+ struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+ rtnl_link(hdr);
+
+ process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+ msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
static void rtnl_addr(struct nlmsghdr *hdr)
{
- struct connman_iface *iface;
- struct rtnl_data *data;
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);
-
- iface = __connman_iface_find(msg->ifa_index);
- if (iface == NULL)
- return;
-
- data = get_rtnl_data(iface);
- if (data == NULL)
- return;
+ print("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)) {
print_inet(attr, "local", msg->ifa_family);
break;
case IFA_LABEL:
- print_char(attr, "label");
+ print_string(attr, "label");
break;
case IFA_BROADCAST:
print_inet(attr, "broadcast", msg->ifa_family);
}
}
+static void rtnl_newaddr(struct nlmsghdr *hdr)
+{
+ struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+
+ rtnl_addr(hdr);
+
+ process_newaddr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
+ msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_deladdr(struct nlmsghdr *hdr)
+{
+ struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+
+ rtnl_addr(hdr);
+
+ process_deladdr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
+ msg, IFA_PAYLOAD(hdr));
+}
+
static void rtnl_route(struct nlmsghdr *hdr)
{
struct rtmsg *msg;
msg = (struct rtmsg *) NLMSG_DATA(hdr);
bytes = RTM_PAYLOAD(hdr);
- DBG("rtm_family %d rtm_flags 0x%04x", msg->rtm_family, msg->rtm_flags);
+ print("rtm_family %d rtm_table %d rtm_protocol %d",
+ msg->rtm_family, msg->rtm_table, msg->rtm_protocol);
+ print("rtm_scope %d rtm_type %d rtm_flags 0x%04x",
+ msg->rtm_scope, msg->rtm_type, msg->rtm_flags);
for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
attr = RTA_NEXT(attr, bytes)) {
print_inet(attr, "src", msg->rtm_family);
break;
case RTA_IIF:
- print_char(attr, "iif");
+ print_string(attr, "iif");
break;
case RTA_OIF:
- print_attr(attr, "oif");
+ print_integer(attr, "oif");
break;
case RTA_GATEWAY:
print_inet(attr, "gateway", msg->rtm_family);
print_attr(attr, "metrics");
break;
case RTA_TABLE:
- print_attr(attr, "table");
+ print_integer(attr, "table");
break;
default:
print_attr(attr, NULL);
}
}
+static connman_bool_t is_route_rtmsg(struct rtmsg *msg)
+{
+
+ if (msg->rtm_table != RT_TABLE_MAIN)
+ return FALSE;
+
+ if (msg->rtm_protocol != RTPROT_BOOT &&
+ msg->rtm_protocol != RTPROT_KERNEL)
+ return FALSE;
+
+ if (msg->rtm_type != RTN_UNICAST)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void rtnl_newroute(struct nlmsghdr *hdr)
+{
+ struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+ rtnl_route(hdr);
+
+ if (is_route_rtmsg(msg))
+ process_newroute(msg->rtm_family, msg->rtm_scope,
+ msg, RTM_PAYLOAD(hdr));
+}
+
+static void rtnl_delroute(struct nlmsghdr *hdr)
+{
+ struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+ rtnl_route(hdr);
+
+ if (is_route_rtmsg(msg))
+ process_delroute(msg->rtm_family, msg->rtm_scope,
+ msg, RTM_PAYLOAD(hdr));
+}
+
+static void *rtnl_nd_opt_rdnss(struct nd_opt_hdr *opt, guint32 *lifetime,
+ int *nr_servers)
+{
+ guint32 *optint = (void *)opt;
+
+ if (opt->nd_opt_len < 3)
+ return NULL;
+
+ if (*lifetime > ntohl(optint[1]))
+ *lifetime = ntohl(optint[1]);
+
+ /* nd_opt_len is in units of 8 bytes. The header is 1 unit (8 bytes)
+ and each address is another 2 units (16 bytes).
+ So the number of addresses (given rounding) is nd_opt_len/2 */
+ *nr_servers = opt->nd_opt_len / 2;
+
+ /* And they start 8 bytes into the packet, or two guint32s in. */
+ return optint + 2;
+}
+
+static const char **rtnl_nd_opt_dnssl(struct nd_opt_hdr *opt, guint32 *lifetime)
+{
+ const char **domains = NULL;
+ guint32 *optint = (void *)opt;
+ unsigned char *optc = (void *)&optint[2];
+ int data_len = (opt->nd_opt_len * 8) - 8;
+ int nr_domains = 0;
+ int i, tmp;
+
+ if (*lifetime > ntohl(optint[1]))
+ *lifetime = ntohl(optint[1]);
+
+ /* Turn it into normal strings by converting the length bytes into '.',
+ and count how many search domains there are while we're at it. */
+ i = 0;
+ while (i < data_len) {
+ if (optc[i] > 0x3f) {
+ DBG("DNSSL contains compressed elements in violation of RFC6106");
+ return NULL;
+ }
+
+ if (optc[i] == 0) {
+ nr_domains++;
+ i++;
+ /* Check for double zero */
+ if (i < data_len && optc[i] == 0)
+ break;
+ continue;
+ }
+
+ tmp = i;
+ i += optc[i] + 1;
+
+ if (i >= data_len) {
+ DBG("DNSSL data overflows option length");
+ return NULL;
+ }
+
+ optc[tmp] = '.';
+ }
+
+ domains = g_try_new0(const char *, nr_domains + 1);
+ if (!domains)
+ return NULL;
+
+ /* Now point to the normal strings, missing out the leading '.' that
+ each of them will have now. */
+ for (i = 0; i < nr_domains; i++) {
+ domains[i] = (char *)optc + 1;
+ optc += strlen((char *)optc) + 1;
+ }
+
+ return domains;
+}
+
+static void rtnl_newnduseropt(struct nlmsghdr *hdr)
+{
+ struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(hdr);
+ struct nd_opt_hdr *opt = (void *)&msg[1];
+ guint32 lifetime = -1;
+ const char **domains = NULL;
+ struct in6_addr *servers = NULL;
+ int nr_servers = 0;
+ int msglen = msg->nduseropt_opts_len;
+ char *interface;
+
+ DBG("family %02x index %x len %04x type %02x code %02x",
+ msg->nduseropt_family, msg->nduseropt_ifindex,
+ msg->nduseropt_opts_len, msg->nduseropt_icmp_type,
+ msg->nduseropt_icmp_code);
+
+ if (msg->nduseropt_family != AF_INET6 ||
+ msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+ msg->nduseropt_icmp_code != 0)
+ return;
+
+ interface = connman_inet_ifname(msg->nduseropt_ifindex);
+ if (!interface)
+ return;
+
+ for (opt = (void *)&msg[1];
+ msglen >= 2 && msglen >= opt->nd_opt_len && opt->nd_opt_len;
+ msglen -= opt->nd_opt_len,
+ opt = ((void *)opt) + opt->nd_opt_len*8) {
+
+ DBG("nd opt type %d len %d\n",
+ opt->nd_opt_type, opt->nd_opt_len);
+
+ if (opt->nd_opt_type == 25)
+ servers = rtnl_nd_opt_rdnss(opt, &lifetime,
+ &nr_servers);
+ else if (opt->nd_opt_type == 31)
+ domains = rtnl_nd_opt_dnssl(opt, &lifetime);
+ }
+
+ if (nr_servers) {
+ int i, j;
+ char buf[40];
+
+ for (i = 0; i < nr_servers; i++) {
+ if (!inet_ntop(AF_INET6, servers + i, buf, sizeof(buf)))
+ continue;
+
+ if (domains == NULL || domains[0] == NULL) {
+ connman_resolver_append_lifetime(interface,
+ NULL, buf, lifetime);
+ continue;
+ }
+
+ for (j = 0; domains[j]; j++)
+ connman_resolver_append_lifetime(interface,
+ domains[j],
+ buf, lifetime);
+ }
+ }
+ g_free(domains);
+ g_free(interface);
+}
+
+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";
+ case RTM_NEWNDUSEROPT:
+ return "NEWNDUSEROPT";
+ 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)
{
DBG("buf %p len %zd", buf, 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);
+ rtnl_newaddr(hdr);
break;
case RTM_DELADDR:
- DBG("DELADDR");
- rtnl_addr(hdr);
+ rtnl_deladdr(hdr);
break;
case RTM_NEWROUTE:
- DBG("NEWROUTE");
- rtnl_route(hdr);
+ rtnl_newroute(hdr);
break;
case RTM_DELROUTE:
- DBG("DELROUTE");
- rtnl_route(hdr);
+ rtnl_delroute(hdr);
break;
- default:
- DBG("type %d", hdr->nlmsg_type);
+ case RTM_NEWNDUSEROPT:
+ rtnl_newnduseropt(hdr);
break;
}
static gboolean netlink_event(GIOChannel *chan,
GIOCondition cond, gpointer data)
{
- unsigned char buf[256];
+ unsigned char buf[4096];
gsize len;
- GIOError err;
+ GIOStatus status;
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
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;
+ status = g_io_channel_read_chars(chan, (gchar *) buf,
+ sizeof(buf), &len, NULL);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ break;
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+ default:
return FALSE;
}
return TRUE;
}
-static GIOChannel *channel = NULL;
+static int send_getlink(void)
+{
+ struct rtnl_request *req;
+
+ DBG("");
-int __connman_rtnl_send(const void *buf, size_t len)
+ 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);
+}
+
+static int send_getaddr(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_GETADDR;
+ 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);
+}
+
+static int 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);
+}
+
+static gboolean update_timeout_cb(gpointer user_data)
+{
+ __connman_rtnl_request_update();
+
+ return TRUE;
+}
+
+static void update_interval_callback(guint min)
+{
+ if (update_timeout > 0)
+ g_source_remove(update_timeout);
+
+ if (min < G_MAXUINT) {
+ update_interval = min;
+ update_timeout = g_timeout_add_seconds(update_interval,
+ update_timeout_cb, NULL);
+ } else {
+ update_timeout = 0;
+ update_interval = G_MAXUINT;
+ }
+}
+
+static gint compare_interval(gconstpointer a, gconstpointer b)
+{
+ guint val_a = GPOINTER_TO_UINT(a);
+ guint val_b = GPOINTER_TO_UINT(b);
+
+ return val_a - val_b;
+}
+
+unsigned int __connman_rtnl_update_interval_add(unsigned int interval)
+{
+ guint min;
+
+ if (interval == 0)
+ return 0;
+
+ update_list = g_slist_insert_sorted(update_list,
+ GUINT_TO_POINTER(interval), compare_interval);
+
+ min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
+ if (min < update_interval) {
+ update_interval_callback(min);
+ __connman_rtnl_request_update();
+ }
+
+ return update_interval;
+}
+
+unsigned int __connman_rtnl_update_interval_remove(unsigned int interval)
+{
+ guint min = G_MAXUINT;
+
+ if (interval == 0)
+ return 0;
+
+ update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval));
+
+ if (update_list != NULL)
+ min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
- return sendto(sk, buf, len, 0,
- (struct sockaddr *) &addr, sizeof(addr));
+ if (min > update_interval)
+ update_interval_callback(min);
+
+ return min;
+}
+
+int __connman_rtnl_request_update(void)
+{
+ return send_getlink();
}
int __connman_rtnl_init(void)
DBG("");
+ interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, free_interface);
+
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;
- //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
- //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
+ addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+ RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
+ (1<<(RTNLGRP_ND_USEROPT-1));
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_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ 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_start(void)
+{
+ DBG("");
+
+ send_getlink();
+ send_getaddr();
+ send_getroute();
+}
+
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;
+
+ g_slist_free(update_list);
+ update_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;
+
+ g_hash_table_destroy(interface_list);
}