51d21d902037a4cf1977955e086746a38956a268
[oonf.git] / src-plugins / subsystems / os_linux / os_routing_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 /* must be first because of a problem with linux/rtnetlink.h */
47 #include <sys/socket.h>
48
49 /* and now the rest of the includes */
50 #include <linux/netlink.h>
51 #include <linux/rtnetlink.h>
52 #include <sys/uio.h>
53
54 #include "common/common_types.h"
55 #include "common/avl.h"
56 #include "common/avl_comp.h"
57 #include "core/oonf_subsystem.h"
58 #include "subsystems/os_system.h"
59
60 #include "subsystems/os_routing.h"
61 #include "subsystems/os_linux/os_routing_linux.h"
62
63 /* Definitions */
64 #define LOG_OS_ROUTING _oonf_os_routing_subsystem.logging
65
66 /**
67  * Array to translate between OONF route types and internal kernel types
68  */
69 struct route_type_translation {
70   /*! OONF route type */
71   enum os_route_type oonf;
72
73   /*! linux kernel route type */
74   uint8_t os_linux;
75 };
76
77 /* prototypes */
78 static int _init(void);
79 static void _cleanup(void);
80
81 static int _routing_set(struct nlmsghdr *msg, struct os_route *route,
82     unsigned char rt_scope);
83
84 static void _routing_finished(struct os_route *route, int error);
85 static void _cb_rtnetlink_message(struct nlmsghdr *);
86 static void _cb_rtnetlink_error(uint32_t seq, int err);
87 static void _cb_rtnetlink_done(uint32_t seq);
88 static void _cb_rtnetlink_timeout(void);
89
90 /* subsystem definition */
91 static const char *_dependencies[] = {
92   OONF_OS_SYSTEM_SUBSYSTEM,
93 };
94
95 static struct oonf_subsystem _oonf_os_routing_subsystem = {
96   .name = OONF_OS_ROUTING_SUBSYSTEM,
97   .dependencies = _dependencies,
98   .dependencies_count = ARRAYSIZE(_dependencies),
99   .init = _init,
100   .cleanup = _cleanup,
101 };
102 DECLARE_OONF_PLUGIN(_oonf_os_routing_subsystem);
103
104 /* translation table between route types */
105 static struct route_type_translation _type_translation[] = {
106     { OS_ROUTE_UNICAST, RTN_UNICAST },
107     { OS_ROUTE_LOCAL, RTN_LOCAL },
108     { OS_ROUTE_BROADCAST, RTN_BROADCAST },
109     { OS_ROUTE_MULTICAST, RTN_MULTICAST },
110     { OS_ROUTE_THROW, RTN_THROW },
111     { OS_ROUTE_UNREACHABLE, RTN_UNREACHABLE },
112     { OS_ROUTE_PROHIBIT, RTN_PROHIBIT },
113     { OS_ROUTE_BLACKHOLE, RTN_BLACKHOLE },
114     { OS_ROUTE_NAT, RTN_NAT }
115 };
116
117 /* netlink socket for route set/get commands */
118 static const uint32_t _rtnetlink_mcast[] = {
119   RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE
120 };
121
122 static struct os_system_netlink _rtnetlink_socket = {
123   .name = "routing",
124   .used_by = &_oonf_os_routing_subsystem,
125   .cb_message = _cb_rtnetlink_message,
126   .cb_error = _cb_rtnetlink_error,
127   .cb_done = _cb_rtnetlink_done,
128   .cb_timeout = _cb_rtnetlink_timeout,
129 };
130
131 static struct avl_tree _rtnetlink_feedback;
132 static struct list_entity _rtnetlink_listener;
133
134 /* default wildcard route */
135 static const struct os_route_parameter OS_ROUTE_WILDCARD = {
136   .family = AF_UNSPEC,
137   .src_ip = { ._type = AF_UNSPEC },
138   .gw = { ._type = AF_UNSPEC },
139   .type = OS_ROUTE_UNDEFINED,
140   .key = {
141       .dst = { ._type = AF_UNSPEC },
142       .src = { ._type = AF_UNSPEC },
143   },
144   .table = RT_TABLE_UNSPEC,
145   .metric = -1,
146   .protocol = RTPROT_UNSPEC,
147   .if_index = 0
148 };
149
150 /* kernel version check */
151 static bool _is_kernel_3_11_0_or_better;
152
153 /**
154  * Initialize routing subsystem
155  * @return -1 if an error happened, 0 otherwise
156  */
157 static int
158 _init(void) {
159   if (os_system_linux_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) {
160     return -1;
161   }
162   if (os_system_linux_netlink_add_mc(&_rtnetlink_socket, _rtnetlink_mcast, ARRAYSIZE(_rtnetlink_mcast))) {
163     os_system_linux_netlink_remove(&_rtnetlink_socket);
164     return -1;
165   }
166   avl_init(&_rtnetlink_feedback, avl_comp_uint32, false);
167   list_init_head(&_rtnetlink_listener);
168
169   _is_kernel_3_11_0_or_better = os_system_linux_is_minimal_kernel(3,11,0);
170   return 0;
171 }
172
173 /**
174  * Cleanup all resources allocated by the routing subsystem
175  */
176 static void
177 _cleanup(void) {
178   struct os_route *rt, *rt_it;
179
180   avl_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) {
181     _routing_finished(rt, 1);
182   }
183
184   os_system_linux_netlink_remove(&_rtnetlink_socket);
185 }
186
187 /**
188  * Check if kernel supports source-specific routing
189  * @param af_family address family
190  * @return true if source-specific routing is supported for
191  *   address family
192  */
193 bool
194 os_routing_linux_supports_source_specific(int af_family) {
195   if (af_family == AF_INET) {
196     return false;
197   }
198
199   /* TODO: better check for source specific routing necessary! */
200   return _is_kernel_3_11_0_or_better;
201 }
202
203 /**
204  * Update an entry of the kernel routing table. This call will only trigger
205  * the change, the real change will be done as soon as the netlink socket is
206  * writable.
207  * @param route data of route to be set/removed
208  * @param set true if route should be set, false if it should be removed
209  * @param del_similar true if similar routes that block this one should be
210  *   removed.
211  * @return -1 if an error happened, 0 otherwise
212  */
213 int
214 os_routing_linux_set(struct os_route *route, bool set, bool del_similar) {
215   uint8_t buffer[UIO_MAXIOV];
216   struct nlmsghdr *msg;
217   unsigned char scope;
218   struct os_route os_rt;
219   int seq;
220 #ifdef OONF_LOG_DEBUG_INFO
221   struct os_route_str rbuf;
222 #endif
223
224   memset(buffer, 0, sizeof(buffer));
225
226   /* copy route settings */
227   memcpy(&os_rt, route, sizeof(os_rt));
228
229   /* get pointers for netlink message */
230   msg = (void *)&buffer[0];
231
232   msg->nlmsg_flags = NLM_F_REQUEST;
233
234   /* set length of netlink message with rtmsg payload */
235   msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
236
237   /* normally all routing operations are UNIVERSE scope */
238   scope = RT_SCOPE_UNIVERSE;
239
240   if (set) {
241     msg->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
242     msg->nlmsg_type = RTM_NEWROUTE;
243   } else {
244     msg->nlmsg_type = RTM_DELROUTE;
245
246     os_rt.p.protocol = 0;
247     netaddr_invalidate(&os_rt.p.src_ip);
248
249     if (del_similar) {
250       /* no interface necessary */
251       os_rt.p.if_index = 0;
252
253       /* as wildcard for fuzzy deletion */
254       scope = RT_SCOPE_NOWHERE;
255     }
256   }
257
258   if (netaddr_is_unspec(&os_rt.p.gw)
259       && netaddr_get_address_family(&os_rt.p.key.dst) == AF_INET
260       && netaddr_get_prefix_length(&os_rt.p.key.dst) == netaddr_get_maxprefix(&os_rt.p.key.dst)) {
261     /* use destination as gateway, to 'force' linux kernel to do proper source address selection */
262     memcpy(&os_rt.p.gw, &os_rt.p.key.dst, sizeof(os_rt.p.gw));
263   }
264
265   OONF_DEBUG(LOG_OS_ROUTING, "%sset route: %s", set ? "" : "re",
266       os_routing_to_string(&rbuf, &os_rt.p));
267
268   if (_routing_set(msg, &os_rt, scope)) {
269     return -1;
270   }
271
272   /* cannot fail */
273   seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg);
274
275   if (route->cb_finished) {
276     route->_internal.nl_seq = seq;
277     route->_internal._node.key = &route->_internal.nl_seq;
278
279     assert (!avl_is_node_added(&route->_internal._node));
280     avl_insert(&_rtnetlink_feedback, &route->_internal._node);
281   }
282   return 0;
283 }
284
285 /**
286  * Request all routing data of a certain address family
287  * @param route pointer to routing filter
288  * @return -1 if an error happened, 0 otherwise
289  */
290 int
291 os_routing_linux_query(struct os_route *route) {
292   uint8_t buffer[UIO_MAXIOV];
293   struct nlmsghdr *msg;
294   struct rtgenmsg *rt_gen;
295   int seq;
296
297   assert (route->cb_finished != NULL && route->cb_get != NULL);
298   memset(buffer, 0, sizeof(buffer));
299
300   /* get pointers for netlink message */
301   msg = (void *)&buffer[0];
302   rt_gen = NLMSG_DATA(msg);
303
304   msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
305
306   /* set length of netlink message with rtmsg payload */
307   msg->nlmsg_len = NLMSG_LENGTH(sizeof(*rt_gen));
308
309   msg->nlmsg_type = RTM_GETROUTE;
310   rt_gen->rtgen_family = route->p.family;
311
312   seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg);
313   if (seq < 0) {
314     return -1;
315   }
316
317   route->_internal.nl_seq = seq;
318   route->_internal._node.key = &route->_internal.nl_seq;
319   avl_insert(&_rtnetlink_feedback, &route->_internal._node);
320   return 0;
321 }
322
323 /**
324  * Stop processing of a routing command
325  * @param route pointer to os_route
326  */
327 void
328 os_routing_linux_interrupt(struct os_route *route) {
329   if (os_routing_linux_is_in_progress(route)) {
330     _routing_finished(route, -1);
331   }
332 }
333
334 /**
335  * @param route os route
336  * @return true if route is being processed by the kernel,
337  *   false otherwise
338  */
339 bool
340 os_routing_linux_is_in_progress(struct os_route *route) {
341   return avl_is_node_added(&route->_internal._node);
342 }
343
344 /**
345  * Add routing change listener
346  * @param listener routing change listener
347  */
348 void
349 os_routing_linux_listener_add(struct os_route_listener *listener) {
350   list_add_tail(&_rtnetlink_listener, &listener->_internal._node);
351 }
352
353 /**
354  * Remove routing change listener
355  * @param listener routing change listener
356  */
357 void
358 os_routing_linux_listener_remove(struct os_route_listener *listener) {
359   list_remove(&listener->_internal._node);
360 }
361
362 /**
363  * Initializes a route with default values. Will zero all
364  * other fields in the struct.
365  * @param route route to be initialized
366  */
367 void
368 os_routing_linux_init_wildcard_route(struct os_route *route) {
369   memset(route, 0, sizeof(*route));
370   memcpy(&route->p, &OS_ROUTE_WILDCARD, sizeof(route->p));
371 }
372
373 /**
374  * Stop processing of a routing command and set error code
375  * for callback
376  * @param route pointer to os_route
377  * @param error error code, 0 if no error
378  */
379 static void
380 _routing_finished(struct os_route *route, int error) {
381   /* remove first to prevent any kind of recursive cleanup */
382   avl_remove(&_rtnetlink_feedback, &route->_internal._node);
383
384   if (route->cb_finished) {
385     route->cb_finished(route, error);
386   }
387 }
388
389 /**
390  * Initiatize the an netlink routing message
391  * @param msg pointer to netlink message header
392  * @param route data to be added to the netlink message
393  * @param rt_scope scope of route to be set/removed
394  * @return -1 if an error happened, 0 otherwise
395  */
396 static int
397 _routing_set(struct nlmsghdr *msg, struct os_route *route,
398     unsigned char rt_scope) {
399   struct rtmsg *rt_msg;
400   size_t i;
401
402   /* calculate address af_type */
403   if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) {
404     route->p.family = netaddr_get_address_family(&route->p.key.dst);
405   }
406   if (netaddr_get_address_family(&route->p.gw) != AF_UNSPEC) {
407     if (route->p.family  != AF_UNSPEC
408         && route->p.family  != netaddr_get_address_family(&route->p.gw)) {
409       return -1;
410     }
411     route->p.family  = netaddr_get_address_family(&route->p.gw);
412   }
413   if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) {
414     if (route->p.family  != AF_UNSPEC && route->p.family  != netaddr_get_address_family(&route->p.src_ip)) {
415       return -1;
416     }
417     route->p.family  = netaddr_get_address_family(&route->p.src_ip);
418   }
419
420   if (route->p.family  == AF_UNSPEC) {
421     route->p.family  = AF_INET;
422   }
423
424   /* initialize rtmsg payload */
425   rt_msg = NLMSG_DATA(msg);
426
427   rt_msg->rtm_family = route->p.family ;
428   rt_msg->rtm_scope = rt_scope;
429   rt_msg->rtm_protocol = route->p.protocol;
430   rt_msg->rtm_table = route->p.table;
431
432   /* set default route type */
433   rt_msg->rtm_type = RTN_UNICAST;
434
435   /* set route type */
436   for (i=0; i<ARRAYSIZE(_type_translation); i++) {
437     if (_type_translation[i].oonf == route->p.type) {
438       rt_msg->rtm_type = _type_translation[i].os_linux;
439       break;
440     }
441   }
442
443   /* add attributes */
444   if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) {
445     /* add src-ip */
446     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket,
447         msg, RTA_PREFSRC, &route->p.src_ip)) {
448       return -1;
449     }
450   }
451
452   if (netaddr_get_address_family(&route->p.gw) != AF_UNSPEC) {
453     rt_msg->rtm_flags |= RTNH_F_ONLINK;
454
455     /* add gateway */
456     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket,
457         msg, RTA_GATEWAY, &route->p.gw)) {
458       return -1;
459     }
460   }
461
462   if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) {
463     rt_msg->rtm_dst_len = netaddr_get_prefix_length(&route->p.key.dst);
464
465     /* add destination */
466     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket,
467         msg, RTA_DST, &route->p.key.dst)) {
468       return -1;
469     }
470   }
471
472   if (netaddr_get_address_family(&route->p.key.src) == AF_INET6
473       && netaddr_get_prefix_length(&route->p.key.src) != 0) {
474     rt_msg->rtm_src_len = netaddr_get_prefix_length(&route->p.key.src);
475
476     /* add source-specific routing prefix */
477     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket,
478         msg, RTA_SRC, &route->p.key.src)) {
479       return -1;
480     }
481   }
482
483   if (route->p.metric != -1) {
484     /* add metric */
485     if (os_system_linux_netlink_addreq(&_rtnetlink_socket,
486         msg, RTA_PRIORITY, &route->p.metric, sizeof(route->p.metric))) {
487       return -1;
488     }
489   }
490
491   if (route->p.if_index) {
492     /* add interface*/
493     if (os_system_linux_netlink_addreq(&_rtnetlink_socket,
494         msg, RTA_OIF, &route->p.if_index, sizeof(route->p.if_index))) {
495       return -1;
496     }
497   }
498   return 0;
499 }
500
501 /**
502  * Parse a rtnetlink header into a os_route object
503  * @param route pointer to target os_route
504  * @param msg pointer to rtnetlink message header
505  * @return -1 if address family of rtnetlink is unknown,
506  *   1 if the entry should be ignored, 0 otherwise
507  */
508 static int
509 _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) {
510   struct rtmsg *rt_msg;
511   struct rtattr *rt_attr;
512   int rt_len;
513   size_t i;
514
515   rt_msg = NLMSG_DATA(msg);
516   rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
517   rt_len = RTM_PAYLOAD(msg);
518
519   if ((rt_msg->rtm_flags & RTM_F_CLONED) != 0) {
520     OONF_DEBUG(LOG_OS_ROUTING, "Received a cloned route");
521     /* ignore cloned route events by returning the wildcard route */
522     return 1;
523   }
524
525   memcpy(&route->p, &OS_ROUTE_WILDCARD, sizeof(OS_ROUTE_WILDCARD));
526
527   route->p.protocol = rt_msg->rtm_protocol;
528   route->p.table = rt_msg->rtm_table;
529   route->p.family = rt_msg->rtm_family;
530
531   if (route->p.family != AF_INET && route->p.family != AF_INET6) {
532     OONF_WARN(LOG_OS_ROUTING, "Got illegal route address family: %d", route->p.family);
533     return -1;
534   }
535
536   /* get route type */
537   route->p.type = OS_ROUTE_UNDEFINED;
538   for (i=0; i<ARRAYSIZE(_type_translation); i++) {
539     if (rt_msg->rtm_type == _type_translation[i].os_linux) {
540       route->p.type = _type_translation[i].oonf;
541       break;
542     }
543   }
544   if (route->p.type == OS_ROUTE_UNDEFINED) {
545     OONF_DEBUG(LOG_OS_ROUTING, "Got route type: %u", rt_msg->rtm_type);
546     return 2;
547   }
548
549   for(; RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) {
550     switch(rt_attr->rta_type) {
551       case RTA_PREFSRC:
552         netaddr_from_binary(&route->p.src_ip, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
553             rt_msg->rtm_family);
554         break;
555       case RTA_GATEWAY:
556         netaddr_from_binary(&route->p.gw, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
557         break;
558       case RTA_DST:
559         netaddr_from_binary_prefix(&route->p.key.dst, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
560             rt_msg->rtm_family, rt_msg->rtm_dst_len);
561         break;
562       case RTA_SRC:
563         netaddr_from_binary_prefix(&route->p.key.src, RTA_DATA(rt_attr),
564             RTA_PAYLOAD(rt_attr), rt_msg->rtm_family, rt_msg->rtm_src_len);
565         break;
566       case RTA_PRIORITY:
567         memcpy(&route->p.metric, RTA_DATA(rt_attr), sizeof(route->p.metric));
568         break;
569       case RTA_OIF:
570         memcpy(&route->p.if_index, RTA_DATA(rt_attr), sizeof(route->p.if_index));
571         break;
572       default:
573         break;
574     }
575   }
576
577   if (netaddr_get_address_family(&route->p.key.dst) == AF_UNSPEC) {
578     memcpy(&route->p.key.dst, route->p.family == AF_INET ? &NETADDR_IPV4_ANY : &NETADDR_IPV6_ANY,
579         sizeof(route->p.key.dst));
580     netaddr_set_prefix_length(&route->p.key.dst, rt_msg->rtm_dst_len);
581   }
582   return 0;
583 }
584
585 /**
586  * Checks if a os_route object matches a routing filter
587  * @param filter pointer to filter
588  * @param route pointer to route object
589  * @return true if route matches the filter, false otherwise
590  */
591 static bool
592 _match_routes(struct os_route *filter, struct os_route *route) {
593   if (filter->p.family != AF_UNSPEC && filter->p.family != route->p.family) {
594     return false;
595   }
596   if (netaddr_get_address_family(&filter->p.src_ip) != AF_UNSPEC
597       && memcmp(&filter->p.src_ip, &route->p.src_ip, sizeof(filter->p.src_ip)) != 0) {
598     return false;
599   }
600   if (filter->p.type != OS_ROUTE_UNDEFINED && filter->p.type != route->p.type) {
601     return false;
602   }
603   if (netaddr_get_address_family(&filter->p.gw) != AF_UNSPEC
604       && memcmp(&filter->p.gw, &route->p.gw, sizeof(filter->p.gw)) != 0) {
605     return false;
606   }
607   if (netaddr_get_address_family(&filter->p.key.dst) != AF_UNSPEC
608       && memcmp(&filter->p.key.dst, &route->p.key.dst, sizeof(filter->p.key.dst)) != 0) {
609     return false;
610   }
611   if (netaddr_get_address_family(&filter->p.key.src) != AF_UNSPEC
612       && memcmp(&filter->p.key.src, &route->p.key.src, sizeof(filter->p.key.src)) != 0) {
613     return false;
614   }
615   if (filter->p.metric != -1 && filter->p.metric != route->p.metric) {
616     return false;
617   }
618   if (filter->p.table != RT_TABLE_UNSPEC && filter->p.table != route->p.table) {
619     return false;
620   }
621   if (filter->p.protocol != RTPROT_UNSPEC && filter->p.protocol != route->p.protocol) {
622     return false;
623   }
624   return filter->p.if_index == 0 || filter->p.if_index == route->p.if_index;
625 }
626
627 /**
628  * Handle incoming rtnetlink messages
629  * @param msg netlink message including header
630  */
631 static void
632 _cb_rtnetlink_message(struct nlmsghdr *msg) {
633   struct os_route_listener *listener;
634   struct os_route *filter;
635   struct os_route rt;
636   int result;
637
638   struct os_route_str rbuf;
639
640   OONF_DEBUG(LOG_OS_ROUTING, "Got message: %d %d 0x%04x", msg->nlmsg_seq, msg->nlmsg_type, msg->nlmsg_flags);
641
642   if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) {
643     return;
644   }
645
646   if ((result = _routing_parse_nlmsg(&rt, msg))) {
647     if (result < 0) {
648       OONF_WARN(LOG_OS_ROUTING, "Error while processing route reply (result %d)", result);
649     }
650     return;
651   }
652
653   OONF_DEBUG(LOG_OS_ROUTING, "Content: %s", os_routing_to_string(&rbuf, &rt.p));
654
655   if (msg->nlmsg_seq == 0) {
656     /* send route events to listeners */
657     list_for_each_element(&_rtnetlink_listener, listener, _internal._node) {
658       listener->cb_get(&rt, msg->nlmsg_type == RTM_NEWROUTE);
659     }
660   }
661   else {
662     /* check for feedback for ongoing route commands */
663     filter = avl_find_element(&_rtnetlink_feedback, &msg->nlmsg_seq, filter, _internal._node);
664     if (filter) {
665       if (filter->cb_get != NULL && _match_routes(filter, &rt)) {
666         filter->cb_get(filter, &rt);
667       }
668     }
669   }
670 }
671
672 /**
673  * Handle feedback from netlink socket
674  * @param seq sequence number of netlink response
675  * @param err error value
676  */
677 static void
678 _cb_rtnetlink_error(uint32_t seq, int err) {
679   struct os_route *route;
680 #ifdef OONF_LOG_DEBUG_INFO
681   struct os_route_str rbuf;
682 #endif
683
684   route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node);
685   if (route) {
686     OONF_DEBUG(LOG_OS_ROUTING, "Route seqno %u failed: %s (%d) %s",
687         seq, strerror(err), err,
688         os_routing_to_string(&rbuf, &route->p));
689
690     _routing_finished(route, err);
691   }
692   else {
693     OONF_DEBUG(LOG_OS_ROUTING, "Unknown route with seqno %u failed: %s (%d)",
694         seq, strerror(err), err);
695   }
696 }
697
698 /**
699  * Handle ack timeout from netlink socket
700  */
701 static void
702 _cb_rtnetlink_timeout(void) {
703   struct os_route *route, *rt_it;
704
705   OONF_WARN(LOG_OS_ROUTING, "Netlink timeout for routing");
706
707   avl_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) {
708     _routing_finished(route, -1);
709   }
710 }
711
712 /**
713  * Handle done from multipart netlink messages
714  * @param seq netlink sequence number
715  */
716 static void
717 _cb_rtnetlink_done(uint32_t seq) {
718   struct os_route *route;
719 #ifdef OONF_LOG_DEBUG_INFO
720   struct os_route_str rbuf;
721 #endif
722
723   OONF_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq);
724
725   route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node);
726   if (route) {
727     OONF_DEBUG(LOG_OS_ROUTING, "Route %s with seqno %u done",
728         os_routing_to_string(&rbuf, &route->p), seq);
729     _routing_finished(route, 0);
730   }
731 }