+#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 | SOCK_CLOEXEC, 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,
+ CONNMAN_IPCONFIG_TYPE_ALL);
+
+ if (gateway != NULL)
+ rtnl->newgateway(index, gateway);
+ }
+}
+