Let the core handle the children unregister task
[framework/connectivity/connman.git] / plugins / ethernet.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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 <unistd.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <linux/if.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
33
34 #include <connman/plugin.h>
35 #include <connman/driver.h>
36 #include <connman/log.h>
37
38 static GStaticMutex ethernet_mutex = G_STATIC_MUTEX_INIT;
39 static GSList *ethernet_list = NULL;
40
41 static void create_element(struct connman_element *parent,
42                                         enum connman_element_type type)
43 {
44         struct connman_element *element;
45
46         DBG("parent %p name %s", parent, parent->name);
47
48         element = connman_element_create();
49
50         element->type = type;
51         element->netdev.index = parent->netdev.index;
52         element->netdev.name = g_strdup(parent->netdev.name);
53
54         connman_element_register(element, parent);
55 }
56
57 static void rtnl_link(struct nlmsghdr *hdr, const char *type)
58 {
59         GSList *list;
60         struct ifinfomsg *msg;
61         int bytes;
62
63         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
64         bytes = IFLA_PAYLOAD(hdr);
65
66         DBG("%s ifi_index %d ifi_flags 0x%04x",
67                                 type, msg->ifi_index, msg->ifi_flags);
68
69         g_static_mutex_lock(&ethernet_mutex);
70
71         for (list = ethernet_list; list; list = list->next) {
72                 struct connman_element *element = list->data;
73
74                 if (element->type != CONNMAN_ELEMENT_TYPE_DEVICE)
75                         continue;
76
77                 if (element->netdev.index != msg->ifi_index)
78                         continue;
79
80                 if ((element->netdev.flags & IFF_RUNNING) ==
81                                                 (msg->ifi_flags & IFF_RUNNING))
82                         continue;
83
84                 element->netdev.flags = msg->ifi_flags;
85
86                 if (msg->ifi_flags & IFF_RUNNING) {
87                         DBG("carrier on");
88
89                         create_element(element, CONNMAN_ELEMENT_TYPE_DHCP);
90                         create_element(element, CONNMAN_ELEMENT_TYPE_ZEROCONF);
91                 } else {
92                         DBG("carrier off");
93
94                         connman_element_unregister_children(element);
95                 }
96         }
97
98         g_static_mutex_unlock(&ethernet_mutex);
99 }
100
101 static gboolean rtnl_event(GIOChannel *chan, GIOCondition cond, gpointer data)
102 {
103         unsigned char buf[1024];
104         void *ptr = buf;
105         gsize len;
106         GIOError err;
107
108         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
109                 return FALSE;
110
111         memset(buf, 0, sizeof(buf));
112
113         err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
114         if (err) {
115                 if (err == G_IO_ERROR_AGAIN)
116                         return TRUE;
117                 return FALSE;
118         }
119
120         DBG("buf %p len %zd", buf, len);
121
122         while (len > 0) {
123                 struct nlmsghdr *hdr = ptr;
124                 struct nlmsgerr *err;
125
126                 if (!NLMSG_OK(hdr, len))
127                         break;
128
129                 DBG("len %d type %d flags 0x%04x seq %d",
130                                         hdr->nlmsg_len, hdr->nlmsg_type,
131                                         hdr->nlmsg_flags, hdr->nlmsg_seq);
132
133                 switch (hdr->nlmsg_type) {
134                 case NLMSG_ERROR:
135                         err = NLMSG_DATA(hdr);
136                         DBG("ERROR %d (%s)", -err->error,
137                                                 strerror(-err->error));
138                         break;
139
140                 case RTM_NEWLINK:
141                         rtnl_link(hdr, "NEWLINK");
142                         break;
143
144                 case RTM_DELLINK:
145                         rtnl_link(hdr, "DELLINK");
146                         break;
147                 }
148
149                 len -= hdr->nlmsg_len;
150                 ptr += hdr->nlmsg_len;
151         }
152
153         return TRUE;
154 }
155
156 static GIOChannel *channel = NULL;
157
158 static int rtnl_request(void)
159 {
160         struct {
161                 struct nlmsghdr hdr;
162                 struct rtgenmsg msg;
163         } req;
164
165         struct sockaddr_nl addr;
166         int sk;
167
168         DBG("");
169
170         memset(&req, 0, sizeof(req));
171         req.hdr.nlmsg_len = sizeof(req.hdr) + sizeof(req.msg);
172         req.hdr.nlmsg_type = RTM_GETLINK;
173         req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
174         req.hdr.nlmsg_pid = 0;
175         req.hdr.nlmsg_seq = 42;
176         req.msg.rtgen_family = AF_INET;
177
178         sk = g_io_channel_unix_get_fd(channel);
179
180         memset(&addr, 0, sizeof(addr));
181         addr.nl_family = AF_NETLINK;
182
183         return sendto(sk, &req, sizeof(req), 0,
184                         (struct sockaddr *) &addr, sizeof(addr));
185 }
186
187 static int iface_up(struct connman_element *element)
188 {
189         struct ifreq ifr;
190         int sk, err;
191
192         DBG("element %p", element);
193
194         sk = socket(PF_INET, SOCK_DGRAM, 0);
195         if (sk < 0)
196                 return -errno;
197
198         memset(&ifr, 0, sizeof(ifr));
199         ifr.ifr_ifindex = element->netdev.index;
200
201         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
202                 err = -errno;
203                 goto done;
204         }
205
206         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
207                 err = -errno;
208                 goto done;
209         }
210
211         if (ifr.ifr_flags & IFF_UP) {
212                 err = -EALREADY;
213                 goto done;
214         }
215
216         ifr.ifr_flags |= IFF_UP;
217
218         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
219                 err = -errno;
220                 goto done;
221         }
222
223         err = 0;
224
225 done:
226         close(sk);
227
228         return err;
229 }
230
231 static int iface_down(struct connman_element *element)
232 {
233         struct ifreq ifr;
234         int sk, err;
235
236         DBG("element %p", element);
237
238         sk = socket(PF_INET, SOCK_DGRAM, 0);
239         if (sk < 0)
240                 return -errno;
241
242         memset(&ifr, 0, sizeof(ifr));
243         ifr.ifr_ifindex = element->netdev.index;
244
245         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
246                 err = -errno;
247                 goto done;
248         }
249
250         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
251                 err = -errno;
252                 goto done;
253         }
254
255         if (!(ifr.ifr_flags & IFF_UP)) {
256                 err = -EALREADY;
257                 goto done;
258         }
259
260         ifr.ifr_flags &= ~IFF_UP;
261
262         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
263                 err = -errno;
264         else
265                 err = 0;
266
267 done:
268         close(sk);
269
270         return err;
271 }
272
273 static int ethernet_probe(struct connman_element *element)
274 {
275         DBG("element %p name %s", element, element->name);
276
277         g_static_mutex_lock(&ethernet_mutex);
278         ethernet_list = g_slist_append(ethernet_list, element);
279         g_static_mutex_unlock(&ethernet_mutex);
280
281         iface_up(element);
282
283         rtnl_request();
284
285         return 0;
286 }
287
288 static void ethernet_remove(struct connman_element *element)
289 {
290         DBG("element %p name %s", element, element->name);
291
292         iface_down(element);
293
294         g_static_mutex_lock(&ethernet_mutex);
295         ethernet_list = g_slist_remove(ethernet_list, element);
296         g_static_mutex_unlock(&ethernet_mutex);
297 }
298
299 static struct connman_driver ethernet_driver = {
300         .name           = "ethernet",
301         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
302         .subtype        = CONNMAN_ELEMENT_SUBTYPE_ETHERNET,
303         .probe          = ethernet_probe,
304         .remove         = ethernet_remove,
305 };
306
307 static int rtnl_init(void)
308 {
309         struct sockaddr_nl addr;
310         int sk, err;
311
312         DBG("");
313
314         sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
315         if (sk < 0)
316                 return -errno;
317
318         memset(&addr, 0, sizeof(addr));
319         addr.nl_family = AF_NETLINK;
320         addr.nl_groups = RTMGRP_LINK;
321
322         if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
323                 err = -errno;
324                 close(sk);
325                 return err;
326         }
327
328         channel = g_io_channel_unix_new(sk);
329         g_io_channel_set_close_on_unref(channel, TRUE);
330
331         g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
332                                                         rtnl_event, NULL);
333
334         return 0;
335 }
336
337 static void rtnl_cleanup(void)
338 {
339         DBG("");
340
341         g_io_channel_shutdown(channel, TRUE, NULL);
342         g_io_channel_unref(channel);
343
344         channel = NULL;
345 }
346
347 static int ethernet_init(void)
348 {
349         int err;
350
351         err = rtnl_init();
352         if (err < 0)
353                 return err;
354
355         err = connman_driver_register(&ethernet_driver);
356         if (err < 0) {
357                 rtnl_cleanup();
358                 return err;
359         }
360
361         return 0;
362 }
363
364 static void ethernet_exit(void)
365 {
366         connman_driver_unregister(&ethernet_driver);
367
368         rtnl_cleanup();
369 }
370
371 CONNMAN_PLUGIN_DEFINE("ethernet", "Ethernet interface plugin", VERSION,
372                                                 ethernet_init, ethernet_exit)