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