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