X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fethernet.c;h=ed4208ad7ec2627423ebb8267d8d6e6a25835e80;hb=dd3cccc5e67548dcc2dd6c6254ed6c97859085d5;hp=0f248bca6dda2f30f24986975458e478a9c92700;hpb=d3073cdb94002354fe6e37e39557402ddfb30561;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/ethernet.c b/plugins/ethernet.c index 0f248bc..ed4208a 100644 --- a/plugins/ethernet.c +++ b/plugins/ethernet.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2008 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 @@ -23,338 +23,430 @@ #include #endif -#include +#include +#include #include #include -#include -#include -#include -#include +#include +#include +#include + +#include +#include +#include + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 +#endif + +#include +#define CONNMAN_API_SUBJECT_TO_CHANGE +#include #include -#include +#include +#include +#include #include +#include -static GStaticMutex ethernet_mutex = G_STATIC_MUTEX_INIT; -static GSList *ethernet_list = NULL; +static bool eth_tethering = false; -static void create_element(struct connman_element *parent, - enum connman_element_type type) -{ - struct connman_element *element; +struct ethernet_data { + int index; + unsigned flags; + unsigned int watch; + struct connman_network *network; +}; - DBG("parent %p name %s", parent, parent->name); - element = connman_element_create(); +static int get_vlan_vid(const char *ifname) +{ + struct vlan_ioctl_args vifr; + int vid; + int sk; - element->type = type; - element->netdev.index = parent->netdev.index; - element->netdev.name = g_strdup(parent->netdev.name); + memset(&vifr, 0, sizeof(vifr)); - connman_element_register(element, parent); -} + sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk < 0) + return -errno; -static void rtnl_link(struct nlmsghdr *hdr, const char *type) -{ - GSList *list; - struct ifinfomsg *msg; - int bytes; + vifr.cmd = GET_VLAN_VID_CMD; + stpncpy(vifr.device1, ifname, sizeof(vifr.device1)); - msg = (struct ifinfomsg *) NLMSG_DATA(hdr); - bytes = IFLA_PAYLOAD(hdr); + if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) + vid = vifr.u.VID; + else + vid = -errno; - DBG("%s ifi_index %d ifi_flags 0x%04x", - type, msg->ifi_index, msg->ifi_flags); + close(sk); - g_static_mutex_lock(ðernet_mutex); + return vid; +} - for (list = ethernet_list; list; list = list->next) { - struct connman_element *element = list->data; +static int get_dsa_port(const char *ifname) +{ + int sk; + int dsaport = -1; + struct ifreq ifr; + struct ethtool_cmd cmd; + struct ethtool_drvinfo drvinfocmd; + struct vlan_ioctl_args vifr; - if (element->type != CONNMAN_ELEMENT_TYPE_DEVICE) - continue; + sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk < 0) + return -errno; - if (element->netdev.index != msg->ifi_index) - continue; + memset(&ifr, 0, sizeof(ifr)); + stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if ((element->netdev.flags & IFF_RUNNING) == - (msg->ifi_flags & IFF_RUNNING)) - continue; + /* check if it is a vlan and get physical interface name*/ + vifr.cmd = GET_VLAN_REALDEV_NAME_CMD; + stpncpy(vifr.device1, ifname, sizeof(vifr.device1)); - element->netdev.flags = msg->ifi_flags; + if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) + stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name)); - if (msg->ifi_flags & IFF_RUNNING) { - DBG("carrier on"); + /* get driver info */ + drvinfocmd.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)&drvinfocmd; - create_element(element, CONNMAN_ELEMENT_TYPE_DHCP); - create_element(element, CONNMAN_ELEMENT_TYPE_ZEROCONF); - } else { - DBG("carrier off"); + if (!ioctl(sk, SIOCETHTOOL, &ifr)) { + if(!strcmp(drvinfocmd.driver, "dsa")) { + /* get dsa port*/ + cmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&cmd; - connman_element_unregister_children(element); + if (!ioctl(sk, SIOCETHTOOL, &ifr)) + dsaport = cmd.phy_address; } } + close(sk); - g_static_mutex_unlock(ðernet_mutex); + return dsaport; } -static gboolean rtnl_event(GIOChannel *chan, GIOCondition cond, gpointer data) +static int eth_network_probe(struct connman_network *network) { - unsigned char buf[1024]; - void *ptr = buf; - gsize len; - GIOError err; + DBG("network %p", network); - if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + return 0; +} - memset(buf, 0, sizeof(buf)); +static void eth_network_remove(struct connman_network *network) +{ + DBG("network %p", network); +} - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); - if (err) { - if (err == G_IO_ERROR_AGAIN) - return TRUE; - return FALSE; - } +static int eth_network_connect(struct connman_network *network) +{ + DBG("network %p", network); - DBG("buf %p len %zd", buf, len); + connman_network_set_connected(network, true); - while (len > 0) { - struct nlmsghdr *hdr = ptr; - struct nlmsgerr *err; + return 0; +} - if (!NLMSG_OK(hdr, len)) - break; +static int eth_network_disconnect(struct connman_network *network) +{ + DBG("network %p", network); - DBG("len %d type %d flags 0x%04x seq %d", - hdr->nlmsg_len, hdr->nlmsg_type, - hdr->nlmsg_flags, hdr->nlmsg_seq); + connman_network_set_connected(network, false); - switch (hdr->nlmsg_type) { - case NLMSG_ERROR: - err = NLMSG_DATA(hdr); - DBG("ERROR %d (%s)", -err->error, - strerror(-err->error)); - break; + return 0; +} - case RTM_NEWLINK: - rtnl_link(hdr, "NEWLINK"); - break; +static struct connman_network_driver eth_network_driver = { + .name = "cable", + .type = CONNMAN_NETWORK_TYPE_ETHERNET, + .probe = eth_network_probe, + .remove = eth_network_remove, + .connect = eth_network_connect, + .disconnect = eth_network_disconnect, +}; - case RTM_DELLINK: - rtnl_link(hdr, "DELLINK"); - break; - } +static void add_network(struct connman_device *device, + struct ethernet_data *ethernet) +{ + struct connman_network *network; + int index; + char *ifname; + + network = connman_network_create("carrier", + CONNMAN_NETWORK_TYPE_ETHERNET); + if (!network) + return; + + index = connman_device_get_index(device); + connman_network_set_index(network, index); + ifname = connman_inet_ifname(index); + if (!ifname) + return; + + connman_network_set_name(network, "Wired"); + + if (connman_device_add_network(device, network) < 0) { + connman_network_unref(network); + g_free(ifname); + return; + } - len -= hdr->nlmsg_len; - ptr += hdr->nlmsg_len; + if (!eth_tethering) { + char group[25] = "cable"; + int vid, dsaport; + + vid = get_vlan_vid(ifname); + dsaport = get_dsa_port(ifname); + + /* + * Prevent service from starting the reconnect + * procedure as we do not want the DHCP client + * to run when tethering. + */ + if((vid >= 0) && (dsaport >= 0)) + snprintf(group, sizeof(group), "p%02x_%03x_cable", dsaport, vid); + else if (vid >= 0) + snprintf(group, sizeof(group), "%03x_cable", vid); + else if (dsaport >= 0) + snprintf(group, sizeof(group), "p%02x_cable", dsaport); + + connman_network_set_group(network, group); } - return TRUE; + ethernet->network = network; + g_free(ifname); } -static GIOChannel *channel = NULL; - -static int rtnl_request(void) +static void remove_network(struct connman_device *device, + struct ethernet_data *ethernet) { - struct { - struct nlmsghdr hdr; - struct rtgenmsg msg; - } req; + if (!ethernet->network) + return; - struct sockaddr_nl addr; - int sk; + connman_device_remove_network(device, ethernet->network); + connman_network_unref(ethernet->network); - DBG(""); - - memset(&req, 0, sizeof(req)); - req.hdr.nlmsg_len = sizeof(req.hdr) + sizeof(req.msg); - 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 = 42; - req.msg.rtgen_family = AF_INET; - - sk = g_io_channel_unix_get_fd(channel); - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - return sendto(sk, &req, sizeof(req), 0, - (struct sockaddr *) &addr, sizeof(addr)); + ethernet->network = NULL; } -static int iface_up(struct connman_element *element) +static void ethernet_newlink(unsigned flags, unsigned change, void *user_data) { - struct ifreq ifr; - int sk, err; - - DBG("element %p", element); - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -errno; + struct connman_device *device = user_data; + struct ethernet_data *ethernet = connman_device_get_data(device); - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = element->netdev.index; + DBG("index %d flags %d change %d", ethernet->index, flags, change); - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - err = -errno; - goto done; + if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) { + if (flags & IFF_UP) { + DBG("power on"); + connman_device_set_powered(device, true); + } else { + DBG("power off"); + connman_device_set_powered(device, false); + } } - if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { - err = -errno; - goto done; + if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) { + if (flags & IFF_LOWER_UP) { + DBG("carrier on"); + add_network(device, ethernet); + } else { + DBG("carrier off"); + remove_network(device, ethernet); + } } - if (ifr.ifr_flags & IFF_UP) { - err = -EALREADY; - goto done; - } + ethernet->flags = flags; +} - ifr.ifr_flags |= IFF_UP; +static int eth_dev_probe(struct connman_device *device) +{ + struct ethernet_data *ethernet; - if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) { - err = -errno; - goto done; - } + DBG("device %p", device); - err = 0; + ethernet = g_try_new0(struct ethernet_data, 1); + if (!ethernet) + return -ENOMEM; -done: - close(sk); + connman_device_set_data(device, ethernet); - return err; -} + ethernet->index = connman_device_get_index(device); + ethernet->flags = 0; -static int iface_down(struct connman_element *element) -{ - struct ifreq ifr; - int sk, err; + ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index, + ethernet_newlink, device); - DBG("element %p", element); + return 0; +} - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -errno; +static void eth_dev_remove(struct connman_device *device) +{ + struct ethernet_data *ethernet = connman_device_get_data(device); - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = element->netdev.index; + DBG("device %p", device); - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - err = -errno; - goto done; - } + connman_device_set_data(device, NULL); - if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { - err = -errno; - goto done; - } + connman_rtnl_remove_watch(ethernet->watch); - if (!(ifr.ifr_flags & IFF_UP)) { - err = -EALREADY; - goto done; - } + remove_network(device, ethernet); - ifr.ifr_flags &= ~IFF_UP; + g_free(ethernet); +} - if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) - err = -errno; - else - err = 0; +static int eth_dev_enable(struct connman_device *device) +{ + struct ethernet_data *ethernet = connman_device_get_data(device); -done: - close(sk); + DBG("device %p", device); - return err; + return connman_inet_ifup(ethernet->index); } -static int ethernet_probe(struct connman_element *element) +static int eth_dev_disable(struct connman_device *device) { - DBG("element %p name %s", element, element->name); + struct ethernet_data *ethernet = connman_device_get_data(device); - g_static_mutex_lock(ðernet_mutex); - ethernet_list = g_slist_append(ethernet_list, element); - g_static_mutex_unlock(ðernet_mutex); + DBG("device %p", device); - iface_up(element); + return connman_inet_ifdown(ethernet->index); +} - rtnl_request(); +static struct connman_device_driver eth_dev_driver = { + .name = "ethernet", + .type = CONNMAN_DEVICE_TYPE_ETHERNET, + .probe = eth_dev_probe, + .remove = eth_dev_remove, + .enable = eth_dev_enable, + .disable = eth_dev_disable, +}; +static int eth_tech_probe(struct connman_technology *technology) +{ return 0; } -static void ethernet_remove(struct connman_element *element) +static void eth_tech_remove(struct connman_technology *technology) { - DBG("element %p name %s", element, element->name); + DBG(""); +} - iface_down(element); +static GList *eth_interface_list = NULL; - g_static_mutex_lock(ðernet_mutex); - ethernet_list = g_slist_remove(ethernet_list, element); - g_static_mutex_unlock(ðernet_mutex); +static void eth_tech_add_interface(struct connman_technology *technology, + int index, const char *name, const char *ident) +{ + DBG("index %d name %s ident %s", index, name, ident); + + if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index))) + return; + + eth_interface_list = g_list_prepend(eth_interface_list, + (GINT_TO_POINTER((int) index))); } -static struct connman_driver ethernet_driver = { - .name = "ethernet", - .type = CONNMAN_ELEMENT_TYPE_DEVICE, - .subtype = CONNMAN_ELEMENT_SUBTYPE_ETHERNET, - .probe = ethernet_probe, - .remove = ethernet_remove, -}; +static void eth_tech_remove_interface(struct connman_technology *technology, + int index) +{ + DBG("index %d", index); + + eth_interface_list = g_list_remove(eth_interface_list, + GINT_TO_POINTER((int) index)); +} -static int rtnl_init(void) +static void eth_tech_enable_tethering(struct connman_technology *technology, + const char *bridge) { - struct sockaddr_nl addr; - int sk, err; + GList *list; + struct ethernet_data *ethernet; + + for (list = eth_interface_list; list; list = list->next) { + int index = GPOINTER_TO_INT(list->data); + struct connman_device *device = + connman_device_find_by_index(index); + + if (device) { + ethernet = connman_device_get_data(device); + if (ethernet) + remove_network(device, ethernet); + } - DBG(""); + connman_technology_tethering_notify(technology, true); - sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - if (sk < 0) - return -errno; + connman_inet_ifup(index); - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_LINK; + connman_inet_add_to_bridge(index, bridge); - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - close(sk); - return err; + eth_tethering = true; } +} - channel = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(channel, TRUE); +static void eth_tech_disable_tethering(struct connman_technology *technology, + const char *bridge) +{ + GList *list; - g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, - rtnl_event, NULL); + for (list = eth_interface_list; list; list = list->next) { + int index = GPOINTER_TO_INT(list->data); + struct connman_device *device = + connman_device_find_by_index(index); - return 0; + connman_inet_remove_from_bridge(index, bridge); + + connman_technology_tethering_notify(technology, false); + + if (device) + connman_device_reconnect_service(device); + + eth_tethering = false; + } } -static void rtnl_cleanup(void) +static int eth_tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled) { - DBG(""); + if (!connman_technology_is_tethering_allowed( + CONNMAN_SERVICE_TYPE_ETHERNET)) + return 0; + + DBG("bridge %s enabled %d", bridge, enabled); - g_io_channel_shutdown(channel, TRUE, NULL); - g_io_channel_unref(channel); + if (enabled) + eth_tech_enable_tethering(technology, bridge); + else + eth_tech_disable_tethering(technology, bridge); - channel = NULL; + return 0; } +static struct connman_technology_driver eth_tech_driver = { + .name = "ethernet", + .type = CONNMAN_SERVICE_TYPE_ETHERNET, + .probe = eth_tech_probe, + .remove = eth_tech_remove, + .add_interface = eth_tech_add_interface, + .remove_interface = eth_tech_remove_interface, + .set_tethering = eth_tech_set_tethering, +}; + static int ethernet_init(void) { int err; - err = rtnl_init(); + err = connman_technology_driver_register(ð_tech_driver); if (err < 0) return err; - err = connman_driver_register(ðernet_driver); + err = connman_network_driver_register(ð_network_driver); + if (err < 0) + return err; + + err = connman_device_driver_register(ð_dev_driver); if (err < 0) { - rtnl_cleanup(); + connman_network_driver_unregister(ð_network_driver); return err; } @@ -363,10 +455,12 @@ static int ethernet_init(void) static void ethernet_exit(void) { - connman_driver_unregister(ðernet_driver); + connman_technology_driver_unregister(ð_tech_driver); + + connman_network_driver_unregister(ð_network_driver); - rtnl_cleanup(); + connman_device_driver_unregister(ð_dev_driver); } -CONNMAN_PLUGIN_DEFINE("ethernet", "Ethernet interface plugin", VERSION, - ethernet_init, ethernet_exit) +CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION, + CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)