*
* Connection Manager
*
- * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
+ * Copyright (C) 2007-2013 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>
#define ARPHDR_PHONET_PIPE (821)
#endif
-#define print(arg...) do { } while (0)
+#if defined TIZEN_EXT
+#ifndef ARPHDR_RMNET
+#define ARPHDR_RMNET (530)
+#endif
+#endif
+
+#define print(arg...) do { if (0) connman_info(arg); } while (0)
//#define print(arg...) connman_info(arg)
struct watch_data {
struct interface_data {
int index;
- char *name;
char *ident;
enum connman_service_type service_type;
enum connman_device_type device_type;
struct interface_data *interface = data;
__connman_technology_remove_interface(interface->service_type,
- interface->index, interface->name, interface->ident);
+ interface->index, interface->ident);
g_free(interface->ident);
- g_free(interface->name);
g_free(interface);
}
-static connman_bool_t ether_blacklisted(const char *name)
+static bool ether_blacklisted(const char *name)
{
- if (name == NULL)
- return TRUE;
+ if (!name)
+ return true;
- /* virtual interface from VMware */
- if (g_str_has_prefix(name, "vmnet") == TRUE)
- return TRUE;
+ if (__connman_device_isfiltered(name))
+ return true;
- /* virtual interface from VirtualBox */
- if (g_str_has_prefix(name, "vboxnet") == TRUE)
- return TRUE;
+ return false;
+}
- /* virtual interface from Virtual Machine Manager */
- if (g_str_has_prefix(name, "virbr") == TRUE)
- return TRUE;
+#if !defined TIZEN_EXT
+static bool wext_interface(char *ifname)
+{
+ struct iwreq wrq;
+ int fd, err;
+
+ fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return false;
+
+ memset(&wrq, 0, sizeof(wrq));
+ strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name) - 1);
+
+ err = ioctl(fd, SIOCGIWNAME, &wrq);
+
+ close(fd);
+
+ if (err < 0)
+ return false;
- return FALSE;
+ return true;
}
+#endif
+
+#if defined TIZEN_EXT
+static bool __connman_rtnl_is_cellular_device(const char *name)
+{
+ char **pattern;
+ char **cellular_interfaces;
+
+ cellular_interfaces =
+ connman_setting_get_string_list(
+ "NetworkCellularInterfaceList");
+ if (!cellular_interfaces)
+ return false;
+
+ for (pattern = cellular_interfaces; *pattern; pattern++) {
+ if (g_str_has_prefix(name, *pattern)) {
+ DBG("Cellular interface: %s", name);
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif
static void read_uevent(struct interface_data *interface)
{
- char *filename, line[128];
+ char *filename, *name, line[128];
+ bool found_devtype;
FILE *f;
- if (ether_blacklisted(interface->name) == TRUE) {
+ name = connman_inet_ifname(interface->index);
+
+#if defined TIZEN_EXT
+ if (__connman_rtnl_is_cellular_device(name)) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
+ interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
+ g_free(name);
+ return;
+ }
+#endif
+
+ if (ether_blacklisted(name)) {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+ goto out;
} 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);
+ filename = g_strdup_printf("/sys/class/net/%s/uevent", name);
f = fopen(filename, "re");
g_free(filename);
- if (f == NULL)
- return;
+ if (!f) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+ interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+ goto out;
+ }
+ found_devtype = false;
while (fgets(line, sizeof(line), f)) {
char *pos;
pos = strchr(line, '\n');
- if (pos == NULL)
+ if (!pos)
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, "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 if (strcmp(line + 8, "vlan") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+ } else if (strcmp(line + 8, "bond") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
} else {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
}
fclose(f);
+
+ if (found_devtype)
+ goto out;
+
+#if !defined TIZEN_EXT
+ /* TIZEN does not use old wext interface */
+ /* We haven't got a DEVTYPE, let's check if it's a wireless device */
+ if (wext_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", name);
+ }
+#endif
+
+out:
+ g_free(name);
}
enum connman_device_type __connman_rtnl_get_device_type(int index)
interface = g_hash_table_lookup(interface_list,
GINT_TO_POINTER(index));
- if (interface == NULL)
+ if (!interface)
return CONNMAN_DEVICE_TYPE_UNKNOWN;
return interface->device_type;
struct watch_data *watch;
watch = g_try_new0(struct watch_data, 1);
- if (watch == NULL)
+ if (!watch)
return 0;
watch->id = ++watch_id;
DBG("id %d", watch->id);
if (callback) {
- unsigned int flags = __connman_ipconfig_get_flags(index);
+ unsigned int flags = __connman_ipconfig_get_flags_from_index(index);
if (flags > 0)
callback(flags, 0, user_data);
struct connman_rtnl *rtnl = user_data;
if (rtnl->newlink) {
- unsigned short type = __connman_ipconfig_get_type(index);
- unsigned int flags = __connman_ipconfig_get_flags(index);
+ 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(index);
+ const char *gateway =
+ __connman_ipconfig_get_gateway_from_index(index,
+ CONNMAN_IPCONFIG_TYPE_ALL);
- if (gateway != NULL)
+ if (gateway)
rtnl->newgateway(index, gateway);
}
}
return "";
}
-static void extract_link(struct ifinfomsg *msg, int bytes,
+static bool 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 rtnl_link_stats *stats)
{
struct rtattr *attr;
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case IFLA_ADDRESS:
- if (address != NULL)
+ if (address)
memcpy(address, RTA_DATA(attr), ETH_ALEN);
break;
case IFLA_IFNAME:
- if (ifname != NULL)
+ if (ifname)
*ifname = RTA_DATA(attr);
break;
case IFLA_MTU:
- if (mtu != NULL)
+ if (mtu)
*mtu = *((unsigned int *) RTA_DATA(attr));
break;
case IFLA_STATS:
- if (stats != NULL)
+ if (stats)
memcpy(stats, RTA_DATA(attr),
sizeof(struct rtnl_link_stats));
break;
case IFLA_OPERSTATE:
- if (operstate != NULL)
+ if (operstate)
*operstate = *((unsigned char *) RTA_DATA(attr));
break;
case IFLA_LINKMODE:
break;
+ case IFLA_WIRELESS:
+ return false;
}
}
+
+ return true;
}
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);
+ if (!extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats))
+ return;
+
+#if defined TIZEN_EXT_WIFI_MESH
+ /* Do not accept Wi-Fi Mesh interface */
+ if (g_strrstr(ifname, "mesh") != NULL) {
+ DBG("Newlink event for Wi-Fi Mesh interface ignored");
+ return;
+ }
+
+ /* Do not accept Wi-Fi WLAN1 interface "dedicated for softAP */
+ if (!g_strcmp0(ifname, "wlan1")) {
+ DBG("Newlink event for Wi-Fi WLAN1 interface ignored");
+ return;
+ }
+#endif
+
+#if defined TIZEN_EXT
+ /* Do not accept Wi-Fi P2P interface */
+ if (g_strrstr(ifname, "p2p") != NULL) {
+ DBG("Newlink event for Wi-Fi P2P interface ignored");
+ return;
+ }
+#endif
snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
address.ether_addr_octet[0],
address.ether_addr_octet[4],
address.ether_addr_octet[5]);
+ if (flags & IFF_SLAVE) {
+ connman_info("%s {newlink} ignoring slave, index %d address %s",
+ ifname, index, str);
+ return;
+ }
+
+#ifdef TIZEN_EXT
+ if (TIZEN_TV_EXT && g_strcmp0(ident, "eeeeeeeeeeee") == 0) {
+ DBG("Newlink event with Dummy MAC. Ignored!");
+ return;
+ }
+#endif
+
switch (type) {
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK:
case ARPHDR_PHONET_PIPE:
+ case ARPHRD_PPP:
case ARPHRD_NONE:
+#if defined TIZEN_EXT
+/*
+ * Description: ARPHDR_RMNET for QC modem using QMI
+ */
+ case ARPHDR_RMNET:
+#endif
__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);
+ 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));
- if (g_hash_table_lookup(interface_list,
- GINT_TO_POINTER(index)) == NULL) {
- struct interface_data *interface;
-
+ interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index));
+ if (!interface) {
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,
if (type == ARPHRD_ETHER)
read_uevent(interface);
+#if defined TIZEN_EXT
+ if (type == ARPHRD_PPP || type == ARPHDR_RMNET)
+ read_uevent(interface);
+
+ } else if (g_strcmp0(interface->ident, ident) != 0) {
+ /* If an original address is built-in physical device,
+ * it's hardly get an address at a initial creation
+ */
+ __connman_technology_remove_interface(interface->service_type,
+ interface->index, interface->ident);
+
+ g_free(interface->ident);
+ interface->ident = g_strdup(ident);
__connman_technology_add_interface(interface->service_type,
- interface->index, interface->name, interface->ident);
- }
+ interface->index, interface->ident);
+
+ interface = NULL;
+#endif
+ } else if (type == ARPHRD_ETHER && interface->device_type == CONNMAN_DEVICE_TYPE_UNKNOWN)
+ read_uevent(interface);
+ else
+ interface = NULL;
for (list = rtnl_list; list; list = list->next) {
struct connman_rtnl *rtnl = list->data;
rtnl->newlink(type, index, flags, change);
}
- for (list = watch_list; list; list = list->next) {
- struct watch_data *watch = list->data;
+ /*
+ * The interface needs to be added after the newlink call.
+ * The newlink will create the technology when needed and
+ * __connman_technology_add_interface() expects the
+ * technology to be there already.
+ */
+ if (interface)
+ __connman_technology_add_interface(interface->service_type,
+ interface->index, interface->ident);
- if (watch->index != index)
- continue;
+ list = watch_list;
+ while (list) {
+ GSList *next = list->next;
+ struct watch_data *watch = list->data;
- if (watch->newlink)
+ if (watch->index == index && watch->newlink)
watch->newlink(flags, change, watch->user_data);
+
+ list = next;
}
}
GSList *list;
memset(&stats, 0, sizeof(stats));
- extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats);
+ if (!extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats))
+ return;
if (operstate != 0xff)
connman_info("%s {dellink} index %d operstate %u <%s>",
switch (type) {
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK:
+ case ARPHDR_PHONET_PIPE:
+ case ARPHRD_PPP:
case ARPHRD_NONE:
+#if defined TIZEN_EXT
+ /*
+ * Description: SLP requires ARPHRD_PPP for PPP type device
+ * ARPHDR_RMNET for QC modem using QMI
+ */
+ case ARPHDR_RMNET:
+#endif
__connman_ipconfig_dellink(index, &stats);
break;
}
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case IFA_ADDRESS:
- if (address != NULL)
+ if (address)
*address = *((struct in_addr *) RTA_DATA(attr));
break;
case IFA_LOCAL:
- if (local != NULL)
+ if (local)
*local = *((struct in_addr *) RTA_DATA(attr));
break;
case IFA_BROADCAST:
- if (broadcast != NULL)
+ if (broadcast)
*broadcast = *((struct in_addr *) RTA_DATA(attr));
break;
case IFA_LABEL:
- if (label != NULL)
+ if (label)
*label = RTA_DATA(attr);
break;
}
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case IFA_ADDRESS:
- if (addr != NULL)
+ if (addr)
*addr = *((struct in6_addr *) RTA_DATA(attr));
break;
case IFA_LOCAL:
- if (local != NULL)
+ if (local)
*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)
{
+ struct in_addr ipv4_addr = { INADDR_ANY };
+ struct in6_addr ipv6_address, ipv6_local;
const char *label = NULL;
void *src;
char ip_string[INET6_ADDRSTRLEN];
- if (family != AF_INET && family != AF_INET6)
- return;
-
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)
+ if (!inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN))
return;
- __connman_ipconfig_newaddr(index, family, label,
- prefixlen, ip_string);
+ if (__connman_ipconfig_newaddr(index, family, label,
+ prefixlen, ip_string) >= 0) {
+ if (family == AF_INET6) {
+ /*
+ * Re-create RDNSS configured servers if there
+ * are any for this interface. This is done
+ * because we might have now properly
+ * configured interface with proper
+ * autoconfigured address.
+ */
+ __connman_resolver_redo_servers(index);
+ }
+ }
}
static void process_deladdr(unsigned char family, unsigned char prefixlen,
int index, struct ifaddrmsg *msg, int bytes)
{
+ struct in_addr ipv4_addr = { INADDR_ANY };
+ struct in6_addr ipv6_address, ipv6_local;
const char *label = NULL;
void *src;
char ip_string[INET6_ADDRSTRLEN];
- if (family != AF_INET && family != AF_INET6)
- return;
-
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)
+ if (!inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN))
return;
__connman_ipconfig_deladdr(index, family, label,
prefixlen, ip_string);
}
-static void extract_route(struct rtmsg *msg, int bytes, int *index,
+static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index,
struct in_addr *dst,
struct in_addr *gateway)
{
attr = RTA_NEXT(attr, bytes)) {
switch (attr->rta_type) {
case RTA_DST:
- if (dst != NULL)
+ if (dst)
*dst = *((struct in_addr *) RTA_DATA(attr));
break;
case RTA_GATEWAY:
- if (gateway != NULL)
+ if (gateway)
*gateway = *((struct in_addr *) RTA_DATA(attr));
break;
case RTA_OIF:
- if (index != NULL)
+ if (index)
+ *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)
+ *dst = *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ case RTA_GATEWAY:
+ if (gateway)
+ *gateway =
+ *((struct in6_addr *) RTA_DATA(attr));
+ break;
+ case RTA_OIF:
+ if (index)
*index = *((int *) RTA_DATA(attr));
break;
}
struct rtmsg *msg, int bytes)
{
GSList *list;
- struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
- char dststr[16], gatewaystr[16];
+ char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
int index = -1;
- if (family != AF_INET)
- return;
+ if (family == AF_INET) {
+ struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
- extract_route(msg, bytes, &index, &dst, &gateway);
+ extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
- inet_ntop(family, &dst, dststr, sizeof(dststr));
- inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
- __connman_ipconfig_newroute(index, family, scope, dststr, gatewaystr);
+ __connman_ipconfig_newroute(index, family, scope, dststr,
+ gatewaystr);
- /* skip host specific routes */
- if (scope != RT_SCOPE_UNIVERSE &&
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
!(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
- return;
+ 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 (dst.s_addr != INADDR_ANY)
+ if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+ return;
+ } else
return;
for (list = rtnl_list; list; list = list->next) {
struct rtmsg *msg, int bytes)
{
GSList *list;
- struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
- char dststr[16], gatewaystr[16];
+ char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
int index = -1;
- if (family != AF_INET)
- return;
+ if (family == AF_INET) {
+ struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
- extract_route(msg, bytes, &index, &dst, &gateway);
+ extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
- inet_ntop(family, &dst, dststr, sizeof(dststr));
- inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+ inet_ntop(family, &dst, dststr, sizeof(dststr));
+ inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
- __connman_ipconfig_delroute(index, family, scope, dststr, gatewaystr);
+ __connman_ipconfig_delroute(index, family, scope, dststr,
+ gatewaystr);
- /* skip host specific routes */
- if (scope != RT_SCOPE_UNIVERSE &&
+ /* skip host specific routes */
+ if (scope != RT_SCOPE_UNIVERSE &&
!(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
- return;
+ 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_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 (dst.s_addr != INADDR_ANY)
+ if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+ return;
+ } else
return;
for (list = rtnl_list; list; list = list->next) {
rtnl_link(hdr);
+ if (hdr->nlmsg_type == IFLA_WIRELESS)
+ connman_warn_once("Obsolete WEXT WiFi driver detected");
+
process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
msg->ifi_change, msg, IFA_PAYLOAD(hdr));
}
}
}
-static connman_bool_t is_route_rtmsg(struct rtmsg *msg)
+static bool is_route_rtmsg(struct rtmsg *msg)
{
+ if (msg->rtm_flags & RTM_F_CLONED)
+ return false;
if (msg->rtm_table != RT_TABLE_MAIN)
- return FALSE;
+ return false;
if (msg->rtm_protocol != RTPROT_BOOT &&
msg->rtm_protocol != RTPROT_KERNEL)
- return FALSE;
+ return false;
if (msg->rtm_type != RTN_UNICAST)
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
static void rtnl_newroute(struct nlmsghdr *hdr)
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;
+ guint32 lifetime = -1;
+ const char **domains = NULL;
+ struct in6_addr *servers = NULL;
+ int i, nr_servers = 0;
+ int msglen = msg->nduseropt_opts_len;
+ int index;
+
+ DBG("family %d index %d len %d type %d code %d",
+ 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;
+
+ index = msg->nduseropt_ifindex;
+ if (index < 0)
+ return;
+
+#if defined TIZEN_EXT
+ struct connman_service *service;
+ enum connman_service_state state;
+ enum connman_dnsconfig_method ipv6_dns_method;
+
+ service = __connman_service_lookup_from_index(index);
+ if (!service) {
+ DBG("Invalid service");
+ return;
+ }
+
+ DBG("service: %p index: %d\n", service, index);
+
+ if (connman_setting_get_bool("SingleConnectedTechnology") == TRUE) {
+ state = __connman_service_ipconfig_get_state(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+ if (state != CONNMAN_SERVICE_STATE_ASSOCIATION &&
+ state != CONNMAN_SERVICE_STATE_CONFIGURATION &&
+ state != CONNMAN_SERVICE_STATE_READY &&
+ state != CONNMAN_SERVICE_STATE_ONLINE) {
+ DBG("Service state[%d] is not connecting/connected", state);
+ return;
+ }
+ }
+
+ ipv6_dns_method = connman_service_get_ipv6_dns_method(service);
+ if (ipv6_dns_method != CONNMAN_DNSCONFIG_METHOD_DHCP) {
+ DBG("IPv6 DNS method is not Auto ignore RA!!! [DNS method: %d]", ipv6_dns_method);
+ return;
+ }
+#endif
+
+ for (opt = (void *)&msg[1];
+ msglen > 0;
+ msglen -= opt->nd_opt_len * 8,
+ opt = ((void *)opt) + opt->nd_opt_len*8) {
+
+ DBG("remaining %d nd opt type %d len %d\n",
+ msglen, opt->nd_opt_type, opt->nd_opt_len);
+
+ if (opt->nd_opt_type == 25) { /* ND_OPT_RDNSS */
+ char buf[40];
+#if defined TIZEN_EXT
+ struct connman_service *service;
+
+ service = __connman_service_lookup_from_index(index);
+ DBG("service: %p\n",service);
+#endif
+ servers = rtnl_nd_opt_rdnss(opt, &lifetime,
+ &nr_servers);
+ for (i = 0; i < nr_servers; i++) {
+ if (!inet_ntop(AF_INET6, servers + i, buf,
+ sizeof(buf)))
+ continue;
+
+#if defined TIZEN_EXT
+ __connman_service_nameserver_remove(service,
+ buf, false,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+ __connman_service_nameserver_append(service,
+ buf, false,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+#endif
+ connman_resolver_append_lifetime(index,
+ NULL, buf, lifetime);
+ }
+
+ } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */
+ g_free(domains);
+
+ domains = rtnl_nd_opt_dnssl(opt, &lifetime);
+ for (i = 0; domains && domains[i]; i++)
+ connman_resolver_append_lifetime(index,
+ domains[i], NULL, lifetime);
+ }
+ }
+
+ g_free(domains);
+}
+
static const char *type2string(uint16_t type)
{
switch (type) {
return "NEWLINK";
case RTM_DELLINK:
return "DELLINK";
+ case RTM_GETADDR:
+ return "GETADDR";
case RTM_NEWADDR:
return "NEWADDR";
case RTM_DELADDR:
return "NEWROUTE";
case RTM_DELROUTE:
return "DELROUTE";
+ case RTM_NEWNDUSEROPT:
+ return "NEWNDUSEROPT";
default:
return "UNKNOWN";
}
}
static GIOChannel *channel = NULL;
+static guint channel_watch = 0;
struct rtnl_request {
struct nlmsghdr hdr;
DBG("seq %d", seq);
req = find_request(seq);
- if (req != NULL) {
+ if (req) {
request_list = g_slist_remove(request_list, req);
g_free(req);
}
req = g_slist_nth_data(request_list, 0);
- if (req == NULL)
+ if (!req)
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",
+ DBG("%s len %u type %u flags 0x%04x seq %u pid %u",
type2string(hdr->nlmsg_type),
hdr->nlmsg_len, hdr->nlmsg_type,
- hdr->nlmsg_flags, hdr->nlmsg_seq);
+ hdr->nlmsg_flags, hdr->nlmsg_seq,
+ hdr->nlmsg_pid);
switch (hdr->nlmsg_type) {
case NLMSG_NOOP:
case RTM_DELROUTE:
rtnl_delroute(hdr);
break;
+ case RTM_NEWNDUSEROPT:
+ rtnl_newnduseropt(hdr);
+ break;
}
len -= hdr->nlmsg_len;
}
}
-static gboolean netlink_event(GIOChannel *chan,
- GIOCondition cond, gpointer data)
+static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data)
{
unsigned char buf[4096];
- gsize len;
- GIOError err;
-
+ struct sockaddr_nl nladdr;
+ socklen_t addr_len = sizeof(nladdr);
+ ssize_t status;
+ int fd;
+
+#if defined TIZEN_EXT
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT);
+ return FALSE;
+ }
+#else /* TIZEN_EXT */
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
return FALSE;
+#endif /* TIZEN_EXT */
memset(buf, 0, sizeof(buf));
+ memset(&nladdr, 0, sizeof(nladdr));
+
+ fd = g_io_channel_unix_get_fd(chan);
- err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
- if (err) {
- if (err == G_IO_ERROR_AGAIN)
+ status = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *) &nladdr, &addr_len);
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
return TRUE;
+
+#if defined TIZEN_EXT
+ __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT);
+#endif /* TIZEN_EXT */
return FALSE;
}
- rtnl_message(buf, len);
+#if defined TIZEN_EXT
+ if (status == 0) {
+ __connman_rtnl_init(GIO_SOCKET_RETRY_COUNT);
+ return FALSE;
+ }
+#else /* TIZEN_EXT */
+ if (status == 0)
+ return FALSE;
+#endif /* TIZEN_EXT */
+
+ if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+ DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+ return TRUE;
+ }
+
+ rtnl_message(buf, status);
return TRUE;
}
DBG("");
req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (req == NULL)
+ if (!req)
return -ENOMEM;
req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
DBG("");
req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (req == NULL)
+ if (!req)
return -ENOMEM;
req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
DBG("");
req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (req == NULL)
+ if (!req)
return -ENOMEM;
req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval));
- if (update_list != NULL)
+ if (update_list)
min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
if (min > update_interval)
return send_getlink();
}
+#if defined TIZEN_EXT
+int __connman_rtnl_init(int retry_count)
+#else /* TIZEN_EXT */
int __connman_rtnl_init(void)
+#endif /* TIZEN_EXT */
{
struct sockaddr_nl addr;
int sk;
+#if defined TIZEN_EXT
+ if (retry_count < 0)
+ return -1;
+
+ DBG("retry_count %d", retry_count);
+#else /* TIZEN_EXT */
DBG("");
+#endif /* TIZEN_EXT */
interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, free_interface);
- sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sk < 0)
+#if defined TIZEN_EXT
+ return __connman_rtnl_init(retry_count - 1);
+#else /* TIZEN_EXT */
return -1;
+#endif /* TIZEN_EXT */
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
- RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
+ RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
+ (1<<(RTNLGRP_ND_USEROPT-1));
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(sk);
+#if defined TIZEN_EXT
+ return __connman_rtnl_init(retry_count - 1);
+#else /* TIZEN_EXT */
return -1;
+#endif /* TIZEN_EXT */
}
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);
+
+ channel_watch = g_io_add_watch(channel,
+ G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ netlink_event, NULL);
return 0;
}
g_slist_free(request_list);
request_list = NULL;
+ if (channel_watch) {
+ g_source_remove(channel_watch);
+ channel_watch = 0;
+ }
+
g_io_channel_shutdown(channel, TRUE, NULL);
g_io_channel_unref(channel);