Provide detailed device and network properties
[connman] / src / rtnl.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 <arpa/inet.h>
30
31 #include <linux/if.h>
32 #include <linux/netlink.h>
33 #include <linux/rtnetlink.h>
34
35 #include <glib.h>
36
37 #include "connman.h"
38
39 static GStaticRWLock rtnl_lock = G_STATIC_RW_LOCK_INIT;
40 static GSList *rtnl_list = NULL;
41
42 static gint compare_priority(gconstpointer a, gconstpointer b)
43 {
44         const struct connman_rtnl *rtnl1 = a;
45         const struct connman_rtnl *rtnl2 = b;
46
47         return rtnl2->priority - rtnl1->priority;
48 }
49
50 /**
51  * connman_rtnl_register:
52  * @rtnl: RTNL module
53  *
54  * Register a new RTNL module
55  *
56  * Returns: %0 on success
57  */
58 int connman_rtnl_register(struct connman_rtnl *rtnl)
59 {
60         DBG("rtnl %p name %s", rtnl, rtnl->name);
61
62         g_static_rw_lock_writer_lock(&rtnl_lock);
63
64         rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
65                                                         compare_priority);
66
67         g_static_rw_lock_writer_unlock(&rtnl_lock);
68
69         return 0;
70 }
71
72 /**
73  * connman_rtnl_unregister:
74  * @rtnl: RTNL module
75  *
76  * Remove a previously registered RTNL module
77  */
78 void connman_rtnl_unregister(struct connman_rtnl *rtnl)
79 {
80         DBG("rtnl %p name %s", rtnl, rtnl->name);
81
82         g_static_rw_lock_writer_lock(&rtnl_lock);
83
84         rtnl_list = g_slist_remove(rtnl_list, rtnl);
85
86         g_static_rw_lock_writer_unlock(&rtnl_lock);
87 }
88
89 static void process_newlink(unsigned short type, int index,
90                                         unsigned flags, unsigned change)
91 {
92         GSList *list;
93
94         DBG("index %d", index);
95
96         g_static_rw_lock_reader_lock(&rtnl_lock);
97
98         for (list = rtnl_list; list; list = list->next) {
99                 struct connman_rtnl *rtnl = list->data;
100
101                 if (rtnl->newlink)
102                         rtnl->newlink(type, index, flags, change);
103         }
104
105         g_static_rw_lock_reader_unlock(&rtnl_lock);
106 }
107
108 static void process_dellink(unsigned short type, int index,
109                                         unsigned flags, unsigned change)
110 {
111         GSList *list;
112
113         DBG("index %d", index);
114
115         g_static_rw_lock_reader_lock(&rtnl_lock);
116
117         for (list = rtnl_list; list; list = list->next) {
118                 struct connman_rtnl *rtnl = list->data;
119
120                 if (rtnl->dellink)
121                         rtnl->dellink(type, index, flags, change);
122         }
123
124         g_static_rw_lock_reader_unlock(&rtnl_lock);
125 }
126
127 static inline void print_inet(struct rtattr *attr, const char *name, int family)
128 {
129         if (family == AF_INET) {
130                 struct in_addr addr;
131                 addr = *((struct in_addr *) RTA_DATA(attr));
132                 DBG("  attr %s (len %jd) %s\n",
133                                 name, RTA_PAYLOAD(attr), inet_ntoa(addr));
134         } else
135                 DBG("  attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
136 }
137
138 static inline void print_char(struct rtattr *attr, const char *name)
139 {
140         DBG("  attr %s (len %jd) %s\n", name, RTA_PAYLOAD(attr),
141                                                 (char *) RTA_DATA(attr));
142 }
143
144 static inline void print_byte(struct rtattr *attr, const char *name)
145 {
146         DBG("  attr %s (len %jd) 0x%02x\n", name, RTA_PAYLOAD(attr),
147                                         *((unsigned char *) RTA_DATA(attr)));
148 }
149
150 static inline void print_attr(struct rtattr *attr, const char *name)
151 {
152         if (name)
153                 DBG("  attr %s (len %jd)\n", name, RTA_PAYLOAD(attr));
154         else
155                 DBG("  attr %d (len %jd)\n",
156                                         attr->rta_type, RTA_PAYLOAD(attr));
157 }
158
159 static void rtnl_link(struct nlmsghdr *hdr)
160 {
161 #if 0
162         struct ifinfomsg *msg;
163         struct rtattr *attr;
164         int bytes;
165
166         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
167         bytes = IFLA_PAYLOAD(hdr);
168
169         DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
170
171         for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
172                                         attr = RTA_NEXT(attr, bytes)) {
173                 switch (attr->rta_type) {
174                 case IFLA_ADDRESS:
175                         print_attr(attr, "address");
176                         break;
177                 case IFLA_BROADCAST:
178                         print_attr(attr, "broadcast");
179                         break;
180                 case IFLA_IFNAME:
181                         print_char(attr, "ifname");
182                         break;
183                 case IFLA_MTU:
184                         print_attr(attr, "mtu");
185                         break;
186                 case IFLA_LINK:
187                         print_attr(attr, "link");
188                         break;
189                 case IFLA_QDISC:
190                         print_attr(attr, "qdisc");
191                         break;
192                 case IFLA_STATS:
193                         print_attr(attr, "stats");
194                         break;
195                 case IFLA_COST:
196                         print_attr(attr, "cost");
197                         break;
198                 case IFLA_PRIORITY:
199                         print_attr(attr, "priority");
200                         break;
201                 case IFLA_MASTER:
202                         print_attr(attr, "master");
203                         break;
204                 case IFLA_WIRELESS:
205                         print_attr(attr, "wireless");
206                         break;
207                 case IFLA_PROTINFO:
208                         print_attr(attr, "protinfo");
209                         break;
210                 case IFLA_TXQLEN:
211                         print_attr(attr, "txqlen");
212                         break;
213                 case IFLA_MAP:
214                         print_attr(attr, "map");
215                         break;
216                 case IFLA_WEIGHT:
217                         print_attr(attr, "weight");
218                         break;
219                 case IFLA_OPERSTATE:
220                         print_byte(attr, "operstate");
221                         break;
222                 case IFLA_LINKMODE:
223                         print_byte(attr, "linkmode");
224                         break;
225                 default:
226                         print_attr(attr, NULL);
227                         break;
228                 }
229         }
230 #endif
231 }
232
233 static void rtnl_newlink(struct nlmsghdr *hdr)
234 {
235         struct ifinfomsg *msg;
236
237         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
238
239         DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
240
241         process_newlink(msg->ifi_type, msg->ifi_index,
242                                         msg->ifi_flags, msg->ifi_change);
243
244         rtnl_link(hdr);
245 }
246
247 static void rtnl_dellink(struct nlmsghdr *hdr)
248 {
249         struct ifinfomsg *msg;
250
251         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
252
253         DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
254
255         process_dellink(msg->ifi_type, msg->ifi_index,
256                                         msg->ifi_flags, msg->ifi_change);
257
258         rtnl_link(hdr);
259 }
260
261 static void rtnl_addr(struct nlmsghdr *hdr)
262 {
263         struct ifaddrmsg *msg;
264         struct rtattr *attr;
265         int bytes;
266
267         msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
268         bytes = IFA_PAYLOAD(hdr);
269
270         DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
271
272         for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
273                                         attr = RTA_NEXT(attr, bytes)) {
274                 switch (attr->rta_type) {
275                 case IFA_ADDRESS:
276                         print_inet(attr, "address", msg->ifa_family);
277                         break;
278                 case IFA_LOCAL:
279                         print_inet(attr, "local", msg->ifa_family);
280                         break;
281                 case IFA_LABEL:
282                         print_char(attr, "label");
283                         break;
284                 case IFA_BROADCAST:
285                         print_inet(attr, "broadcast", msg->ifa_family);
286                         break;
287                 case IFA_ANYCAST:
288                         print_attr(attr, "anycast");
289                         break;
290                 case IFA_CACHEINFO:
291                         print_attr(attr, "cacheinfo");
292                         break;
293                 case IFA_MULTICAST:
294                         print_attr(attr, "multicast");
295                         break;
296                 default:
297                         print_attr(attr, NULL);
298                         break;
299                 }
300         }
301 }
302
303 static void rtnl_route(struct nlmsghdr *hdr)
304 {
305         struct rtmsg *msg;
306         struct rtattr *attr;
307         int bytes;
308
309         msg = (struct rtmsg *) NLMSG_DATA(hdr);
310         bytes = RTM_PAYLOAD(hdr);
311
312         DBG("rtm_family %d rtm_flags 0x%04x", msg->rtm_family, msg->rtm_flags);
313
314         for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
315                                         attr = RTA_NEXT(attr, bytes)) {
316                 switch (attr->rta_type) {
317                 case RTA_DST:
318                         print_inet(attr, "dst", msg->rtm_family);
319                         break;
320                 case RTA_SRC:
321                         print_inet(attr, "src", msg->rtm_family);
322                         break;
323                 case RTA_IIF:
324                         print_char(attr, "iif");
325                         break;
326                 case RTA_OIF:
327                         print_attr(attr, "oif");
328                         break;
329                 case RTA_GATEWAY:
330                         print_inet(attr, "gateway", msg->rtm_family);
331                         break;
332                 case RTA_PRIORITY:
333                         print_attr(attr, "priority");
334                         break;
335                 case RTA_PREFSRC:
336                         print_inet(attr, "prefsrc", msg->rtm_family);
337                         break;
338                 case RTA_METRICS:
339                         print_attr(attr, "metrics");
340                         break;
341                 case RTA_TABLE:
342                         print_attr(attr, "table");
343                         break;
344                 default:
345                         print_attr(attr, NULL);
346                         break;
347                 }
348         }
349 }
350
351 static void rtnl_message(void *buf, size_t len)
352 {
353         DBG("buf %p len %zd", buf, len);
354
355         while (len > 0) {
356                 struct nlmsghdr *hdr = buf;
357                 struct nlmsgerr *err;
358
359                 if (!NLMSG_OK(hdr, len))
360                         break;
361
362                 DBG("len %d type %d flags 0x%04x seq %d",
363                                         hdr->nlmsg_len, hdr->nlmsg_type,
364                                         hdr->nlmsg_flags, hdr->nlmsg_seq);
365
366                 switch (hdr->nlmsg_type) {
367                 case NLMSG_NOOP:
368                         DBG("NOOP");
369                         return;
370                 case NLMSG_ERROR:
371                         err = NLMSG_DATA(hdr);
372                         DBG("ERROR %d (%s)", -err->error,
373                                                 strerror(-err->error));
374                         return;
375                 case NLMSG_DONE:
376                         DBG("DONE");
377                         return;
378                 case NLMSG_OVERRUN:
379                         DBG("OVERRUN");
380                         return;
381                 case RTM_NEWLINK:
382                         DBG("NEWLINK");
383                         rtnl_newlink(hdr);
384                         break;
385                 case RTM_DELLINK:
386                         DBG("DELLINK");
387                         rtnl_dellink(hdr);
388                         break;
389                 case RTM_NEWADDR:
390                         DBG("NEWADDR");
391                         rtnl_addr(hdr);
392                         break;
393                 case RTM_DELADDR:
394                         DBG("DELADDR");
395                         rtnl_addr(hdr);
396                         break;
397                 case RTM_NEWROUTE:
398                         DBG("NEWROUTE");
399                         rtnl_route(hdr);
400                         break;
401                 case RTM_DELROUTE:
402                         DBG("DELROUTE");
403                         rtnl_route(hdr);
404                         break;
405                 default:
406                         DBG("type %d", hdr->nlmsg_type);
407                         break;
408                 }
409
410                 len -= hdr->nlmsg_len;
411                 buf += hdr->nlmsg_len;
412         }
413 }
414
415 static gboolean netlink_event(GIOChannel *chan,
416                                 GIOCondition cond, gpointer data)
417 {
418         unsigned char buf[4096];
419         gsize len;
420         GIOError err;
421
422         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
423                 return FALSE;
424
425         memset(buf, 0, sizeof(buf));
426
427         err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
428         if (err) {
429                 if (err == G_IO_ERROR_AGAIN)
430                         return TRUE;
431                 return FALSE;
432         }
433
434         rtnl_message(buf, len);
435
436         return TRUE;
437 }
438
439 static GIOChannel *channel = NULL;
440
441 int __connman_rtnl_send(const void *buf, size_t len)
442 {
443         struct sockaddr_nl addr;
444         int sk;
445
446         DBG("buf %p len %zd", buf, len);
447
448         sk = g_io_channel_unix_get_fd(channel);
449
450         memset(&addr, 0, sizeof(addr));
451         addr.nl_family = AF_NETLINK;
452
453         return sendto(sk, buf, len, 0,
454                         (struct sockaddr *) &addr, sizeof(addr));
455 }
456
457 int connman_rtnl_send_getlink(void)
458 {
459         struct {
460                 struct nlmsghdr hdr;
461                 struct rtgenmsg msg;
462         } req;
463
464         DBG("");
465
466         memset(&req, 0, sizeof(req));
467         req.hdr.nlmsg_len = sizeof(req.hdr) + sizeof(req.msg);
468         req.hdr.nlmsg_type = RTM_GETLINK;
469         req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
470         req.hdr.nlmsg_pid = 0;
471         req.hdr.nlmsg_seq = 42;
472         req.msg.rtgen_family = AF_INET;
473
474         return __connman_rtnl_send(&req, sizeof(req));
475 }
476
477 int __connman_rtnl_init(void)
478 {
479         struct sockaddr_nl addr;
480         int sk;
481
482         DBG("");
483
484         sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
485         if (sk < 0)
486                 return -1;
487
488         memset(&addr, 0, sizeof(addr));
489         addr.nl_family = AF_NETLINK;
490         addr.nl_groups = RTMGRP_LINK;
491         //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
492         //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
493
494         if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
495                 close(sk);
496                 return -1;
497         }
498
499         channel = g_io_channel_unix_new(sk);
500         g_io_channel_set_close_on_unref(channel, TRUE);
501
502         g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
503                                                         netlink_event, NULL);
504
505         return 0;
506 }
507
508 void __connman_rtnl_cleanup(void)
509 {
510         DBG("");
511
512         g_io_channel_shutdown(channel, TRUE, NULL);
513         g_io_channel_unref(channel);
514
515         channel = NULL;
516 }