b0031cbe6eb6daaa58c11684dd0013d97c9d61ac
[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 #if defined TIZEN_EXT_WIFI_MESH
53 #include <connman/mesh.h>
54 #endif
55
56 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
57 #include <connman/option.h>
58 #include <gsupplicant/gsupplicant.h>
59 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
60
61 static bool eth_tethering = false;
62
63 struct ethernet_data {
64         int index;
65         unsigned flags;
66         unsigned int watch;
67         struct connman_network *network;
68 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
69         GSupplicantInterface *interface;
70 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
71 };
72
73
74 static int get_vlan_vid(const char *ifname)
75 {
76         struct vlan_ioctl_args vifr;
77         int vid;
78         int sk;
79
80         memset(&vifr, 0, sizeof(vifr));
81
82         sk = socket(AF_INET, SOCK_STREAM, 0);
83         if (sk < 0)
84                 return -errno;
85
86         vifr.cmd = GET_VLAN_VID_CMD;
87         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
88
89         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
90                 vid = vifr.u.VID;
91         else
92                 vid = -errno;
93
94         close(sk);
95
96         return vid;
97 }
98
99 static int get_dsa_port(const char *ifname)
100 {
101         int sk;
102         int dsaport = -1;
103         struct ifreq ifr;
104         struct ethtool_cmd cmd;
105         struct ethtool_drvinfo drvinfocmd;
106         struct vlan_ioctl_args vifr;
107
108         sk = socket(AF_INET, SOCK_STREAM, 0);
109         if (sk < 0)
110                 return -errno;
111
112         memset(&ifr, 0, sizeof(ifr));
113         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
114
115         /* check if it is a vlan and get physical interface name*/
116         vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
117         strncpy(vifr.device1, ifname, sizeof(vifr.device1));
118
119         if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
120                 strncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
121
122         /* get driver info */
123         drvinfocmd.cmd =  ETHTOOL_GDRVINFO;
124         ifr.ifr_data = (caddr_t)&drvinfocmd;
125
126         if (!ioctl(sk, SIOCETHTOOL, &ifr)) {
127                 if(!strcmp(drvinfocmd.driver, "dsa")) {
128                         /* get dsa port*/
129                         cmd.cmd =  ETHTOOL_GSET;
130                         ifr.ifr_data = (caddr_t)&cmd;
131
132                         if (!ioctl(sk, SIOCETHTOOL, &ifr))
133                                 dsaport = cmd.phy_address;
134                 }
135         }
136         close(sk);
137
138         return dsaport;
139 }
140
141 static int eth_network_probe(struct connman_network *network)
142 {
143         DBG("network %p", network);
144
145         return 0;
146 }
147
148 static void eth_network_remove(struct connman_network *network)
149 {
150         DBG("network %p", network);
151 }
152
153 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
154 static struct connman_network *g_network = NULL;
155
156 void handle_eap_signal(GSupplicantInterface *interface, bool status)
157 {
158         DBG("captured EAP signal");
159
160         if (!g_network)
161                 return;
162
163         if (g_strcmp0("wired", g_supplicant_interface_get_driver(interface)))
164                 return;
165
166         if (!connman_network_check_validity(g_network))
167                 return;
168
169         DBG("network is valid");
170
171         g_supplicant_unregister_eap_callback();
172
173         if (!status) {
174                 // Should we mark service as non favorite or make autoconnect as false?
175
176                 struct ethernet_data *ethernet = g_supplicant_interface_get_data(interface);
177                 if (ethernet && ethernet->interface) {
178                         g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
179                         ethernet->interface = NULL;
180                 }
181
182                 connman_network_set_error(g_network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
183                 g_network = NULL;
184                 return;
185         }
186
187         connman_network_set_connected(g_network, status);
188         g_network = NULL;
189 }
190
191 static void interface_create_callback(int result,
192                 GSupplicantInterface *interface, void *user_data)
193 {
194         struct ethernet_data *ethernet = user_data;
195
196         if (result < 0 || !interface || !ethernet)
197                 return;
198
199         DBG("result %d ifname %s, ethernet %p", result,
200                         g_supplicant_interface_get_ifname(interface),
201                         ethernet);
202
203         ethernet->interface = interface;
204         g_supplicant_interface_set_data(interface, ethernet);
205 }
206
207 static int eth_network_connect(struct connman_network *network)
208 {
209         DBG("network %p", network);
210
211         struct connman_service *service = connman_service_lookup_from_network(network);
212
213         if (service && __connman_service_get_use_eapol(service)) {
214                 struct connman_device *device = connman_network_get_device(network);
215                 struct ethernet_data *ethernet = connman_device_get_data(device);
216                 const char *driver = "wired";
217                 int index = connman_network_get_index(network);
218                 char *ifname = connman_inet_ifname(index);;
219                 char *config_file = NULL;
220
221                 g_supplicant_register_eap_callback(handle_eap_signal);
222                 g_network = network;
223
224                 if (asprintf(&config_file, "/var/lib/connman/%s-eapol.conf", ifname) < 0) {
225                         g_free(ifname);
226                         return -ENOMEM;
227                 }
228
229                 DBG("config_file %s", config_file);
230
231                 g_supplicant_replace_config_file(ifname, config_file);
232                 free(config_file);
233
234                 /*
235                  *  TODO: RemoveInterface if already present because
236                  *  already created interface will not start EAP handshake.
237                  */
238                 g_supplicant_interface_create(ifname, driver, NULL,
239                                 interface_create_callback, ethernet);
240
241                 g_free(ifname);
242
243                 return 0;
244         }
245
246         connman_network_set_connected(network, true);
247
248         return 0;
249 }
250
251 static int eth_network_disconnect(struct connman_network *network)
252 {
253         DBG("network %p", network);
254
255         struct connman_service *service = connman_service_lookup_from_network(network);
256
257         if (service && __connman_service_get_use_eapol(service)) {
258                 struct connman_device *device = connman_network_get_device(network);
259                 struct ethernet_data *ethernet = connman_device_get_data(device);
260
261                 g_network = NULL;
262                 g_supplicant_unregister_eap_callback();
263                 if (ethernet && ethernet->interface) {
264                         g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
265                         ethernet->interface = NULL;
266                 }
267                 connman_network_set_associating(network, false);
268                 connman_network_set_connected(network, false);
269
270                 return 0;
271         }
272
273         connman_network_set_connected(network, false);
274
275         return 0;
276 }
277
278 #else /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
279
280 static int eth_network_connect(struct connman_network *network)
281 {
282         DBG("network %p", network);
283
284         connman_network_set_connected(network, true);
285
286         return 0;
287 }
288
289 static int eth_network_disconnect(struct connman_network *network)
290 {
291         DBG("network %p", network);
292
293         connman_network_set_connected(network, false);
294
295         return 0;
296 }
297
298 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
299
300 static struct connman_network_driver eth_network_driver = {
301         .name           = "cable",
302         .type           = CONNMAN_NETWORK_TYPE_ETHERNET,
303         .probe          = eth_network_probe,
304         .remove         = eth_network_remove,
305         .connect        = eth_network_connect,
306         .disconnect     = eth_network_disconnect,
307 };
308
309 static void add_network(struct connman_device *device,
310                         struct ethernet_data *ethernet)
311 {
312         struct connman_network *network;
313         int index;
314         char *ifname;
315
316         network = connman_network_create("carrier",
317                                         CONNMAN_NETWORK_TYPE_ETHERNET);
318         if (!network)
319                 return;
320
321         index = connman_device_get_index(device);
322         connman_network_set_index(network, index);
323         ifname = connman_inet_ifname(index);
324         if (!ifname)
325                 return;
326
327         connman_network_set_name(network, "Wired");
328
329         if (connman_device_add_network(device, network) < 0) {
330                 connman_network_unref(network);
331                 g_free(ifname);
332                 return;
333         }
334
335         if (!eth_tethering) {
336                 char group[25] = "cable";
337                 int vid, dsaport;
338
339                 vid = get_vlan_vid(ifname);
340                 dsaport = get_dsa_port(ifname);
341
342                 /*
343                  * Prevent service from starting the reconnect
344                  * procedure as we do not want the DHCP client
345                  * to run when tethering.
346                  */
347                 if((vid >= 0) && (dsaport >= 0))
348                         snprintf(group, sizeof(group), "p%02x_%03x_cable", dsaport, vid);
349                 else if (vid >= 0)
350                         snprintf(group, sizeof(group), "%03x_cable", vid);
351                 else if (dsaport >= 0)
352                         snprintf(group, sizeof(group), "p%02x_cable", dsaport);
353
354                 connman_network_set_group(network, group);
355         }
356
357         ethernet->network = network;
358         g_free(ifname);
359 }
360
361 static void remove_network(struct connman_device *device,
362                                 struct ethernet_data *ethernet)
363 {
364         if (!ethernet->network)
365                 return;
366
367         connman_device_remove_network(device, ethernet->network);
368         connman_network_unref(ethernet->network);
369
370         ethernet->network = NULL;
371 }
372
373 static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
374 {
375         struct connman_device *device = user_data;
376         struct ethernet_data *ethernet = connman_device_get_data(device);
377
378         DBG("index %d flags %d change %d", ethernet->index, flags, change);
379
380         if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
381                 if (flags & IFF_UP) {
382                         DBG("power on");
383                         connman_device_set_powered(device, true);
384                 } else {
385                         DBG("power off");
386                         connman_device_set_powered(device, false);
387                 }
388         }
389
390         if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
391                 if (flags & IFF_LOWER_UP) {
392                         DBG("carrier on");
393                         add_network(device, ethernet);
394                 } else {
395                         DBG("carrier off");
396                         remove_network(device, ethernet);
397 #if defined TIZEN_EXT_WIFI_MESH
398                         /* Remove ethernet from mesh bridge */
399                         __connman_mesh_remove_ethernet_from_bridge();
400 #endif
401                 }
402         }
403
404         ethernet->flags = flags;
405 }
406
407 static int eth_dev_probe(struct connman_device *device)
408 {
409         struct ethernet_data *ethernet;
410
411         DBG("device %p", device);
412
413         ethernet = g_try_new0(struct ethernet_data, 1);
414         if (!ethernet)
415                 return -ENOMEM;
416
417         connman_device_set_data(device, ethernet);
418
419         ethernet->index = connman_device_get_index(device);
420         ethernet->flags = 0;
421 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
422         ethernet->interface = NULL;
423 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
424
425         ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
426                                                 ethernet_newlink, device);
427
428         return 0;
429 }
430
431 static void eth_dev_remove(struct connman_device *device)
432 {
433         struct ethernet_data *ethernet = connman_device_get_data(device);
434
435         DBG("device %p", device);
436
437         connman_device_set_data(device, NULL);
438
439 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
440         if (!ethernet)
441                return;
442
443         if (ethernet->interface) {
444                 g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
445                 ethernet->interface = NULL;
446         }
447 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
448
449         connman_rtnl_remove_watch(ethernet->watch);
450
451         remove_network(device, ethernet);
452
453         g_free(ethernet);
454 }
455
456 static int eth_dev_enable(struct connman_device *device)
457 {
458         struct ethernet_data *ethernet = connman_device_get_data(device);
459
460         DBG("device %p", device);
461
462         return connman_inet_ifup(ethernet->index);
463 }
464
465 static int eth_dev_disable(struct connman_device *device)
466 {
467         struct ethernet_data *ethernet = connman_device_get_data(device);
468
469         DBG("device %p", device);
470
471         return connman_inet_ifdown(ethernet->index);
472 }
473
474 static struct connman_device_driver eth_dev_driver = {
475         .name           = "ethernet",
476         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
477         .probe          = eth_dev_probe,
478         .remove         = eth_dev_remove,
479         .enable         = eth_dev_enable,
480         .disable        = eth_dev_disable,
481 };
482
483 static int eth_tech_probe(struct connman_technology *technology)
484 {
485         return 0;
486 }
487
488 static void eth_tech_remove(struct connman_technology *technology)
489 {
490         DBG("");
491 }
492
493 static GList *eth_interface_list = NULL;
494
495 static void eth_tech_add_interface(struct connman_technology *technology,
496                         int index, const char *name, const char *ident)
497 {
498         DBG("index %d name %s ident %s", index, name, ident);
499
500         if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index)))
501                 return;
502
503         eth_interface_list = g_list_prepend(eth_interface_list,
504                                         (GINT_TO_POINTER((int) index)));
505 }
506
507 static void eth_tech_remove_interface(struct connman_technology *technology,
508                                                                 int index)
509 {
510         DBG("index %d", index);
511
512         eth_interface_list = g_list_remove(eth_interface_list,
513                                         GINT_TO_POINTER((int) index));
514 }
515
516 static void eth_tech_enable_tethering(struct connman_technology *technology,
517                                                 const char *bridge)
518 {
519         GList *list;
520         struct ethernet_data *ethernet;
521
522         for (list = eth_interface_list; list; list = list->next) {
523                 int index = GPOINTER_TO_INT(list->data);
524                 struct connman_device *device =
525                         connman_device_find_by_index(index);
526
527                 if (device) {
528                         ethernet = connman_device_get_data(device);
529                         if (ethernet)
530                                 remove_network(device, ethernet);
531                 }
532
533                 connman_technology_tethering_notify(technology, true);
534
535                 connman_inet_ifup(index);
536
537                 connman_inet_add_to_bridge(index, bridge);
538
539                 eth_tethering = true;
540         }
541 }
542
543 static void eth_tech_disable_tethering(struct connman_technology *technology,
544                                                 const char *bridge)
545 {
546         GList *list;
547
548         for (list = eth_interface_list; list; list = list->next) {
549                 int index = GPOINTER_TO_INT(list->data);
550                 struct connman_device *device =
551                         connman_device_find_by_index(index);
552
553                 connman_inet_remove_from_bridge(index, bridge);
554
555                 connman_technology_tethering_notify(technology, false);
556
557                 if (device)
558                         connman_device_reconnect_service(device);
559
560                 eth_tethering = false;
561         }
562 }
563
564 static int eth_tech_set_tethering(struct connman_technology *technology,
565                                 const char *identifier, const char *passphrase,
566                                 const char *bridge, bool enabled)
567 {
568         if (!connman_technology_is_tethering_allowed(
569                         CONNMAN_SERVICE_TYPE_ETHERNET))
570                 return 0;
571
572         DBG("bridge %s enabled %d", bridge, enabled);
573
574         if (enabled)
575                 eth_tech_enable_tethering(technology, bridge);
576         else
577                 eth_tech_disable_tethering(technology, bridge);
578
579         return 0;
580 }
581
582 static struct connman_technology_driver eth_tech_driver = {
583         .name                   = "ethernet",
584         .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
585         .probe                  = eth_tech_probe,
586         .remove                 = eth_tech_remove,
587         .add_interface          = eth_tech_add_interface,
588         .remove_interface       = eth_tech_remove_interface,
589         .set_tethering          = eth_tech_set_tethering,
590 };
591
592 #if defined TIZEN_EXT_WIFI_MESH
593 static int eth_mesh_add_to_bridge(const char *bridge)
594 {
595         GList *list;
596         struct ethernet_data *ethernet;
597
598         DBG("Add ethernet to bridge %s", bridge);
599
600         for (list = eth_interface_list; list; list = list->next) {
601                 int index = GPOINTER_TO_INT(list->data);
602                 struct connman_device *device =
603                         connman_device_find_by_index(index);
604
605                 if (device) {
606                         ethernet = connman_device_get_data(device);
607                         if (ethernet)
608                                 remove_network(device, ethernet);
609                 }
610
611                 connman_inet_ifup(index);
612
613                 connman_inet_add_to_bridge(index, bridge);
614         }
615
616         return 0;
617 }
618
619 static int eth_mesh_remove_from_bridge(const char *bridge)
620 {
621         GList *list;
622
623         DBG("Remove ethernet from bridge %s", bridge);
624
625         for (list = eth_interface_list; list; list = list->next) {
626                 int index = GPOINTER_TO_INT(list->data);
627
628                 connman_inet_remove_from_bridge(index, bridge);
629         }
630
631         return 0;
632 }
633
634 static struct connman_mesh_eth_driver eth_mesh_driver = {
635         .add_to_bridge          = eth_mesh_add_to_bridge,
636         .remove_from_bridge     = eth_mesh_remove_from_bridge,
637 };
638 #endif
639
640 static int ethernet_init(void)
641 {
642         int err;
643
644         err = connman_technology_driver_register(&eth_tech_driver);
645         if (err < 0)
646                 return err;
647
648 #if defined TIZEN_EXT_WIFI_MESH
649         err = connman_mesh_eth_driver_register(&eth_mesh_driver);
650         if (err < 0)
651                 return err;
652 #endif
653
654         err = connman_network_driver_register(&eth_network_driver);
655         if (err < 0)
656                 return err;
657
658         err = connman_device_driver_register(&eth_dev_driver);
659         if (err < 0) {
660                 connman_network_driver_unregister(&eth_network_driver);
661                 return err;
662         }
663
664         return 0;
665 }
666
667 static void ethernet_exit(void)
668 {
669         connman_technology_driver_unregister(&eth_tech_driver);
670
671 #if defined TIZEN_EXT_WIFI_MESH
672         connman_mesh_eth_driver_unregister(&eth_mesh_driver);
673 #endif
674
675         connman_network_driver_unregister(&eth_network_driver);
676
677         connman_device_driver_unregister(&eth_dev_driver);
678 }
679
680 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
681                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)