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