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