Imported Upstream version 1.35
[platform/upstream/connman.git] / plugins / ethernet.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <net/if.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <unistd.h>
31 #include <stdio.h>
32
33 #include <linux/if_vlan.h>
34 #include <linux/sockios.h>
35 #include <linux/ethtool.h>
36
37 #ifndef IFF_LOWER_UP
38 #define IFF_LOWER_UP    0x10000
39 #endif
40
41 #include <glib.h>
42
43 #define CONNMAN_API_SUBJECT_TO_CHANGE
44 #include <connman/technology.h>
45 #include <connman/plugin.h>
46 #include <connman/device.h>
47 #include <connman/inet.h>
48 #include <connman/rtnl.h>
49 #include <connman/log.h>
50 #include <connman/setting.h>
51
52 static bool eth_tethering = false;
53
54 struct ethernet_data {
55         int index;
56         unsigned flags;
57         unsigned int watch;
58         struct connman_network *network;
59 };
60
61
62 static int get_vlan_vid(const char *ifname)
63 {
64         struct vlan_ioctl_args vifr;
65         int vid;
66         int sk;
67
68         memset(&vifr, 0, sizeof(vifr));
69
70         sk = socket(AF_INET, SOCK_STREAM, 0);
71         if (sk < 0)
72                 return -errno;
73
74         vifr.cmd = GET_VLAN_VID_CMD;
75         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
76
77         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
78                 vid = vifr.u.VID;
79         else
80                 vid = -errno;
81
82         close(sk);
83
84         return vid;
85 }
86
87 static int get_dsa_port(const char *ifname)
88 {
89         int sk;
90         int dsaport = -1;
91         struct ifreq ifr;
92         struct ethtool_cmd cmd;
93         struct ethtool_drvinfo drvinfocmd;
94         struct vlan_ioctl_args vifr;
95
96         sk = socket(AF_INET, SOCK_STREAM, 0);
97         if (sk < 0)
98                 return -errno;
99
100         memset(&ifr, 0, sizeof(ifr));
101         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
102
103         /* check if it is a vlan and get physical interface name*/
104         vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
105         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
106
107         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
108                 strncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
109
110         /* get driver info */
111         drvinfocmd.cmd =  ETHTOOL_GDRVINFO;
112         ifr.ifr_data = (caddr_t)&drvinfocmd;
113
114         if (!ioctl(sk, SIOCETHTOOL, &ifr)) {
115                 if(!strcmp(drvinfocmd.driver, "dsa")) {
116                         /* get dsa port*/
117                         cmd.cmd =  ETHTOOL_GSET;
118                         ifr.ifr_data = (caddr_t)&cmd;
119
120                         if (!ioctl(sk, SIOCETHTOOL, &ifr))
121                                 dsaport = cmd.phy_address;
122                 }
123         }
124         close(sk);
125
126         return dsaport;
127 }
128
129 static int eth_network_probe(struct connman_network *network)
130 {
131         DBG("network %p", network);
132
133         return 0;
134 }
135
136 static void eth_network_remove(struct connman_network *network)
137 {
138         DBG("network %p", network);
139 }
140
141 static int eth_network_connect(struct connman_network *network)
142 {
143         DBG("network %p", network);
144
145         connman_network_set_connected(network, true);
146
147         return 0;
148 }
149
150 static int eth_network_disconnect(struct connman_network *network)
151 {
152         DBG("network %p", network);
153
154         connman_network_set_connected(network, false);
155
156         return 0;
157 }
158
159 static struct connman_network_driver eth_network_driver = {
160         .name           = "cable",
161         .type           = CONNMAN_NETWORK_TYPE_ETHERNET,
162         .probe          = eth_network_probe,
163         .remove         = eth_network_remove,
164         .connect        = eth_network_connect,
165         .disconnect     = eth_network_disconnect,
166 };
167
168 static void add_network(struct connman_device *device,
169                         struct ethernet_data *ethernet)
170 {
171         struct connman_network *network;
172         int index;
173         char *ifname;
174
175         network = connman_network_create("carrier",
176                                         CONNMAN_NETWORK_TYPE_ETHERNET);
177         if (!network)
178                 return;
179
180         index = connman_device_get_index(device);
181         connman_network_set_index(network, index);
182         ifname = connman_inet_ifname(index);
183         if (!ifname)
184                 return;
185
186         connman_network_set_name(network, "Wired");
187
188         if (connman_device_add_network(device, network) < 0) {
189                 connman_network_unref(network);
190                 return;
191         }
192
193         if (!eth_tethering) {
194                 char group[16] = "cable";
195                 int vid, dsaport;
196
197                 vid = get_vlan_vid(ifname);
198                 dsaport = get_dsa_port(ifname);
199
200                 /*
201                  * Prevent service from starting the reconnect
202                  * procedure as we do not want the DHCP client
203                  * to run when tethering.
204                  */
205                 if((vid >= 0) && (dsaport >= 0))
206                         snprintf(group, sizeof(group), "p%02x_%03x_cable", dsaport, vid);
207                 else if (vid >= 0)
208                         snprintf(group, sizeof(group), "%03x_cable", vid);
209                 else if (dsaport >= 0)
210                         snprintf(group, sizeof(group), "p%02x_cable", dsaport);
211
212                 connman_network_set_group(network, group);
213         }
214
215         ethernet->network = network;
216         g_free(ifname);
217 }
218
219 static void remove_network(struct connman_device *device,
220                                 struct ethernet_data *ethernet)
221 {
222         if (!ethernet->network)
223                 return;
224
225         connman_device_remove_network(device, ethernet->network);
226         connman_network_unref(ethernet->network);
227
228         ethernet->network = NULL;
229 }
230
231 static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
232 {
233         struct connman_device *device = user_data;
234         struct ethernet_data *ethernet = connman_device_get_data(device);
235
236         DBG("index %d flags %d change %d", ethernet->index, flags, change);
237
238         if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
239                 if (flags & IFF_UP) {
240                         DBG("power on");
241                         connman_device_set_powered(device, true);
242                 } else {
243                         DBG("power off");
244                         connman_device_set_powered(device, false);
245                 }
246         }
247
248         if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
249                 if (flags & IFF_LOWER_UP) {
250                         DBG("carrier on");
251                         add_network(device, ethernet);
252                 } else {
253                         DBG("carrier off");
254                         remove_network(device, ethernet);
255                 }
256         }
257
258         ethernet->flags = flags;
259 }
260
261 static int eth_dev_probe(struct connman_device *device)
262 {
263         struct ethernet_data *ethernet;
264
265         DBG("device %p", device);
266
267         ethernet = g_try_new0(struct ethernet_data, 1);
268         if (!ethernet)
269                 return -ENOMEM;
270
271         connman_device_set_data(device, ethernet);
272
273         ethernet->index = connman_device_get_index(device);
274         ethernet->flags = 0;
275
276         ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
277                                                 ethernet_newlink, device);
278
279         return 0;
280 }
281
282 static void eth_dev_remove(struct connman_device *device)
283 {
284         struct ethernet_data *ethernet = connman_device_get_data(device);
285
286         DBG("device %p", device);
287
288         connman_device_set_data(device, NULL);
289
290         connman_rtnl_remove_watch(ethernet->watch);
291
292         remove_network(device, ethernet);
293
294         g_free(ethernet);
295 }
296
297 static int eth_dev_enable(struct connman_device *device)
298 {
299         struct ethernet_data *ethernet = connman_device_get_data(device);
300
301         DBG("device %p", device);
302
303         return connman_inet_ifup(ethernet->index);
304 }
305
306 static int eth_dev_disable(struct connman_device *device)
307 {
308         struct ethernet_data *ethernet = connman_device_get_data(device);
309
310         DBG("device %p", device);
311
312         return connman_inet_ifdown(ethernet->index);
313 }
314
315 static struct connman_device_driver eth_dev_driver = {
316         .name           = "ethernet",
317         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
318         .probe          = eth_dev_probe,
319         .remove         = eth_dev_remove,
320         .enable         = eth_dev_enable,
321         .disable        = eth_dev_disable,
322 };
323
324 static int eth_tech_probe(struct connman_technology *technology)
325 {
326         return 0;
327 }
328
329 static void eth_tech_remove(struct connman_technology *technology)
330 {
331         DBG("");
332 }
333
334 static GList *eth_interface_list = NULL;
335
336 static void eth_tech_add_interface(struct connman_technology *technology,
337                         int index, const char *name, const char *ident)
338 {
339         DBG("index %d name %s ident %s", index, name, ident);
340
341         if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index)))
342                 return;
343
344         eth_interface_list = g_list_prepend(eth_interface_list,
345                                         (GINT_TO_POINTER((int) index)));
346 }
347
348 static void eth_tech_remove_interface(struct connman_technology *technology,
349                                                                 int index)
350 {
351         DBG("index %d", index);
352
353         eth_interface_list = g_list_remove(eth_interface_list,
354                                         GINT_TO_POINTER((int) index));
355 }
356
357 static void eth_tech_enable_tethering(struct connman_technology *technology,
358                                                 const char *bridge)
359 {
360         GList *list;
361         struct ethernet_data *ethernet;
362
363         for (list = eth_interface_list; list; list = list->next) {
364                 int index = GPOINTER_TO_INT(list->data);
365                 struct connman_device *device =
366                         connman_device_find_by_index(index);
367
368                 if (device) {
369                         ethernet = connman_device_get_data(device);
370                         if (ethernet)
371                                 remove_network(device, ethernet);
372                 }
373
374                 connman_technology_tethering_notify(technology, true);
375
376                 connman_inet_ifup(index);
377
378                 connman_inet_add_to_bridge(index, bridge);
379
380                 eth_tethering = true;
381         }
382 }
383
384 static void eth_tech_disable_tethering(struct connman_technology *technology,
385                                                 const char *bridge)
386 {
387         GList *list;
388
389         for (list = eth_interface_list; list; list = list->next) {
390                 int index = GPOINTER_TO_INT(list->data);
391                 struct connman_device *device =
392                         connman_device_find_by_index(index);
393
394                 connman_inet_remove_from_bridge(index, bridge);
395
396                 connman_technology_tethering_notify(technology, false);
397
398                 if (device)
399                         connman_device_reconnect_service(device);
400
401                 eth_tethering = false;
402         }
403 }
404
405 static int eth_tech_set_tethering(struct connman_technology *technology,
406                                 const char *identifier, const char *passphrase,
407                                 const char *bridge, bool enabled)
408 {
409         if (!connman_technology_is_tethering_allowed(
410                         CONNMAN_SERVICE_TYPE_ETHERNET))
411                 return 0;
412
413         DBG("bridge %s enabled %d", bridge, enabled);
414
415         if (enabled)
416                 eth_tech_enable_tethering(technology, bridge);
417         else
418                 eth_tech_disable_tethering(technology, bridge);
419
420         return 0;
421 }
422
423 static struct connman_technology_driver eth_tech_driver = {
424         .name                   = "ethernet",
425         .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
426         .probe                  = eth_tech_probe,
427         .remove                 = eth_tech_remove,
428         .add_interface          = eth_tech_add_interface,
429         .remove_interface       = eth_tech_remove_interface,
430         .set_tethering          = eth_tech_set_tethering,
431 };
432
433 static int ethernet_init(void)
434 {
435         int err;
436
437         err = connman_technology_driver_register(&eth_tech_driver);
438         if (err < 0)
439                 return err;
440
441         err = connman_network_driver_register(&eth_network_driver);
442         if (err < 0)
443                 return err;
444
445         err = connman_device_driver_register(&eth_dev_driver);
446         if (err < 0) {
447                 connman_network_driver_unregister(&eth_network_driver);
448                 return err;
449         }
450
451         return 0;
452 }
453
454 static void ethernet_exit(void)
455 {
456         connman_technology_driver_unregister(&eth_tech_driver);
457
458         connman_network_driver_unregister(&eth_network_driver);
459
460         connman_device_driver_unregister(&eth_dev_driver);
461 }
462
463 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
464                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)