[connman] Added Tizen Wi-Fi Mesh
[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 #if defined TIZEN_EXT_WIFI_MESH
52 #include <connman/mesh.h>
53 #endif
54
55 static bool eth_tethering = false;
56
57 struct ethernet_data {
58         int index;
59         unsigned flags;
60         unsigned int watch;
61         struct connman_network *network;
62 };
63
64
65 static int get_vlan_vid(const char *ifname)
66 {
67         struct vlan_ioctl_args vifr;
68         int vid;
69         int sk;
70
71         memset(&vifr, 0, sizeof(vifr));
72
73         sk = socket(AF_INET, SOCK_STREAM, 0);
74         if (sk < 0)
75                 return -errno;
76
77         vifr.cmd = GET_VLAN_VID_CMD;
78         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
79
80         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
81                 vid = vifr.u.VID;
82         else
83                 vid = -errno;
84
85         close(sk);
86
87         return vid;
88 }
89
90 static int get_dsa_port(const char *ifname)
91 {
92         int sk;
93         int dsaport = -1;
94         struct ifreq ifr;
95         struct ethtool_cmd cmd;
96         struct ethtool_drvinfo drvinfocmd;
97         struct vlan_ioctl_args vifr;
98
99         sk = socket(AF_INET, SOCK_STREAM, 0);
100         if (sk < 0)
101                 return -errno;
102
103         memset(&ifr, 0, sizeof(ifr));
104         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
105
106         /* check if it is a vlan and get physical interface name*/
107         vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
108         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
109
110         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
111                 strncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
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                 return;
194         }
195
196         if (!eth_tethering) {
197                 char group[16] = "cable";
198                 int vid, dsaport;
199
200                 vid = get_vlan_vid(ifname);
201                 dsaport = get_dsa_port(ifname);
202
203                 /*
204                  * Prevent service from starting the reconnect
205                  * procedure as we do not want the DHCP client
206                  * to run when tethering.
207                  */
208                 if((vid >= 0) && (dsaport >= 0))
209                         snprintf(group, sizeof(group), "p%02x_%03x_cable", dsaport, vid);
210                 else if (vid >= 0)
211                         snprintf(group, sizeof(group), "%03x_cable", vid);
212                 else if (dsaport >= 0)
213                         snprintf(group, sizeof(group), "p%02x_cable", dsaport);
214
215                 connman_network_set_group(network, group);
216         }
217
218         ethernet->network = network;
219         g_free(ifname);
220 }
221
222 static void remove_network(struct connman_device *device,
223                                 struct ethernet_data *ethernet)
224 {
225         if (!ethernet->network)
226                 return;
227
228         connman_device_remove_network(device, ethernet->network);
229         connman_network_unref(ethernet->network);
230
231         ethernet->network = NULL;
232 }
233
234 static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
235 {
236         struct connman_device *device = user_data;
237         struct ethernet_data *ethernet = connman_device_get_data(device);
238
239         DBG("index %d flags %d change %d", ethernet->index, flags, change);
240
241         if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
242                 if (flags & IFF_UP) {
243                         DBG("power on");
244                         connman_device_set_powered(device, true);
245                 } else {
246                         DBG("power off");
247                         connman_device_set_powered(device, false);
248                 }
249         }
250
251         if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
252                 if (flags & IFF_LOWER_UP) {
253                         DBG("carrier on");
254                         add_network(device, ethernet);
255                 } else {
256                         DBG("carrier off");
257                         remove_network(device, ethernet);
258 #if defined TIZEN_EXT_WIFI_MESH
259                         /* Remove ethernet from mesh bridge */
260                         __connman_mesh_remove_ethernet_from_bridge();
261 #endif
262                 }
263         }
264
265         ethernet->flags = flags;
266 }
267
268 static int eth_dev_probe(struct connman_device *device)
269 {
270         struct ethernet_data *ethernet;
271
272         DBG("device %p", device);
273
274         ethernet = g_try_new0(struct ethernet_data, 1);
275         if (!ethernet)
276                 return -ENOMEM;
277
278         connman_device_set_data(device, ethernet);
279
280         ethernet->index = connman_device_get_index(device);
281         ethernet->flags = 0;
282
283         ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
284                                                 ethernet_newlink, device);
285
286         return 0;
287 }
288
289 static void eth_dev_remove(struct connman_device *device)
290 {
291         struct ethernet_data *ethernet = connman_device_get_data(device);
292
293         DBG("device %p", device);
294
295         connman_device_set_data(device, NULL);
296
297         connman_rtnl_remove_watch(ethernet->watch);
298
299         remove_network(device, ethernet);
300
301         g_free(ethernet);
302 }
303
304 static int eth_dev_enable(struct connman_device *device)
305 {
306         struct ethernet_data *ethernet = connman_device_get_data(device);
307
308         DBG("device %p", device);
309
310         return connman_inet_ifup(ethernet->index);
311 }
312
313 static int eth_dev_disable(struct connman_device *device)
314 {
315         struct ethernet_data *ethernet = connman_device_get_data(device);
316
317         DBG("device %p", device);
318
319         return connman_inet_ifdown(ethernet->index);
320 }
321
322 static struct connman_device_driver eth_dev_driver = {
323         .name           = "ethernet",
324         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
325         .probe          = eth_dev_probe,
326         .remove         = eth_dev_remove,
327         .enable         = eth_dev_enable,
328         .disable        = eth_dev_disable,
329 };
330
331 static int eth_tech_probe(struct connman_technology *technology)
332 {
333         return 0;
334 }
335
336 static void eth_tech_remove(struct connman_technology *technology)
337 {
338         DBG("");
339 }
340
341 static GList *eth_interface_list = NULL;
342
343 static void eth_tech_add_interface(struct connman_technology *technology,
344                         int index, const char *name, const char *ident)
345 {
346         DBG("index %d name %s ident %s", index, name, ident);
347
348         if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index)))
349                 return;
350
351         eth_interface_list = g_list_prepend(eth_interface_list,
352                                         (GINT_TO_POINTER((int) index)));
353 }
354
355 static void eth_tech_remove_interface(struct connman_technology *technology,
356                                                                 int index)
357 {
358         DBG("index %d", index);
359
360         eth_interface_list = g_list_remove(eth_interface_list,
361                                         GINT_TO_POINTER((int) index));
362 }
363
364 static void eth_tech_enable_tethering(struct connman_technology *technology,
365                                                 const char *bridge)
366 {
367         GList *list;
368         struct ethernet_data *ethernet;
369
370         for (list = eth_interface_list; list; list = list->next) {
371                 int index = GPOINTER_TO_INT(list->data);
372                 struct connman_device *device =
373                         connman_device_find_by_index(index);
374
375                 if (device) {
376                         ethernet = connman_device_get_data(device);
377                         if (ethernet)
378                                 remove_network(device, ethernet);
379                 }
380
381                 connman_technology_tethering_notify(technology, true);
382
383                 connman_inet_ifup(index);
384
385                 connman_inet_add_to_bridge(index, bridge);
386
387                 eth_tethering = true;
388         }
389 }
390
391 static void eth_tech_disable_tethering(struct connman_technology *technology,
392                                                 const char *bridge)
393 {
394         GList *list;
395
396         for (list = eth_interface_list; list; list = list->next) {
397                 int index = GPOINTER_TO_INT(list->data);
398                 struct connman_device *device =
399                         connman_device_find_by_index(index);
400
401                 connman_inet_remove_from_bridge(index, bridge);
402
403                 connman_technology_tethering_notify(technology, false);
404
405                 if (device)
406                         connman_device_reconnect_service(device);
407
408                 eth_tethering = false;
409         }
410 }
411
412 static int eth_tech_set_tethering(struct connman_technology *technology,
413                                 const char *identifier, const char *passphrase,
414                                 const char *bridge, bool enabled)
415 {
416         if (!connman_technology_is_tethering_allowed(
417                         CONNMAN_SERVICE_TYPE_ETHERNET))
418                 return 0;
419
420         DBG("bridge %s enabled %d", bridge, enabled);
421
422         if (enabled)
423                 eth_tech_enable_tethering(technology, bridge);
424         else
425                 eth_tech_disable_tethering(technology, bridge);
426
427         return 0;
428 }
429
430 static struct connman_technology_driver eth_tech_driver = {
431         .name                   = "ethernet",
432         .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
433         .probe                  = eth_tech_probe,
434         .remove                 = eth_tech_remove,
435         .add_interface          = eth_tech_add_interface,
436         .remove_interface       = eth_tech_remove_interface,
437         .set_tethering          = eth_tech_set_tethering,
438 };
439
440 #if defined TIZEN_EXT_WIFI_MESH
441 static int eth_mesh_add_to_bridge(const char *bridge)
442 {
443         GList *list;
444         struct ethernet_data *ethernet;
445
446         DBG("Add ethernet to bridge %s", bridge);
447
448         for (list = eth_interface_list; list; list = list->next) {
449                 int index = GPOINTER_TO_INT(list->data);
450                 struct connman_device *device =
451                         connman_device_find_by_index(index);
452
453                 if (device) {
454                         ethernet = connman_device_get_data(device);
455                         if (ethernet)
456                                 remove_network(device, ethernet);
457                 }
458
459                 connman_inet_ifup(index);
460
461                 connman_inet_add_to_bridge(index, bridge);
462         }
463
464         return 0;
465 }
466
467 static int eth_mesh_remove_from_bridge(const char *bridge)
468 {
469         GList *list;
470
471         DBG("Remove ethernet from bridge %s", bridge);
472
473         for (list = eth_interface_list; list; list = list->next) {
474                 int index = GPOINTER_TO_INT(list->data);
475
476                 connman_inet_remove_from_bridge(index, bridge);
477         }
478
479         return 0;
480 }
481
482 static struct connman_mesh_eth_driver eth_mesh_driver = {
483         .add_to_bridge          = eth_mesh_add_to_bridge,
484         .remove_from_bridge     = eth_mesh_remove_from_bridge,
485 };
486 #endif
487
488 static int ethernet_init(void)
489 {
490         int err;
491
492         err = connman_technology_driver_register(&eth_tech_driver);
493         if (err < 0)
494                 return err;
495
496 #if defined TIZEN_EXT_WIFI_MESH
497         err = connman_mesh_eth_driver_register(&eth_mesh_driver);
498         if (err < 0)
499                 return err;
500 #endif
501
502         err = connman_network_driver_register(&eth_network_driver);
503         if (err < 0)
504                 return err;
505
506         err = connman_device_driver_register(&eth_dev_driver);
507         if (err < 0) {
508                 connman_network_driver_unregister(&eth_network_driver);
509                 return err;
510         }
511
512         return 0;
513 }
514
515 static void ethernet_exit(void)
516 {
517         connman_technology_driver_unregister(&eth_tech_driver);
518
519 #if defined TIZEN_EXT_WIFI_MESH
520         connman_mesh_eth_driver_unregister(&eth_mesh_driver);
521 #endif
522
523         connman_network_driver_unregister(&eth_network_driver);
524
525         connman_device_driver_unregister(&eth_dev_driver);
526 }
527
528 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
529                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)