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