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