Merge "default_changed: get default service after checking service list" into tizen
[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                         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 && ethernet->interface) {
441                 g_supplicant_interface_remove(ethernet->interface, NULL, NULL);
442                 ethernet->interface = NULL;
443         }
444 #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */
445
446         connman_rtnl_remove_watch(ethernet->watch);
447
448         remove_network(device, ethernet);
449
450         g_free(ethernet);
451 }
452
453 static int eth_dev_enable(struct connman_device *device)
454 {
455         struct ethernet_data *ethernet = connman_device_get_data(device);
456
457         DBG("device %p", device);
458
459         return connman_inet_ifup(ethernet->index);
460 }
461
462 static int eth_dev_disable(struct connman_device *device)
463 {
464         struct ethernet_data *ethernet = connman_device_get_data(device);
465
466         DBG("device %p", device);
467
468         return connman_inet_ifdown(ethernet->index);
469 }
470
471 static struct connman_device_driver eth_dev_driver = {
472         .name           = "ethernet",
473         .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
474         .probe          = eth_dev_probe,
475         .remove         = eth_dev_remove,
476         .enable         = eth_dev_enable,
477         .disable        = eth_dev_disable,
478 };
479
480 static int eth_tech_probe(struct connman_technology *technology)
481 {
482         return 0;
483 }
484
485 static void eth_tech_remove(struct connman_technology *technology)
486 {
487         DBG("");
488 }
489
490 static GList *eth_interface_list = NULL;
491
492 static void eth_tech_add_interface(struct connman_technology *technology,
493                         int index, const char *name, const char *ident)
494 {
495         DBG("index %d name %s ident %s", index, name, ident);
496
497         if (g_list_find(eth_interface_list, GINT_TO_POINTER((int)index)))
498                 return;
499
500         eth_interface_list = g_list_prepend(eth_interface_list,
501                                         (GINT_TO_POINTER((int) index)));
502 }
503
504 static void eth_tech_remove_interface(struct connman_technology *technology,
505                                                                 int index)
506 {
507         DBG("index %d", index);
508
509         eth_interface_list = g_list_remove(eth_interface_list,
510                                         GINT_TO_POINTER((int) index));
511 }
512
513 static void eth_tech_enable_tethering(struct connman_technology *technology,
514                                                 const char *bridge)
515 {
516         GList *list;
517         struct ethernet_data *ethernet;
518
519         for (list = eth_interface_list; list; list = list->next) {
520                 int index = GPOINTER_TO_INT(list->data);
521                 struct connman_device *device =
522                         connman_device_find_by_index(index);
523
524                 if (device) {
525                         ethernet = connman_device_get_data(device);
526                         if (ethernet)
527                                 remove_network(device, ethernet);
528                 }
529
530                 connman_technology_tethering_notify(technology, true);
531
532                 connman_inet_ifup(index);
533
534                 connman_inet_add_to_bridge(index, bridge);
535
536                 eth_tethering = true;
537         }
538 }
539
540 static void eth_tech_disable_tethering(struct connman_technology *technology,
541                                                 const char *bridge)
542 {
543         GList *list;
544
545         for (list = eth_interface_list; list; list = list->next) {
546                 int index = GPOINTER_TO_INT(list->data);
547                 struct connman_device *device =
548                         connman_device_find_by_index(index);
549
550                 connman_inet_remove_from_bridge(index, bridge);
551
552                 connman_technology_tethering_notify(technology, false);
553
554                 if (device)
555                         connman_device_reconnect_service(device);
556
557                 eth_tethering = false;
558         }
559 }
560
561 static int eth_tech_set_tethering(struct connman_technology *technology,
562                                 const char *identifier, const char *passphrase,
563                                 const char *bridge, bool enabled)
564 {
565         if (!connman_technology_is_tethering_allowed(
566                         CONNMAN_SERVICE_TYPE_ETHERNET))
567                 return 0;
568
569         DBG("bridge %s enabled %d", bridge, enabled);
570
571         if (enabled)
572                 eth_tech_enable_tethering(technology, bridge);
573         else
574                 eth_tech_disable_tethering(technology, bridge);
575
576         return 0;
577 }
578
579 static struct connman_technology_driver eth_tech_driver = {
580         .name                   = "ethernet",
581         .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
582         .probe                  = eth_tech_probe,
583         .remove                 = eth_tech_remove,
584         .add_interface          = eth_tech_add_interface,
585         .remove_interface       = eth_tech_remove_interface,
586         .set_tethering          = eth_tech_set_tethering,
587 };
588
589 #if defined TIZEN_EXT_WIFI_MESH
590 static int eth_mesh_add_to_bridge(const char *bridge)
591 {
592         GList *list;
593         struct ethernet_data *ethernet;
594
595         DBG("Add ethernet to bridge %s", bridge);
596
597         for (list = eth_interface_list; list; list = list->next) {
598                 int index = GPOINTER_TO_INT(list->data);
599                 struct connman_device *device =
600                         connman_device_find_by_index(index);
601
602                 if (device) {
603                         ethernet = connman_device_get_data(device);
604                         if (ethernet)
605                                 remove_network(device, ethernet);
606                 }
607
608                 connman_inet_ifup(index);
609
610                 connman_inet_add_to_bridge(index, bridge);
611         }
612
613         return 0;
614 }
615
616 static int eth_mesh_remove_from_bridge(const char *bridge)
617 {
618         GList *list;
619
620         DBG("Remove ethernet from bridge %s", bridge);
621
622         for (list = eth_interface_list; list; list = list->next) {
623                 int index = GPOINTER_TO_INT(list->data);
624
625                 connman_inet_remove_from_bridge(index, bridge);
626         }
627
628         return 0;
629 }
630
631 static struct connman_mesh_eth_driver eth_mesh_driver = {
632         .add_to_bridge          = eth_mesh_add_to_bridge,
633         .remove_from_bridge     = eth_mesh_remove_from_bridge,
634 };
635 #endif
636
637 static int ethernet_init(void)
638 {
639         int err;
640
641         err = connman_technology_driver_register(&eth_tech_driver);
642         if (err < 0)
643                 return err;
644
645 #if defined TIZEN_EXT_WIFI_MESH
646         err = connman_mesh_eth_driver_register(&eth_mesh_driver);
647         if (err < 0)
648                 return err;
649 #endif
650
651         err = connman_network_driver_register(&eth_network_driver);
652         if (err < 0)
653                 return err;
654
655         err = connman_device_driver_register(&eth_dev_driver);
656         if (err < 0) {
657                 connman_network_driver_unregister(&eth_network_driver);
658                 return err;
659         }
660
661         return 0;
662 }
663
664 static void ethernet_exit(void)
665 {
666         connman_technology_driver_unregister(&eth_tech_driver);
667
668 #if defined TIZEN_EXT_WIFI_MESH
669         connman_mesh_eth_driver_unregister(&eth_mesh_driver);
670 #endif
671
672         connman_network_driver_unregister(&eth_network_driver);
673
674         connman_device_driver_unregister(&eth_dev_driver);
675 }
676
677 CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
678                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)