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