Add callback for wireless netlink events
[connman] / src / rtnl.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31
32 #include <linux/netlink.h>
33 #include <linux/rtnetlink.h>
34
35 #include <glib.h>
36
37 #include "connman.h"
38
39 static inline void print_char(struct rtattr *attr, const char *name)
40 {
41         printf("  attr %s (len %d) %s", name, RTA_PAYLOAD(attr),
42                                                 (char *) RTA_DATA(attr));
43 }
44
45 static inline void print_attr(struct rtattr *attr, const char *name)
46 {
47         if (name)
48                 printf("  attr %s (len %d)", name, RTA_PAYLOAD(attr));
49         else
50                 printf("  attr %d (len %d)", attr->rta_type, RTA_PAYLOAD(attr));
51 }
52
53 static void rtnl_link(struct nlmsghdr *hdr)
54 {
55         struct connman_iface *iface;
56         struct ifinfomsg *msg;
57         struct rtattr *attr;
58         int bytes;
59
60         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
61         bytes = IFLA_PAYLOAD(hdr);
62
63         DBG("ifi_index %d ifi_flags %d", msg->ifi_index, msg->ifi_flags);
64
65         iface = __connman_iface_find(msg->ifi_index);
66         if (iface == NULL)
67                 return;
68
69         if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
70                 return;
71
72         for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
73                                         attr = RTA_NEXT(attr, bytes)) {
74                 switch (attr->rta_type) {
75                 case IFLA_ADDRESS:
76                         print_attr(attr, "address");
77                         break;
78                 case IFLA_BROADCAST:
79                         print_attr(attr, "broadcast");
80                         break;
81                 case IFLA_IFNAME:
82                         print_char(attr, "ifname");
83                         break;
84                 case IFLA_MTU:
85                         print_attr(attr, "mtu");
86                         break;
87                 case IFLA_LINK:
88                         print_attr(attr, "link");
89                         break;
90                 case IFLA_QDISC:
91                         print_attr(attr, "qdisc");
92                         break;
93                 case IFLA_STATS:
94                         print_attr(attr, "stats");
95                         break;
96                 case IFLA_COST:
97                         print_attr(attr, "cost");
98                         break;
99                 case IFLA_PRIORITY:
100                         print_attr(attr, "priority");
101                         break;
102                 case IFLA_MASTER:
103                         print_attr(attr, "master");
104                         break;
105                 case IFLA_WIRELESS:
106                         if (iface->driver->rtnl_wireless)
107                                 iface->driver->rtnl_wireless(iface,
108                                         RTA_DATA(attr), RTA_PAYLOAD(attr));
109                         break;
110                 case IFLA_PROTINFO:
111                         print_attr(attr, "protinfo");
112                         break;
113                 case IFLA_TXQLEN:
114                         print_attr(attr, "txqlen");
115                         break;
116                 case IFLA_MAP:
117                         print_attr(attr, "map");
118                         break;
119                 case IFLA_WEIGHT:
120                         print_attr(attr, "weight");
121                         break;
122                 case IFLA_OPERSTATE:
123                         print_attr(attr, "operstate");
124                         break;
125                 case IFLA_LINKMODE:
126                         print_attr(attr, "linkmode");
127                         break;
128                 default:
129                         print_attr(attr, NULL);
130                         break;
131                 }
132         }
133 }
134
135 static void rtnl_addr(struct nlmsghdr *hdr)
136 {
137         struct connman_iface *iface;
138         struct ifaddrmsg *msg;
139         struct rtattr *attr;
140         int bytes;
141
142         msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
143         bytes = IFA_PAYLOAD(hdr);
144
145         DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
146
147         iface = __connman_iface_find(msg->ifa_index);
148         if (iface == NULL)
149                 return;
150
151         if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
152                 return;
153
154         for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
155                                         attr = RTA_NEXT(attr, bytes)) {
156                 switch (attr->rta_type) {
157                 case IFA_ADDRESS:
158                         print_attr(attr, "address");
159                         if (msg->ifa_family == AF_INET) {
160                                 struct in_addr addr;
161                                 addr = *((struct in_addr *) RTA_DATA(attr));
162                                 DBG("    address %s", inet_ntoa(addr));
163                         }
164                         break;
165                 case IFA_LOCAL:
166                         print_attr(attr, "local");
167                         if (msg->ifa_family == AF_INET) {
168                                 struct in_addr addr;
169                                 addr = *((struct in_addr *) RTA_DATA(attr));
170                                 DBG("    address %s", inet_ntoa(addr));
171                         }
172                         break;
173                 case IFA_LABEL:
174                         print_char(attr, "label");
175                         break;
176                 case IFA_BROADCAST:
177                         print_attr(attr, "broadcast");
178                         if (msg->ifa_family == AF_INET) {
179                                 struct in_addr addr;
180                                 addr = *((struct in_addr *) RTA_DATA(attr));
181                                 DBG("    address %s", inet_ntoa(addr));
182                         }
183                         break;
184                 case IFA_ANYCAST:
185                         print_attr(attr, "anycast");
186                         break;
187                 case IFA_CACHEINFO:
188                         print_attr(attr, "cacheinfo");
189                         break;
190                 case IFA_MULTICAST:
191                         print_attr(attr, "multicast");
192                         break;
193                 default:
194                         print_attr(attr, NULL);
195                         break;
196                 }
197         }
198 }
199
200 static void rtnl_route(struct nlmsghdr *hdr)
201 {
202         struct rtmsg *msg;
203         struct rtattr *attr;
204         int bytes;
205
206         msg = (struct rtmsg *) NLMSG_DATA(hdr);
207         bytes = RTM_PAYLOAD(hdr);
208
209         DBG("rtm_family %d rtm_flags %d", msg->rtm_family, msg->rtm_flags);
210
211         for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
212                                         attr = RTA_NEXT(attr, bytes)) {
213                 switch (attr->rta_type) {
214                 case RTA_DST:
215                         print_attr(attr, "dst");
216                         if (msg->rtm_family == AF_INET) {
217                                 struct in_addr addr;
218                                 addr = *((struct in_addr *) RTA_DATA(attr));
219                                 DBG("    address %s", inet_ntoa(addr));
220                         }
221                         break;
222                 case RTA_SRC:
223                         print_attr(attr, "src");
224                         if (msg->rtm_family == AF_INET) {
225                                 struct in_addr addr;
226                                 addr = *((struct in_addr *) RTA_DATA(attr));
227                                 DBG("    address %s", inet_ntoa(addr));
228                         }
229                         break;
230                 case RTA_IIF:
231                         print_char(attr, "iif");
232                         break;
233                 case RTA_OIF:
234                         print_attr(attr, "oif");
235                         break;
236                 case RTA_GATEWAY:
237                         print_attr(attr, "gateway");
238                         if (msg->rtm_family == AF_INET) {
239                                 struct in_addr addr;
240                                 addr = *((struct in_addr *) RTA_DATA(attr));
241                                 DBG("    address %s", inet_ntoa(addr));
242                         }
243                         break;
244                 case RTA_PRIORITY:
245                         print_attr(attr, "priority");
246                         break;
247                 case RTA_PREFSRC:
248                         print_attr(attr, "prefsrc");
249                         if (msg->rtm_family == AF_INET) {
250                                 struct in_addr addr;
251                                 addr = *((struct in_addr *) RTA_DATA(attr));
252                                 DBG("    address %s", inet_ntoa(addr));
253                         }
254                         break;
255                 case RTA_METRICS:
256                         print_attr(attr, "metrics");
257                         break;
258                 case RTA_TABLE:
259                         print_attr(attr, "table");
260                         break;
261                 default:
262                         print_attr(attr, NULL);
263                         break;
264                 }
265         }
266 }
267
268 static void rtnl_message(unsigned char *buf, size_t size)
269 {
270         struct nlmsghdr *hdr = (void *) buf;
271
272         if (!NLMSG_OK(hdr, size))
273                 return;
274
275         switch (hdr->nlmsg_type) {
276         case NLMSG_DONE:
277                 DBG("done");
278                 return;
279         case NLMSG_NOOP:
280                 DBG("noop");
281                 return;
282         case NLMSG_OVERRUN:
283                 DBG("overrun");
284                 return;
285         case NLMSG_ERROR:
286                 DBG("error");
287                 return;
288         case RTM_NEWLINK:
289                 rtnl_link(hdr);
290                 break;
291         case RTM_DELLINK:
292                 rtnl_link(hdr);
293                 break;
294         case RTM_NEWADDR:
295                 rtnl_addr(hdr);
296                 break;
297         case RTM_DELADDR:
298                 rtnl_addr(hdr);
299                 break;
300         case RTM_NEWROUTE:
301                 rtnl_route(hdr);
302                 break;
303         case RTM_DELROUTE:
304                 rtnl_route(hdr);
305                 break;
306         default:
307                 DBG("type %d", hdr->nlmsg_type);
308                 break;
309         }
310 }
311
312 static gboolean netlink_event(GIOChannel *chan,
313                                 GIOCondition cond, gpointer data)
314 {
315         unsigned char buf[256];
316         gsize len;
317         GIOError err;
318
319         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
320                 g_io_channel_unref(chan);
321                 return FALSE;
322         }
323
324         memset(buf, 0, sizeof(buf));
325
326         err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
327         if (err) {
328                 if (err == G_IO_ERROR_AGAIN)
329                         return TRUE;
330                 g_io_channel_unref(chan);
331                 return FALSE;
332         }
333
334         rtnl_message(buf, len);
335
336         return TRUE;
337 }
338
339 static GIOChannel *channel = NULL;
340
341 int __connman_rtnl_init(void)
342 {
343         struct sockaddr_nl addr;
344         int sk;
345
346         DBG("");
347
348         sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
349         if (sk < 0)
350                 return -1;
351
352         memset(&addr, 0, sizeof(addr));
353         addr.nl_family = AF_NETLINK;
354         addr.nl_groups = RTMGRP_LINK;
355         //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
356         addr.nl_pid = getpid();
357
358         if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
359                 close(sk);
360                 return -1;
361         }
362
363         channel = g_io_channel_unix_new(sk);
364         g_io_channel_set_close_on_unref(channel, TRUE);
365
366         g_io_add_watch(channel,
367                         G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
368                                                 netlink_event, NULL);
369
370         g_io_channel_unref(channel);
371
372         return 0;
373 }
374
375 void __connman_rtnl_cleanup(void)
376 {
377         DBG("");
378
379         g_io_channel_unref(channel);
380
381         channel = NULL;
382 }