#include <unistd.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/netlink.h>
#include <connman/driver.h>
#include <connman/log.h>
+struct ethernet_data {
+ int index;
+ short flags;
+};
+
static GStaticMutex ethernet_mutex = G_STATIC_MUTEX_INIT;
static GSList *ethernet_list = NULL;
-static GStaticMutex element_mutex = G_STATIC_MUTEX_INIT;
-static GSList *element_list = NULL;
-
static void create_element(struct connman_element *parent,
enum connman_element_type type)
{
DBG("parent %p name %s", parent, parent->name);
- element = connman_element_create();
+ element = connman_element_create(NULL);
+ if (element == NULL)
+ return;
element->type = type;
- element->netdev.index = parent->netdev.index;
- element->netdev.name = g_strdup(parent->netdev.name);
-
- g_static_mutex_lock(&element_mutex);
- element_list = g_slist_append(element_list, element);
- g_static_mutex_unlock(&element_mutex);
+ element->index = parent->index;
connman_element_register(element, parent);
}
-static void remove_elements(struct connman_element *parent)
-{
- GSList *list = element_list;
-
- DBG("parent %p name %s", parent, parent->name);
-
- g_static_mutex_lock(&element_mutex);
-
- while (list) {
- GSList *next = list->next;
- struct connman_element *element = list->data;
-
- if (element->netdev.index != parent->netdev.index) {
- list = next;
- continue;
- }
-
- element_list = g_slist_delete_link(element_list, list);
-
- connman_element_unregister(element);
-
- connman_element_unref(element);
-
- list = next;
- }
-
- g_static_mutex_unlock(&element_mutex);
-}
-
static void rtnl_link(struct nlmsghdr *hdr, const char *type)
{
GSList *list;
for (list = ethernet_list; list; list = list->next) {
struct connman_element *element = list->data;
+ struct ethernet_data *ethernet;
- if (element->type != CONNMAN_ELEMENT_TYPE_DEVICE)
+ ethernet = connman_element_get_data(element);
+ if (ethernet == NULL)
continue;
- if (element->netdev.index != msg->ifi_index)
+ if (ethernet->index != msg->ifi_index)
continue;
- if ((element->netdev.flags & IFF_RUNNING) ==
- (msg->ifi_flags & IFF_RUNNING))
+ if ((ethernet->flags & IFF_RUNNING) ==
+ (msg->ifi_flags & IFF_RUNNING))
continue;
- element->netdev.flags = msg->ifi_flags;
+ ethernet->flags = msg->ifi_flags;
- if (msg->ifi_flags & IFF_RUNNING) {
+ if (ethernet->flags & IFF_RUNNING) {
DBG("carrier on");
create_element(element, CONNMAN_ELEMENT_TYPE_DHCP);
} else {
DBG("carrier off");
- remove_elements(element);
+ connman_element_unregister_children(element);
}
}
return TRUE;
}
-static GIOChannel *channel = NULL;
+static GIOChannel *channel;
static int rtnl_request(void)
{
(struct sockaddr *) &addr, sizeof(addr));
}
+static int iface_up(struct ethernet_data *ethernet)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ DBG("index %d flags %d", ethernet->index, ethernet->flags);
+
+ sk = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return -errno;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = ethernet->index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ if (ifr.ifr_flags & IFF_UP) {
+ err = -EALREADY;
+ goto done;
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ err = 0;
+
+done:
+ close(sk);
+
+ return err;
+}
+
+static int iface_down(struct ethernet_data *ethernet)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ DBG("index %d flags %d", ethernet->index, ethernet->flags);
+
+ sk = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return -errno;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = ethernet->index;
+
+ if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+ err = -errno;
+ goto done;
+ }
+
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ err = -EALREADY;
+ goto done;
+ }
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
+ err = -errno;
+ else
+ err = 0;
+
+done:
+ close(sk);
+
+ return err;
+}
+
static int ethernet_probe(struct connman_element *element)
{
+ struct ethernet_data *ethernet;
+
DBG("element %p name %s", element, element->name);
+ ethernet = g_try_new0(struct ethernet_data, 1);
+ if (ethernet == NULL)
+ return -ENOMEM;
+
g_static_mutex_lock(ðernet_mutex);
ethernet_list = g_slist_append(ethernet_list, element);
g_static_mutex_unlock(ðernet_mutex);
+ connman_element_set_data(element, ethernet);
+
+ ethernet->index = element->index;
+
+ iface_up(ethernet);
+
rtnl_request();
return 0;
static void ethernet_remove(struct connman_element *element)
{
+ struct ethernet_data *ethernet = connman_element_get_data(element);
+
DBG("element %p name %s", element, element->name);
- remove_elements(element);
+ connman_element_set_data(element, NULL);
+
+ iface_down(ethernet);
g_static_mutex_lock(ðernet_mutex);
ethernet_list = g_slist_remove(ethernet_list, element);
g_static_mutex_unlock(ðernet_mutex);
+
+ g_free(ethernet);
}
static struct connman_driver ethernet_driver = {
g_io_channel_shutdown(channel, TRUE, NULL);
g_io_channel_unref(channel);
-
- channel = NULL;
}
static int ethernet_init(void)