X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fethernet.c;h=4dda80c8851d19624675cc6361eb18af7f609857;hb=e2aacb30e612fed1db0ab4a35b922b55d52e90ae;hp=1ddf1fa900a42340cdd4bc48c86cb425828f02b1;hpb=991fe1e5f6458aa30ee82ad73b2f0411b74b1d3e;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/ethernet.c b/plugins/ethernet.c index 1ddf1fa..4dda80c 100644 --- a/plugins/ethernet.c +++ b/plugins/ethernet.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2009 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 @@ -24,100 +24,518 @@ #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 +#if defined TIZEN_EXT_WIFI_MESH +#include +#endif -#include "inet.h" +#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET +#include +#include +#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ + +static bool eth_tethering = false; struct ethernet_data { int index; unsigned flags; + unsigned int watch; + struct connman_network *network; +#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET + GSupplicantInterface *interface; +#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ }; -static GSList *ethernet_list = NULL; -static void ethernet_newlink(unsigned short type, int index, - unsigned flags, unsigned change) +static int get_vlan_vid(const char *ifname) { - GSList *list; + struct vlan_ioctl_args vifr; + int vid; + int sk; - DBG("index %d flags %ld change %ld", index, flags, change); + memset(&vifr, 0, sizeof(vifr)); - for (list = ethernet_list; list; list = list->next) { - struct connman_device *device = list->data; - struct ethernet_data *ethernet; + sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk < 0) + return -errno; - ethernet = connman_device_get_data(device); - if (ethernet == NULL) - continue; + vifr.cmd = GET_VLAN_VID_CMD; + stpncpy(vifr.device1, ifname, sizeof(vifr.device1)); - if (ethernet->index != index) - continue; + if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) + vid = vifr.u.VID; + else + vid = -errno; - 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); - } + close(sk); + + return vid; +} + +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; + + sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk < 0) + return -errno; + + memset(&ifr, 0, sizeof(ifr)); + stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + /* 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)); + + if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) + stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name)); + + /* get driver info */ + drvinfocmd.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)&drvinfocmd; + + if (!ioctl(sk, SIOCETHTOOL, &ifr)) { + if(!strcmp(drvinfocmd.driver, "dsa")) { + /* get dsa port*/ + cmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&cmd; + + if (!ioctl(sk, SIOCETHTOOL, &ifr)) + dsaport = cmd.phy_address; } + } + close(sk); + + return dsaport; +} + +static int eth_network_probe(struct connman_network *network) +{ + DBG("network %p", network); + + return 0; +} + +static void eth_network_remove(struct connman_network *network) +{ + DBG("network %p", network); +} + +#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET +#define NETCONFIG_SERVICE "net.netconfig" +#define NETCONFIG_ETHERNET_INTERFACE NETCONFIG_SERVICE ".ethernet" +#define NETCONFIG_ETHERNET_PATH "/net/netconfig/ethernet" + +struct eapol_method_call_data { + DBusConnection *connection; + struct connman_network *network; +}; + +static struct eapol_method_call_data enable_eapol_data; + +void handle_eap_signal(GSupplicantInterface *interface, bool status) +{ + DBG("captured EAP signal"); + + if (!enable_eapol_data.network) + return; + + if (g_strcmp0("wired", g_supplicant_interface_get_driver(interface))) + return; + + if (!connman_network_check_validity(enable_eapol_data.network)) + return; + + DBG("network is valid"); + + g_supplicant_unregister_eap_callback(); + + if (!status) { + // Should we mark service as non favorite or make autoconnect as false? + + struct ethernet_data *ethernet = g_supplicant_interface_get_data(interface); + if (ethernet && ethernet->interface) { + g_supplicant_interface_remove(ethernet->interface, NULL, NULL); + ethernet->interface = NULL; + } + + connman_network_set_error(enable_eapol_data.network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); + enable_eapol_data.network = NULL; + return; + } + + connman_network_set_connected(enable_eapol_data.network, status); + enable_eapol_data.network = NULL; +} + +static void interface_create_callback(int result, + GSupplicantInterface *interface, void *user_data) +{ + struct ethernet_data *ethernet = user_data; + + if (result < 0 || !interface || !ethernet) + return; + + DBG("result %d ifname %s, ethernet %p", result, + g_supplicant_interface_get_ifname(interface), + ethernet); + + ethernet->interface = interface; + g_supplicant_interface_set_data(interface, ethernet); +} + +static int eapol_interface_create(void) +{ + struct connman_network *network = enable_eapol_data.network; + struct connman_service *service = connman_service_lookup_from_network(network); + + if (!service) { + DBG("service not found"); + return -1; + } + + struct connman_device *device = connman_network_get_device(network); + struct ethernet_data *ethernet = connman_device_get_data(device); + const char *driver = "wired"; + int index = connman_network_get_index(network); + char *ifname = connman_inet_ifname(index);; + char *config_file = NULL; + + g_supplicant_register_eap_callback(handle_eap_signal); + + if (asprintf(&config_file, "/var/lib/connman/%s-eapol.conf", ifname) < 0) { + g_free(ifname); + return -ENOMEM; + } + + DBG("config_file %s", config_file); + + g_supplicant_replace_config_file(ifname, config_file); + free(config_file); - if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) { - if (flags & IFF_LOWER_UP) { - DBG("carrier on"); - connman_device_set_carrier(device, TRUE); - } else { - DBG("carrier off"); - connman_device_set_carrier(device, FALSE); + /* + * TODO: RemoveInterface if already present because + * already created interface will not start EAP handshake. + */ + return g_supplicant_interface_create(ifname, driver, NULL, + 0, 0, 60, interface_create_callback, ethernet); +} + +static void enable_eapol_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply; + DBusError error; + DBusMessageIter args; + + DBG(""); + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&error); + if (dbus_set_error_from_message(&error, reply)) { + DBG("enable_eapol_request() %s %s", error.name, error.message); + dbus_error_free(&error); + dbus_message_unref(reply); + dbus_pending_call_unref(call); + dbus_connection_unref(enable_eapol_data.connection); + + enable_eapol_data.connection = NULL; + return; + } + + if (eapol_interface_create() < 0) + DBG("Failed to create eapol interface"); +} + +static int eth_network_enable_eapol(struct connman_service *service, struct connman_network *network) +{ + DBusMessage *msg = NULL; + DBusPendingCall *call; + + DBusConnection *connection = connman_dbus_get_connection(); + if (!connection) { + DBG("dbus connection does not exist"); + return -EINVAL; + } + + msg = dbus_message_new_method_call(NETCONFIG_SERVICE, NETCONFIG_ETHERNET_PATH, + NETCONFIG_ETHERNET_INTERFACE, "EnableEap"); + if (!msg) { + dbus_connection_unref(connection); + return -EINVAL; + } + + const char *path = __connman_service_get_path(service); + dbus_bool_t enable = true; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &enable, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, msg, + &call, DBUS_TIMEOUT_USE_DEFAULT)) { + dbus_message_unref(msg); + dbus_connection_unref(connection); + return -EIO; + } + + if (!call) { + dbus_message_unref(msg); + dbus_connection_unref(connection); + return -EIO; + } + + enable_eapol_data.connection = connection; + enable_eapol_data.network = network; + + dbus_pending_call_set_notify(call, enable_eapol_reply, NULL, NULL); + dbus_message_unref(msg); + + return 0; +} + +static int eth_network_connect(struct connman_network *network) +{ + DBG("network %p", network); + + int err = 0; + struct connman_service *service = connman_service_lookup_from_network(network); + + if (service && __connman_service_get_use_eapol(service)) { + /** Enable eapol on device reboot **/ + if (__connman_service_get_connect_reason(service) != CONNMAN_SERVICE_CONNECT_REASON_USER) { + err = eth_network_enable_eapol(service, network); + if (err < 0) { + DBG("Failed to enable eapol"); + return err; + } + } else { + err = eapol_interface_create(); + if (err < 0) { + DBG("Failed to create eapol interface"); + return err; } + + return 0; } + } + + connman_network_set_connected(network, true); + + return 0; +} + +static int eth_network_disconnect(struct connman_network *network) +{ + DBG("network %p", network); - ethernet->flags = flags; + struct connman_service *service = connman_service_lookup_from_network(network); + + if (service && __connman_service_get_use_eapol(service)) { + struct connman_device *device = connman_network_get_device(network); + struct ethernet_data *ethernet = connman_device_get_data(device); + + enable_eapol_data.network = NULL; + g_supplicant_unregister_eap_callback(); + if (ethernet && ethernet->interface) { + g_supplicant_interface_remove(ethernet->interface, NULL, NULL); + ethernet->interface = NULL; + } + connman_network_set_associating(network, false); + connman_network_set_connected(network, false); + + return 0; } + + connman_network_set_connected(network, false); + + return 0; } -static struct connman_rtnl ethernet_rtnl = { - .name = "ethernet", - .newlink = ethernet_newlink, +#else /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ + +static int eth_network_connect(struct connman_network *network) +{ + DBG("network %p", network); + + connman_network_set_connected(network, true); + + return 0; +} + +static int eth_network_disconnect(struct connman_network *network) +{ + DBG("network %p", network); + + connman_network_set_connected(network, false); + + return 0; +} + +#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ + +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, }; -static int ethernet_probe(struct connman_device *device) +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; + } + + 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); + } + + ethernet->network = network; + g_free(ifname); +} + +static void remove_network(struct connman_device *device, + struct ethernet_data *ethernet) +{ + if (!ethernet->network) + return; + + connman_device_remove_network(device, ethernet->network); + connman_network_unref(ethernet->network); + + ethernet->network = NULL; +} + +static void ethernet_newlink(unsigned flags, unsigned change, void *user_data) +{ + struct connman_device *device = user_data; + struct ethernet_data *ethernet = connman_device_get_data(device); + + DBG("index %d flags %d change %d", ethernet->index, flags, change); + + 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 ((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 defined TIZEN_EXT_WIFI_MESH + /* Remove ethernet from mesh bridge */ + __connman_mesh_remove_ethernet_from_bridge(); +#endif + } + } + + ethernet->flags = flags; +} + +static int eth_dev_probe(struct connman_device *device) { struct ethernet_data *ethernet; DBG("device %p", device); ethernet = g_try_new0(struct ethernet_data, 1); - if (ethernet == NULL) + if (!ethernet) return -ENOMEM; - ethernet_list = g_slist_append(ethernet_list, device); - connman_device_set_data(device, ethernet); ethernet->index = connman_device_get_index(device); + ethernet->flags = 0; +#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET + ethernet->interface = NULL; +#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ - connman_rtnl_send_getlink(); + ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index, + ethernet_newlink, device); return 0; } -static void ethernet_remove(struct connman_device *device) +static void eth_dev_remove(struct connman_device *device) { struct ethernet_data *ethernet = connman_device_get_data(device); @@ -125,49 +543,228 @@ static void ethernet_remove(struct connman_device *device) connman_device_set_data(device, NULL); - ethernet_list = g_slist_remove(ethernet_list, device); +#if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET + if (!ethernet) + return; + + if (ethernet->interface) { + g_supplicant_interface_remove(ethernet->interface, NULL, NULL); + ethernet->interface = NULL; + } +#endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ + + connman_rtnl_remove_watch(ethernet->watch); + + remove_network(device, ethernet); g_free(ethernet); } -static int ethernet_enable(struct connman_device *device) +static int eth_dev_enable(struct connman_device *device) { struct ethernet_data *ethernet = connman_device_get_data(device); DBG("device %p", device); - return inet_ifup(ethernet->index); + return connman_inet_ifup(ethernet->index); } -static int ethernet_disable(struct connman_device *device) +static int eth_dev_disable(struct connman_device *device) { struct ethernet_data *ethernet = connman_device_get_data(device); DBG("device %p", device); - return inet_ifdown(ethernet->index); + return connman_inet_ifdown(ethernet->index); } -static struct connman_device_driver ethernet_driver = { +static struct connman_device_driver eth_dev_driver = { .name = "ethernet", .type = CONNMAN_DEVICE_TYPE_ETHERNET, - .probe = ethernet_probe, - .remove = ethernet_remove, - .enable = ethernet_enable, - .disable = ethernet_disable, + .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 eth_tech_remove(struct connman_technology *technology) +{ + DBG(""); +} + +static GList *eth_interface_list = NULL; + +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 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 void eth_tech_enable_tethering(struct connman_technology *technology, + const char *bridge) +{ + 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); + } + + connman_technology_tethering_notify(technology, true); + + connman_inet_ifup(index); + + connman_inet_add_to_bridge(index, bridge); + + eth_tethering = true; + } +} + +static void eth_tech_disable_tethering(struct connman_technology *technology, + const char *bridge) +{ + GList *list; + + 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); + + connman_inet_remove_from_bridge(index, bridge); + + connman_technology_tethering_notify(technology, false); + + if (device) + connman_device_reconnect_service(device); + + eth_tethering = false; + } +} + +static int eth_tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled) +{ + if (!connman_technology_is_tethering_allowed( + CONNMAN_SERVICE_TYPE_ETHERNET)) + return 0; + + DBG("bridge %s enabled %d", bridge, enabled); + + if (enabled) + eth_tech_enable_tethering(technology, bridge); + else + eth_tech_disable_tethering(technology, bridge); + + 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, }; +#if defined TIZEN_EXT_WIFI_MESH +static int eth_mesh_add_to_bridge(const char *bridge) +{ + GList *list; + struct ethernet_data *ethernet; + + DBG("Add ethernet to bridge %s", bridge); + + 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); + } + + connman_inet_ifup(index); + + connman_inet_add_to_bridge(index, bridge); + } + + return 0; +} + +static int eth_mesh_remove_from_bridge(const char *bridge) +{ + GList *list; + + DBG("Remove ethernet from bridge %s", bridge); + + for (list = eth_interface_list; list; list = list->next) { + int index = GPOINTER_TO_INT(list->data); + + connman_inet_remove_from_bridge(index, bridge); + } + + return 0; +} + +static struct connman_mesh_eth_driver eth_mesh_driver = { + .add_to_bridge = eth_mesh_add_to_bridge, + .remove_from_bridge = eth_mesh_remove_from_bridge, +}; +#endif + static int ethernet_init(void) { int err; - err = connman_rtnl_register(ðernet_rtnl); + err = connman_technology_driver_register(ð_tech_driver); + if (err < 0) + return err; + +#if defined TIZEN_EXT_WIFI_MESH + err = connman_mesh_eth_driver_register(ð_mesh_driver); + if (err < 0) + return err; +#endif + + err = connman_network_driver_register(ð_network_driver); if (err < 0) return err; - err = connman_device_driver_register(ðernet_driver); + err = connman_device_driver_register(ð_dev_driver); if (err < 0) { - connman_rtnl_unregister(ðernet_rtnl); + connman_network_driver_unregister(ð_network_driver); return err; } @@ -176,10 +773,16 @@ static int ethernet_init(void) static void ethernet_exit(void) { - connman_device_driver_unregister(ðernet_driver); + connman_technology_driver_unregister(ð_tech_driver); + +#if defined TIZEN_EXT_WIFI_MESH + connman_mesh_eth_driver_unregister(ð_mesh_driver); +#endif + + connman_network_driver_unregister(ð_network_driver); - connman_rtnl_unregister(ðernet_rtnl); + connman_device_driver_unregister(ð_dev_driver); } CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION, - ethernet_init, ethernet_exit) + CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)