Update EAPoL property in connman settings.
[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, "/opt/usr/data/network/%s-eapol.conf", ifname) < 0)
225                         return -ENOMEM;
226
227                 DBG("config_file %s", config_file);
228
229                 g_supplicant_replace_config_file(ifname, config_file);
230                 free(config_file);
231
232                 /*
233                  *  TODO: RemoveInterface if already present because
234                  *  already created interface will not start EAP handshake.
235                  */
236                 g_supplicant_interface_create(ifname, driver, NULL,
237                                 interface_create_callback, ethernet);
238
239                 g_free(ifname);
240
241                 return 0;
242         }
243
244         connman_network_set_connected(network, true);
245
246         return 0;
247 }
248
249 static int eth_network_disconnect(struct connman_network *network)
250 {
251         DBG("network %p", network);
252
253         struct connman_service *service = connman_service_lookup_from_network(network);
254
255         if (service && __connman_service_get_use_eapol(service)) {
256                 struct connman_device *device = connman_network_get_device(network);
257                 struct ethernet_data *ethernet = connman_device_get_data(device);
258
259                 g_network = NULL;
260                 g_supplicant_unregister_eap_callback();
261                 if (ethernet && ethernet->interface) {
262                         g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
263                         ethernet->interface = NULL;
264                 }
265                 connman_network_set_associating(network, false);
266                 connman_network_set_connected(network, false);
267
268                 return 0;
269         }
270
271         connman_network_set_connected(network, false);
272
273         return 0;
274 }
275
276 #else /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
277
278 static int eth_network_connect(struct connman_network *network)
279 {
280         DBG("network %p", network);
281
282         connman_network_set_connected(network, true);
283
284         return 0;
285 }
286
287 static int eth_network_disconnect(struct connman_network *network)
288 {
289         DBG("network %p", network);
290
291         connman_network_set_connected(network, false);
292
293         return 0;
294 }
295
296 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
297
298 static struct connman_network_driver eth_network_driver = {
299         .name           = "cable",
300         .type           = CONNMAN_NETWORK_TYPE_ETHERNET,
301         .probe          = eth_network_probe,
302         .remove         = eth_network_remove,
303         .connect        = eth_network_connect,
304         .disconnect     = eth_network_disconnect,
305 };
306
307 static void add_network(struct connman_device *device,
308                         struct ethernet_data *ethernet)
309 {
310         struct connman_network *network;
311         int index;
312         char *ifname;
313
314         network = connman_network_create("carrier",
315                                         CONNMAN_NETWORK_TYPE_ETHERNET);
316         if (!network)
317                 return;
318
319         index = connman_device_get_index(device);
320         connman_network_set_index(network, index);
321         ifname = connman_inet_ifname(index);
322         if (!ifname)
323                 return;
324
325         connman_network_set_name(network, "Wired");
326
327         if (connman_device_add_network(device, network) < 0) {
328                 connman_network_unref(network);
329                 g_free(ifname);
330                 return;
331         }
332
333         if (!eth_tethering) {
334                 char group[25] = "cable";
335                 int vid, dsaport;
336
337                 vid = get_vlan_vid(ifname);
338                 dsaport = get_dsa_port(ifname);
339
340                 /*
341                  * Prevent service from starting the reconnect
342                  * procedure as we do not want the DHCP client
343                  * to run when tethering.
344                  */
345                 if((vid >= 0) && (dsaport >= 0))
346                         snprintf(group, sizeof(group), "p%02x_%03x_cable", dsaport, vid);
347                 else if (vid >= 0)
348                         snprintf(group, sizeof(group), "%03x_cable", vid);
349                 else if (dsaport >= 0)
350                         snprintf(group, sizeof(group), "p%02x_cable", dsaport);
351
352                 connman_network_set_group(network, group);
353         }
354
355         ethernet->network = network;
356         g_free(ifname);
357 }
358
359 static void remove_network(struct connman_device *device,
360                                 struct ethernet_data *ethernet)
361 {
362         if (!ethernet->network)
363                 return;
364
365         connman_device_remove_network(device, ethernet->network);
366         connman_network_unref(ethernet->network);
367
368         ethernet->network = NULL;
369 }
370
371 static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
372 {
373         struct connman_device *device = user_data;
374         struct ethernet_data *ethernet = connman_device_get_data(device);
375
376         DBG("index %d flags %d change %d", ethernet->index, flags, change);
377
378         if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
379                 if (flags & IFF_UP) {
380                         DBG("power on");
381                         connman_device_set_powered(device, true);
382                 } else {
383                         DBG("power off");
384                         connman_device_set_powered(device, false);
385                 }
386         }
387
388         if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
389                 if (flags & IFF_LOWER_UP) {
390                         DBG("carrier on");
391                         add_network(device, ethernet);
392                 } else {
393                         DBG("carrier off");
394                         remove_network(device, ethernet);
395 #if defined TIZEN_EXT_WIFI_MESH
396                         /* Remove ethernet from mesh bridge */
397                         __connman_mesh_remove_ethernet_from_bridge();
398 #endif
399                 }
400         }
401
402         ethernet->flags = flags;
403 }
404
405 static int eth_dev_probe(struct connman_device *device)
406 {
407         struct ethernet_data *ethernet;
408
409         DBG("device %p", device);
410
411         ethernet = g_try_new0(struct ethernet_data, 1);
412         if (!ethernet)
413                 return -ENOMEM;
414
415         connman_device_set_data(device, ethernet);
416
417         ethernet->index = connman_device_get_index(device);
418         ethernet->flags = 0;
419 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
420         ethernet->interface = NULL;
421 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
422
423         ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
424                                                 ethernet_newlink, device);
425
426         return 0;
427 }
428
429 static void eth_dev_remove(struct connman_device *device)
430 {
431         struct ethernet_data *ethernet = connman_device_get_data(device);
432
433         DBG("device %p", device);
434
435         connman_device_set_data(device, NULL);
436
437 #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET
438         if (ethernet && ethernet->interface) {
439                 g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
440                 ethernet->interface = NULL;
441         }
442 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
443
444         connman_rtnl_remove_watch(ethernet->watch);
445
446         remove_network(device, ethernet);
447
448         g_free(ethernet);
449 }
450
451 static int eth_dev_enable(struct connman_device *device)
452 {
453         struct ethernet_data *ethernet = connman_device_get_data(device);
454
455         DBG("device %p", device);
456
457         return connman_inet_ifup(ethernet->index);
458 }
459
460 static int eth_dev_disable(struct connman_device *device)
461 {
462         struct ethernet_data *ethernet = connman_device_get_data(device);
463
464         DBG("device %p", device);
465
466         return connman_inet_ifdown(ethernet->index);
467 }
468
469 static struct connman_device_driver eth_dev_driver = {
470         .name           = "ethernet",
471         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
472         .probe          = eth_dev_probe,
473         .remove         = eth_dev_remove,
474         .enable         = eth_dev_enable,
475         .disable        = eth_dev_disable,
476 };
477
478 static int eth_tech_probe(struct connman_technology *technology)
479 {
480         return 0;
481 }
482
483 static void eth_tech_remove(struct connman_technology *technology)
484 {
485         DBG("");
486 }
487
488 static GList *eth_interface_list = NULL;
489
490 static void eth_tech_add_interface(struct connman_technology *technology,
491                         int index, const char *name, const char *ident)
492 {
493         DBG("index %d name %s ident %s", index, name, ident);
494
495         if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index)))
496                 return;
497
498         eth_interface_list = g_list_prepend(eth_interface_list,
499                                         (GINT_TO_POINTER((int) index)));
500 }
501
502 static void eth_tech_remove_interface(struct connman_technology *technology,
503                                                                 int index)
504 {
505         DBG("index %d", index);
506
507         eth_interface_list = g_list_remove(eth_interface_list,
508                                         GINT_TO_POINTER((int) index));
509 }
510
511 static void eth_tech_enable_tethering(struct connman_technology *technology,
512                                                 const char *bridge)
513 {
514         GList *list;
515         struct ethernet_data *ethernet;
516
517         for (list = eth_interface_list; list; list = list->next) {
518                 int index = GPOINTER_TO_INT(list->data);
519                 struct connman_device *device =
520                         connman_device_find_by_index(index);
521
522                 if (device) {
523                         ethernet = connman_device_get_data(device);
524                         if (ethernet)
525                                 remove_network(device, ethernet);
526                 }
527
528                 connman_technology_tethering_notify(technology, true);
529
530                 connman_inet_ifup(index);
531
532                 connman_inet_add_to_bridge(index, bridge);
533
534                 eth_tethering = true;
535         }
536 }
537
538 static void eth_tech_disable_tethering(struct connman_technology *technology,
539                                                 const char *bridge)
540 {
541         GList *list;
542
543         for (list = eth_interface_list; list; list = list->next) {
544                 int index = GPOINTER_TO_INT(list->data);
545                 struct connman_device *device =
546                         connman_device_find_by_index(index);
547
548                 connman_inet_remove_from_bridge(index, bridge);
549
550                 connman_technology_tethering_notify(technology, false);
551
552                 if (device)
553                         connman_device_reconnect_service(device);
554
555                 eth_tethering = false;
556         }
557 }
558
559 static int eth_tech_set_tethering(struct connman_technology *technology,
560                                 const char *identifier, const char *passphrase,
561                                 const char *bridge, bool enabled)
562 {
563         if (!connman_technology_is_tethering_allowed(
564                         CONNMAN_SERVICE_TYPE_ETHERNET))
565                 return 0;
566
567         DBG("bridge %s enabled %d", bridge, enabled);
568
569         if (enabled)
570                 eth_tech_enable_tethering(technology, bridge);
571         else
572                 eth_tech_disable_tethering(technology, bridge);
573
574         return 0;
575 }
576
577 static struct connman_technology_driver eth_tech_driver = {
578         .name                   = "ethernet",
579         .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
580         .probe                  = eth_tech_probe,
581         .remove                 = eth_tech_remove,
582         .add_interface          = eth_tech_add_interface,
583         .remove_interface       = eth_tech_remove_interface,
584         .set_tethering          = eth_tech_set_tethering,
585 };
586
587 #if defined TIZEN_EXT_WIFI_MESH
588 static int eth_mesh_add_to_bridge(const char *bridge)
589 {
590         GList *list;
591         struct ethernet_data *ethernet;
592
593         DBG("Add ethernet to bridge %s", bridge);
594
595         for (list = eth_interface_list; list; list = list->next) {
596                 int index = GPOINTER_TO_INT(list->data);
597                 struct connman_device *device =
598                         connman_device_find_by_index(index);
599
600                 if (device) {
601                         ethernet = connman_device_get_data(device);
602                         if (ethernet)
603                                 remove_network(device, ethernet);
604                 }
605
606                 connman_inet_ifup(index);
607
608                 connman_inet_add_to_bridge(index, bridge);
609         }
610
611         return 0;
612 }
613
614 static int eth_mesh_remove_from_bridge(const char *bridge)
615 {
616         GList *list;
617
618         DBG("Remove ethernet from bridge %s", bridge);
619
620         for (list = eth_interface_list; list; list = list->next) {
621                 int index = GPOINTER_TO_INT(list->data);
622
623                 connman_inet_remove_from_bridge(index, bridge);
624         }
625
626         return 0;
627 }
628
629 static struct connman_mesh_eth_driver eth_mesh_driver = {
630         .add_to_bridge          = eth_mesh_add_to_bridge,
631         .remove_from_bridge     = eth_mesh_remove_from_bridge,
632 };
633 #endif
634
635 static int ethernet_init(void)
636 {
637         int err;
638
639         err = connman_technology_driver_register(&eth_tech_driver);
640         if (err < 0)
641                 return err;
642
643 #if defined TIZEN_EXT_WIFI_MESH
644         err = connman_mesh_eth_driver_register(&eth_mesh_driver);
645         if (err < 0)
646                 return err;
647 #endif
648
649         err = connman_network_driver_register(&eth_network_driver);
650         if (err < 0)
651                 return err;
652
653         err = connman_device_driver_register(&eth_dev_driver);
654         if (err < 0) {
655                 connman_network_driver_unregister(&eth_network_driver);
656                 return err;
657         }
658
659         return 0;
660 }
661
662 static void ethernet_exit(void)
663 {
664         connman_technology_driver_unregister(&eth_tech_driver);
665
666 #if defined TIZEN_EXT_WIFI_MESH
667         connman_mesh_eth_driver_unregister(&eth_mesh_driver);
668 #endif
669
670         connman_network_driver_unregister(&eth_network_driver);
671
672         connman_device_driver_unregister(&eth_dev_driver);
673 }
674
675 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
676                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)