Optimize the RTNL routing table parsing
[framework/connectivity/connman.git] / src / rtnl.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  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/if_arp.h>
33 #include <linux/netlink.h>
34 #include <linux/rtnetlink.h>
35
36 #include <glib.h>
37
38 #include "connman.h"
39
40 #define print(arg...) do { } while (0)
41 //#define print(arg...) connman_info(arg)
42
43 struct watch_data {
44         unsigned int id;
45         int index;
46         connman_rtnl_link_cb_t newlink;
47         void *user_data;
48 };
49
50 static GSList *watch_list = NULL;
51 static unsigned int watch_id = 0;
52
53 static GHashTable *ipconfig_hash = NULL;
54 static GSList *ipconfig_list = NULL;
55
56 static void register_ipconfig(struct connman_ipconfig *ipconfig)
57 {
58         ipconfig_list = g_slist_append(ipconfig_list, ipconfig);
59 }
60
61 static void unregister_ipconfig(struct connman_ipconfig *ipconfig)
62 {
63         ipconfig_list = g_slist_remove(ipconfig_list, ipconfig);
64 }
65
66 static void free_ipconfig(gpointer data)
67 {
68         struct connman_ipconfig *ipconfig = data;
69
70         unregister_ipconfig(ipconfig);
71
72         connman_ipconfig_unref(ipconfig);
73 }
74
75 /**
76  * connman_rtnl_add_newlink_watch:
77  * @index: network device index
78  * @callback: callback function
79  * @user_data: callback data;
80  *
81  * Add a new RTNL watch for newlink events
82  *
83  * Returns: %0 on failure and a unique id on success
84  */
85 unsigned int connman_rtnl_add_newlink_watch(int index,
86                         connman_rtnl_link_cb_t callback, void *user_data)
87 {
88         struct connman_ipconfig *ipconfig;
89         struct watch_data *watch;
90
91         watch = g_try_new0(struct watch_data, 1);
92         if (watch == NULL)
93                 return 0;
94
95         watch->id = ++watch_id;
96         watch->index = index;
97
98         watch->newlink = callback;
99         watch->user_data = user_data;
100
101         watch_list = g_slist_prepend(watch_list, watch);
102
103         DBG("id %d", watch->id);
104
105         ipconfig = g_hash_table_lookup(ipconfig_hash, GINT_TO_POINTER(index));
106         if (ipconfig != NULL) {
107                 unsigned int flags = __connman_ipconfig_get_flags(ipconfig);
108
109                 if (callback)
110                         callback(flags, 0, user_data);
111         }
112
113         return watch->id;
114 }
115
116 /**
117  * connman_rtnl_remove_watch:
118  * @id: watch identifier
119  *
120  * Remove the RTNL watch for the identifier
121  */
122 void connman_rtnl_remove_watch(unsigned int id)
123 {
124         GSList *list;
125
126         DBG("id %d", id);
127
128         if (id == 0)
129                 return;
130
131         for (list = watch_list; list; list = list->next) {
132                 struct watch_data *watch = list->data;
133
134                 if (watch->id  == id) {
135                         watch_list = g_slist_remove(watch_list, watch);
136                         g_free(watch);
137                         break;
138                 }
139         }
140 }
141
142 static void trigger_newlink(gpointer key, gpointer value, gpointer user_data)
143 {
144         struct connman_rtnl *rtnl = user_data;
145         struct connman_ipconfig *ipconfig = value;
146         int index = GPOINTER_TO_INT(key);
147
148         if (index < 0 || ipconfig == NULL)
149                 return;
150
151         if (rtnl->newlink) {
152                 unsigned short type = __connman_ipconfig_get_type(ipconfig);
153                 unsigned int flags = __connman_ipconfig_get_flags(ipconfig);
154
155                 rtnl->newlink(type, index, flags, 0);
156         }
157 }
158
159 static GSList *rtnl_list = NULL;
160
161 static gint compare_priority(gconstpointer a, gconstpointer b)
162 {
163         const struct connman_rtnl *rtnl1 = a;
164         const struct connman_rtnl *rtnl2 = b;
165
166         return rtnl2->priority - rtnl1->priority;
167 }
168
169 /**
170  * connman_rtnl_register:
171  * @rtnl: RTNL module
172  *
173  * Register a new RTNL module
174  *
175  * Returns: %0 on success
176  */
177 int connman_rtnl_register(struct connman_rtnl *rtnl)
178 {
179         DBG("rtnl %p name %s", rtnl, rtnl->name);
180
181         rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
182                                                         compare_priority);
183
184         g_hash_table_foreach(ipconfig_hash, trigger_newlink, rtnl);
185
186         return 0;
187 }
188
189 /**
190  * connman_rtnl_unregister:
191  * @rtnl: RTNL module
192  *
193  * Remove a previously registered RTNL module
194  */
195 void connman_rtnl_unregister(struct connman_rtnl *rtnl)
196 {
197         DBG("rtnl %p name %s", rtnl, rtnl->name);
198
199         rtnl_list = g_slist_remove(rtnl_list, rtnl);
200 }
201
202 static void process_newlink(unsigned short type, int index,
203                                         unsigned flags, unsigned change)
204 {
205         struct connman_ipconfig *ipconfig;
206         GSList *list;
207
208         switch (type) {
209         case ARPHRD_ETHER:
210         case ARPHRD_LOOPBACK:
211         case ARPHRD_NONE:
212                 ipconfig = g_hash_table_lookup(ipconfig_hash,
213                                                 GINT_TO_POINTER(index));
214                 if (ipconfig == NULL) {
215                         ipconfig = connman_ipconfig_create(index);
216                         if (ipconfig != NULL) {
217                                 g_hash_table_insert(ipconfig_hash,
218                                         GINT_TO_POINTER(index), ipconfig);
219
220                                 register_ipconfig(ipconfig);
221                         }
222                 }
223
224                 if (ipconfig != NULL)
225                         __connman_ipconfig_update_link(ipconfig,
226                                                         flags, change);
227                 break;
228         }
229
230         for (list = rtnl_list; list; list = list->next) {
231                 struct connman_rtnl *rtnl = list->data;
232
233                 if (rtnl->newlink)
234                         rtnl->newlink(type, index, flags, change);
235         }
236
237         for (list = watch_list; list; list = list->next) {
238                 struct watch_data *watch = list->data;
239
240                 if (watch->index != index)
241                         continue;
242
243                 if (watch->newlink)
244                         watch->newlink(flags, change, watch->user_data);
245         }
246 }
247
248 static void process_dellink(unsigned short type, int index,
249                                         unsigned flags, unsigned change)
250 {
251         GSList *list;
252
253         for (list = rtnl_list; list; list = list->next) {
254                 struct connman_rtnl *rtnl = list->data;
255
256                 if (rtnl->dellink)
257                         rtnl->dellink(type, index, flags, change);
258         }
259
260         switch (type) {
261         case ARPHRD_ETHER:
262         case ARPHRD_LOOPBACK:
263         case ARPHRD_NONE:
264                 g_hash_table_remove(ipconfig_hash, GINT_TO_POINTER(index));
265                 break;
266         }
267 }
268
269 static void extract_addr(struct ifaddrmsg *msg, int bytes,
270                                                 const char **label,
271                                                 struct in_addr *local,
272                                                 struct in_addr *address,
273                                                 struct in_addr *broadcast)
274 {
275         struct rtattr *attr;
276
277         for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
278                                         attr = RTA_NEXT(attr, bytes)) {
279                 switch (attr->rta_type) {
280                 case IFA_ADDRESS:
281                         if (address != NULL)
282                                 *address = *((struct in_addr *) RTA_DATA(attr));
283                         break;
284                 case IFA_LOCAL:
285                         if (local != NULL)
286                                 *local = *((struct in_addr *) RTA_DATA(attr));
287                         break;
288                 case IFA_BROADCAST:
289                         if (broadcast != NULL)
290                                 *broadcast = *((struct in_addr *) RTA_DATA(attr));
291                         break;
292                 case IFA_LABEL:
293                         if (label != NULL)
294                                 *label = RTA_DATA(attr);
295                         break;
296                 }
297         }
298 }
299
300 static void process_newaddr(unsigned char family, unsigned char prefixlen,
301                                 int index, struct ifaddrmsg *msg, int bytes)
302 {
303         GSList *list;
304         const char *label;
305         struct in_addr address = { INADDR_ANY };
306
307         if (family != AF_INET)
308                 return;
309
310         extract_addr(msg, bytes, &label, &address, NULL, NULL);
311
312         for (list = ipconfig_list; list; list = list->next) {
313                 struct connman_ipconfig *ipconfig = list->data;
314
315                 if (__connman_ipconfig_get_index(ipconfig) != index)
316                         continue;
317
318                 __connman_ipconfig_add_address(ipconfig, label, prefixlen,
319                                                 inet_ntoa(address), NULL);
320         }
321 }
322
323 static void process_deladdr(unsigned char family, unsigned char prefixlen,
324                                 int index, struct ifaddrmsg *msg, int bytes)
325 {
326         GSList *list;
327         const char *label;
328         struct in_addr address = { INADDR_ANY };
329
330         if (family != AF_INET)
331                 return;
332
333         extract_addr(msg, bytes, &label, &address, NULL, NULL);
334
335         for (list = ipconfig_list; list; list = list->next) {
336                 struct connman_ipconfig *ipconfig = list->data;
337
338                 if (__connman_ipconfig_get_index(ipconfig) != index)
339                         continue;
340
341                 __connman_ipconfig_del_address(ipconfig, label, prefixlen,
342                                                 inet_ntoa(address), NULL);
343         }
344 }
345
346 static void extract_route(struct rtmsg *msg, int bytes, int *index,
347                                                 struct in_addr *dst,
348                                                 struct in_addr *gateway)
349 {
350         struct rtattr *attr;
351
352         for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
353                                         attr = RTA_NEXT(attr, bytes)) {
354                 switch (attr->rta_type) {
355                 case RTA_DST:
356                         if (dst != NULL)
357                                 *dst = *((struct in_addr *) RTA_DATA(attr));
358                         break;
359                 case RTA_GATEWAY:
360                         if (gateway != NULL)
361                                 *gateway = *((struct in_addr *) RTA_DATA(attr));
362                         break;
363                 case RTA_OIF:
364                         if (index != NULL)
365                                 *index = *((int *) RTA_DATA(attr));
366                         break;
367                 }
368         }
369 }
370
371 static void process_newroute(unsigned char family, unsigned char scope,
372                                                 struct rtmsg *msg, int bytes)
373 {
374         GSList *list;
375         struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
376         char dststr[16], gatewaystr[16];
377         int index = -1;
378
379         if (family != AF_INET)
380                 return;
381
382         extract_route(msg, bytes, &index, &dst, &gateway);
383
384         inet_ntop(family, &dst, dststr, sizeof(dststr));
385         inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
386
387         for (list = ipconfig_list; list; list = list->next) {
388                 struct connman_ipconfig *ipconfig = list->data;
389
390                 if (__connman_ipconfig_get_index(ipconfig) != index)
391                         continue;
392
393                 __connman_ipconfig_add_route(ipconfig, scope,
394                                                         dststr, gatewaystr);
395         }
396
397         if (scope != RT_SCOPE_UNIVERSE || dst.s_addr != INADDR_ANY)
398                 return;
399
400         for (list = rtnl_list; list; list = list->next) {
401                 struct connman_rtnl *rtnl = list->data;
402
403                 if (rtnl->newgateway)
404                         rtnl->newgateway(index, gatewaystr);
405         }
406 }
407
408 static void process_delroute(unsigned char family, unsigned char scope,
409                                                 struct rtmsg *msg, int bytes)
410 {
411         GSList *list;
412         struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
413         char dststr[16], gatewaystr[16];
414         int index = -1;
415
416         if (family != AF_INET)
417                 return;
418
419         extract_route(msg, bytes, &index, &dst, &gateway);
420
421         inet_ntop(family, &dst, dststr, sizeof(dststr));
422         inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
423
424         for (list = ipconfig_list; list; list = list->next) {
425                 struct connman_ipconfig *ipconfig = list->data;
426
427                 if (__connman_ipconfig_get_index(ipconfig) != index)
428                         continue;
429
430                 __connman_ipconfig_del_route(ipconfig, scope,
431                                                         dststr, gatewaystr);
432         }
433
434         if (scope != RT_SCOPE_UNIVERSE || dst.s_addr != INADDR_ANY)
435                 return;
436
437         for (list = rtnl_list; list; list = list->next) {
438                 struct connman_rtnl *rtnl = list->data;
439
440                 if (rtnl->delgateway)
441                         rtnl->delgateway(index, gatewaystr);
442         }
443 }
444
445 static inline void print_inet(struct rtattr *attr, const char *name,
446                                                         unsigned char family)
447 {
448         if (family == AF_INET) {
449                 struct in_addr addr;
450                 addr = *((struct in_addr *) RTA_DATA(attr));
451                 print("  attr %s (len %d) %s\n", name,
452                                 (int) RTA_PAYLOAD(attr), inet_ntoa(addr));
453         } else
454                 print("  attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr));
455 }
456
457 static inline void print_string(struct rtattr *attr, const char *name)
458 {
459         print("  attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr),
460                                                 (char *) RTA_DATA(attr));
461 }
462
463 static inline void print_byte(struct rtattr *attr, const char *name)
464 {
465         print("  attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr),
466                                         *((unsigned char *) RTA_DATA(attr)));
467 }
468
469 static inline void print_integer(struct rtattr *attr, const char *name)
470 {
471         print("  attr %s (len %d) %d\n", name, (int) RTA_PAYLOAD(attr),
472                                                 *((int *) RTA_DATA(attr)));
473 }
474
475 static inline void print_attr(struct rtattr *attr, const char *name)
476 {
477         if (name)
478                 print("  attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr));
479         else
480                 print("  attr %d (len %d)\n",
481                                 attr->rta_type, (int) RTA_PAYLOAD(attr));
482 }
483
484 static void rtnl_link(struct nlmsghdr *hdr)
485 {
486         struct ifinfomsg *msg;
487         struct rtattr *attr;
488         int bytes;
489
490         msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
491         bytes = IFLA_PAYLOAD(hdr);
492
493         print("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
494
495         for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
496                                         attr = RTA_NEXT(attr, bytes)) {
497                 switch (attr->rta_type) {
498                 case IFLA_ADDRESS:
499                         print_attr(attr, "address");
500                         break;
501                 case IFLA_BROADCAST:
502                         print_attr(attr, "broadcast");
503                         break;
504                 case IFLA_IFNAME:
505                         print_string(attr, "ifname");
506                         break;
507                 case IFLA_MTU:
508                         print_integer(attr, "mtu");
509                         break;
510                 case IFLA_LINK:
511                         print_attr(attr, "link");
512                         break;
513                 case IFLA_QDISC:
514                         print_attr(attr, "qdisc");
515                         break;
516                 case IFLA_STATS:
517                         print_attr(attr, "stats");
518                         break;
519                 case IFLA_COST:
520                         print_attr(attr, "cost");
521                         break;
522                 case IFLA_PRIORITY:
523                         print_attr(attr, "priority");
524                         break;
525                 case IFLA_MASTER:
526                         print_attr(attr, "master");
527                         break;
528                 case IFLA_WIRELESS:
529                         print_attr(attr, "wireless");
530                         break;
531                 case IFLA_PROTINFO:
532                         print_attr(attr, "protinfo");
533                         break;
534                 case IFLA_TXQLEN:
535                         print_attr(attr, "txqlen");
536                         break;
537                 case IFLA_MAP:
538                         print_attr(attr, "map");
539                         break;
540                 case IFLA_WEIGHT:
541                         print_attr(attr, "weight");
542                         break;
543                 case IFLA_OPERSTATE:
544                         print_byte(attr, "operstate");
545                         break;
546                 case IFLA_LINKMODE:
547                         print_byte(attr, "linkmode");
548                         break;
549                 default:
550                         print_attr(attr, NULL);
551                         break;
552                 }
553         }
554 }
555
556 static void rtnl_newlink(struct nlmsghdr *hdr)
557 {
558         struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
559
560         rtnl_link(hdr);
561
562         process_newlink(msg->ifi_type, msg->ifi_index,
563                                         msg->ifi_flags, msg->ifi_change);
564 }
565
566 static void rtnl_dellink(struct nlmsghdr *hdr)
567 {
568         struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
569
570         rtnl_link(hdr);
571
572         process_dellink(msg->ifi_type, msg->ifi_index,
573                                         msg->ifi_flags, msg->ifi_change);
574 }
575
576 static void rtnl_addr(struct nlmsghdr *hdr)
577 {
578         struct ifaddrmsg *msg;
579         struct rtattr *attr;
580         int bytes;
581
582         msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
583         bytes = IFA_PAYLOAD(hdr);
584
585         print("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
586
587         for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
588                                         attr = RTA_NEXT(attr, bytes)) {
589                 switch (attr->rta_type) {
590                 case IFA_ADDRESS:
591                         print_inet(attr, "address", msg->ifa_family);
592                         break;
593                 case IFA_LOCAL:
594                         print_inet(attr, "local", msg->ifa_family);
595                         break;
596                 case IFA_LABEL:
597                         print_string(attr, "label");
598                         break;
599                 case IFA_BROADCAST:
600                         print_inet(attr, "broadcast", msg->ifa_family);
601                         break;
602                 case IFA_ANYCAST:
603                         print_attr(attr, "anycast");
604                         break;
605                 case IFA_CACHEINFO:
606                         print_attr(attr, "cacheinfo");
607                         break;
608                 case IFA_MULTICAST:
609                         print_attr(attr, "multicast");
610                         break;
611                 default:
612                         print_attr(attr, NULL);
613                         break;
614                 }
615         }
616 }
617
618 static void rtnl_newaddr(struct nlmsghdr *hdr)
619 {
620         struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
621
622         rtnl_addr(hdr);
623
624         process_newaddr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
625                                                 msg, IFA_PAYLOAD(hdr));
626 }
627
628 static void rtnl_deladdr(struct nlmsghdr *hdr)
629 {
630         struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
631
632         rtnl_addr(hdr);
633
634         process_deladdr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
635                                                 msg, IFA_PAYLOAD(hdr));
636 }
637
638 static void rtnl_route(struct nlmsghdr *hdr)
639 {
640         struct rtmsg *msg;
641         struct rtattr *attr;
642         int bytes;
643
644         msg = (struct rtmsg *) NLMSG_DATA(hdr);
645         bytes = RTM_PAYLOAD(hdr);
646
647         print("rtm_family %d rtm_table %d rtm_protocol %d",
648                         msg->rtm_family, msg->rtm_table, msg->rtm_protocol);
649         print("rtm_scope %d rtm_type %d rtm_flags 0x%04x",
650                                 msg->rtm_scope, msg->rtm_type, msg->rtm_flags);
651
652         for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
653                                         attr = RTA_NEXT(attr, bytes)) {
654                 switch (attr->rta_type) {
655                 case RTA_DST:
656                         print_inet(attr, "dst", msg->rtm_family);
657                         break;
658                 case RTA_SRC:
659                         print_inet(attr, "src", msg->rtm_family);
660                         break;
661                 case RTA_IIF:
662                         print_string(attr, "iif");
663                         break;
664                 case RTA_OIF:
665                         print_integer(attr, "oif");
666                         break;
667                 case RTA_GATEWAY:
668                         print_inet(attr, "gateway", msg->rtm_family);
669                         break;
670                 case RTA_PRIORITY:
671                         print_attr(attr, "priority");
672                         break;
673                 case RTA_PREFSRC:
674                         print_inet(attr, "prefsrc", msg->rtm_family);
675                         break;
676                 case RTA_METRICS:
677                         print_attr(attr, "metrics");
678                         break;
679                 case RTA_TABLE:
680                         print_integer(attr, "table");
681                         break;
682                 default:
683                         print_attr(attr, NULL);
684                         break;
685                 }
686         }
687 }
688
689 static void rtnl_newroute(struct nlmsghdr *hdr)
690 {
691         struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
692
693         rtnl_route(hdr);
694
695         if (msg->rtm_table == RT_TABLE_MAIN &&
696                                 msg->rtm_protocol == RTPROT_BOOT &&
697                                                 msg->rtm_type == RTN_UNICAST)
698                 process_newroute(msg->rtm_family, msg->rtm_scope,
699                                                 msg, RTM_PAYLOAD(hdr));
700 }
701
702 static void rtnl_delroute(struct nlmsghdr *hdr)
703 {
704         struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
705
706         rtnl_route(hdr);
707
708         if (msg->rtm_table == RT_TABLE_MAIN &&
709                                 msg->rtm_protocol == RTPROT_BOOT &&
710                                                 msg->rtm_type == RTN_UNICAST)
711                 process_delroute(msg->rtm_family, msg->rtm_scope,
712                                                 msg, RTM_PAYLOAD(hdr));
713 }
714
715 static const char *type2string(uint16_t type)
716 {
717         switch (type) {
718         case NLMSG_NOOP:
719                 return "NOOP";
720         case NLMSG_ERROR:
721                 return "ERROR";
722         case NLMSG_DONE:
723                 return "DONE";
724         case NLMSG_OVERRUN:
725                 return "OVERRUN";
726         case RTM_GETLINK:
727                 return "GETLINK";
728         case RTM_NEWLINK:
729                 return "NEWLINK";
730         case RTM_DELLINK:
731                 return "DELLINK";
732         case RTM_NEWADDR:
733                 return "NEWADDR";
734         case RTM_DELADDR:
735                 return "DELADDR";
736         case RTM_GETROUTE:
737                 return "GETROUTE";
738         case RTM_NEWROUTE:
739                 return "NEWROUTE";
740         case RTM_DELROUTE:
741                 return "DELROUTE";
742         default:
743                 return "UNKNOWN";
744         }
745 }
746
747 static GIOChannel *channel = NULL;
748
749 struct rtnl_request {
750         struct nlmsghdr hdr;
751         struct rtgenmsg msg;
752 };
753 #define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
754
755 static GSList *request_list = NULL;
756 static guint32 request_seq = 0;
757
758 static struct rtnl_request *find_request(guint32 seq)
759 {
760         GSList *list;
761
762         for (list = request_list; list; list = list->next) {
763                 struct rtnl_request *req = list->data;
764
765                 if (req->hdr.nlmsg_seq == seq)
766                         return req;
767         }
768
769         return NULL;
770 }
771
772 static int send_request(struct rtnl_request *req)
773 {
774         struct sockaddr_nl addr;
775         int sk;
776
777         DBG("%s len %d type %d flags 0x%04x seq %d",
778                                 type2string(req->hdr.nlmsg_type),
779                                 req->hdr.nlmsg_len, req->hdr.nlmsg_type,
780                                 req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
781
782         sk = g_io_channel_unix_get_fd(channel);
783
784         memset(&addr, 0, sizeof(addr));
785         addr.nl_family = AF_NETLINK;
786
787         return sendto(sk, req, req->hdr.nlmsg_len, 0,
788                                 (struct sockaddr *) &addr, sizeof(addr));
789 }
790
791 static int queue_request(struct rtnl_request *req)
792 {
793         request_list = g_slist_append(request_list, req);
794
795         if (g_slist_length(request_list) > 1)
796                 return 0;
797
798         return send_request(req);
799 }
800
801 static int process_response(guint32 seq)
802 {
803         struct rtnl_request *req;
804
805         DBG("seq %d", seq);
806
807         req = find_request(seq);
808         if (req != NULL) {
809                 request_list = g_slist_remove(request_list, req);
810                 g_free(req);
811         }
812
813         req = g_slist_nth_data(request_list, 0);
814         if (req == NULL)
815                 return 0;
816
817         return send_request(req);
818 }
819
820 static void rtnl_message(void *buf, size_t len)
821 {
822         DBG("buf %p len %zd", buf, len);
823
824         while (len > 0) {
825                 struct nlmsghdr *hdr = buf;
826                 struct nlmsgerr *err;
827
828                 if (!NLMSG_OK(hdr, len))
829                         break;
830
831                 DBG("%s len %d type %d flags 0x%04x seq %d",
832                                         type2string(hdr->nlmsg_type),
833                                         hdr->nlmsg_len, hdr->nlmsg_type,
834                                         hdr->nlmsg_flags, hdr->nlmsg_seq);
835
836                 switch (hdr->nlmsg_type) {
837                 case NLMSG_NOOP:
838                 case NLMSG_OVERRUN:
839                         return;
840                 case NLMSG_DONE:
841                         process_response(hdr->nlmsg_seq);
842                         return;
843                 case NLMSG_ERROR:
844                         err = NLMSG_DATA(hdr);
845                         DBG("error %d (%s)", -err->error,
846                                                 strerror(-err->error));
847                         return;
848                 case RTM_NEWLINK:
849                         rtnl_newlink(hdr);
850                         break;
851                 case RTM_DELLINK:
852                         rtnl_dellink(hdr);
853                         break;
854                 case RTM_NEWADDR:
855                         rtnl_newaddr(hdr);
856                         break;
857                 case RTM_DELADDR:
858                         rtnl_deladdr(hdr);
859                         break;
860                 case RTM_NEWROUTE:
861                         rtnl_newroute(hdr);
862                         break;
863                 case RTM_DELROUTE:
864                         rtnl_delroute(hdr);
865                         break;
866                 }
867
868                 len -= hdr->nlmsg_len;
869                 buf += hdr->nlmsg_len;
870         }
871 }
872
873 static gboolean netlink_event(GIOChannel *chan,
874                                 GIOCondition cond, gpointer data)
875 {
876         unsigned char buf[4096];
877         gsize len;
878         GIOError err;
879
880         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
881                 return FALSE;
882
883         memset(buf, 0, sizeof(buf));
884
885         err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
886         if (err) {
887                 if (err == G_IO_ERROR_AGAIN)
888                         return TRUE;
889                 return FALSE;
890         }
891
892         rtnl_message(buf, len);
893
894         return TRUE;
895 }
896
897 static int send_getlink(void)
898 {
899         struct rtnl_request *req;
900
901         DBG("");
902
903         req = g_try_malloc0(RTNL_REQUEST_SIZE);
904         if (req == NULL)
905                 return -ENOMEM;
906
907         req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
908         req->hdr.nlmsg_type = RTM_GETLINK;
909         req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
910         req->hdr.nlmsg_pid = 0;
911         req->hdr.nlmsg_seq = request_seq++;
912         req->msg.rtgen_family = AF_INET;
913
914         return queue_request(req);
915 }
916
917 static int send_getaddr(void)
918 {
919         struct rtnl_request *req;
920
921         DBG("");
922
923         req = g_try_malloc0(RTNL_REQUEST_SIZE);
924         if (req == NULL)
925                 return -ENOMEM;
926
927         req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
928         req->hdr.nlmsg_type = RTM_GETADDR;
929         req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
930         req->hdr.nlmsg_pid = 0;
931         req->hdr.nlmsg_seq = request_seq++;
932         req->msg.rtgen_family = AF_INET;
933
934         return queue_request(req);
935 }
936
937 int connman_rtnl_send_getroute(void)
938 {
939         struct rtnl_request *req;
940
941         DBG("");
942
943         req = g_try_malloc0(RTNL_REQUEST_SIZE);
944         if (req == NULL)
945                 return -ENOMEM;
946
947         req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
948         req->hdr.nlmsg_type = RTM_GETROUTE;
949         req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
950         req->hdr.nlmsg_pid = 0;
951         req->hdr.nlmsg_seq = request_seq++;
952         req->msg.rtgen_family = AF_INET;
953
954         return queue_request(req);
955 }
956
957 int __connman_rtnl_init(void)
958 {
959         struct sockaddr_nl addr;
960         int sk;
961
962         DBG("");
963
964         ipconfig_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
965                                                         NULL, free_ipconfig);
966
967         sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
968         if (sk < 0)
969                 return -1;
970
971         memset(&addr, 0, sizeof(addr));
972         addr.nl_family = AF_NETLINK;
973         addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
974
975         if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
976                 close(sk);
977                 return -1;
978         }
979
980         channel = g_io_channel_unix_new(sk);
981         g_io_channel_set_close_on_unref(channel, TRUE);
982
983         g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
984                                                         netlink_event, NULL);
985
986         return 0;
987 }
988
989 void __connman_rtnl_start(void)
990 {
991         DBG("");
992
993         send_getlink();
994         send_getaddr();
995         connman_rtnl_send_getroute();
996 }
997
998 void __connman_rtnl_cleanup(void)
999 {
1000         GSList *list;
1001
1002         DBG("");
1003
1004         g_slist_free(ipconfig_list);
1005         ipconfig_list = NULL;
1006
1007         g_hash_table_destroy(ipconfig_hash);
1008         ipconfig_hash = NULL;
1009
1010         for (list = watch_list; list; list = list->next) {
1011                 struct watch_data *watch = list->data;
1012
1013                 DBG("removing watch %d", watch->id);
1014
1015                 g_free(watch);
1016                 list->data = NULL;
1017         }
1018
1019         g_slist_free(watch_list);
1020         watch_list = NULL;
1021
1022         for (list = request_list; list; list = list->next) {
1023                 struct rtnl_request *req = list->data;
1024
1025                 DBG("%s len %d type %d flags 0x%04x seq %d",
1026                                 type2string(req->hdr.nlmsg_type),
1027                                 req->hdr.nlmsg_len, req->hdr.nlmsg_type,
1028                                 req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
1029
1030                 g_free(req);
1031                 list->data = NULL;
1032         }
1033
1034         g_slist_free(request_list);
1035         request_list = NULL;
1036
1037         g_io_channel_shutdown(channel, TRUE, NULL);
1038         g_io_channel_unref(channel);
1039
1040         channel = NULL;
1041 }