Add "config query" command to remotecontrol plugin to query a configuration value...
[oonf.git] / src / 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 <oonf/libcommon/avl.h>
55 #include <oonf/libcommon/avl_comp.h>
56 #include <oonf/oonf.h>
57 #include <oonf/libcore/oonf_subsystem.h>
58 #include <oonf/subsystems/os_system.h>
59
60 #include <oonf/subsystems/os_linux/os_routing_linux.h>
61 #include <oonf/subsystems/os_routing.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, unsigned char rt_scope);
82
83 static void _routing_finished(struct os_route *route, int error);
84 static void _cb_rtnetlink_message(struct nlmsghdr *);
85 static void _cb_rtnetlink_error(uint32_t seq, int err);
86 static void _cb_rtnetlink_done(uint32_t seq);
87 static void _cb_rtnetlink_timeout(void);
88
89 /* subsystem definition */
90 static const char *_dependencies[] = {
91   OONF_OS_SYSTEM_SUBSYSTEM,
92 };
93
94 static struct oonf_subsystem _oonf_os_routing_subsystem = {
95   .name = OONF_OS_ROUTING_SUBSYSTEM,
96   .dependencies = _dependencies,
97   .dependencies_count = ARRAYSIZE(_dependencies),
98   .init = _init,
99   .cleanup = _cleanup,
100 };
101 DECLARE_OONF_PLUGIN(_oonf_os_routing_subsystem);
102
103 /* translation table between route types */
104 static struct route_type_translation _type_translation[] = { { OS_ROUTE_UNICAST, RTN_UNICAST },
105   { OS_ROUTE_LOCAL, RTN_LOCAL }, { OS_ROUTE_BROADCAST, RTN_BROADCAST }, { OS_ROUTE_MULTICAST, RTN_MULTICAST },
106   { OS_ROUTE_THROW, RTN_THROW }, { OS_ROUTE_UNREACHABLE, RTN_UNREACHABLE }, { OS_ROUTE_PROHIBIT, RTN_PROHIBIT },
107   { OS_ROUTE_BLACKHOLE, RTN_BLACKHOLE }, { OS_ROUTE_NAT, RTN_NAT } };
108
109 /* netlink socket for route set/get commands */
110 static const uint32_t _rtnetlink_mcast[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE };
111
112 static struct os_system_netlink _rtnetlink_socket = {
113   .name = "routing",
114   .used_by = &_oonf_os_routing_subsystem,
115   .cb_message = _cb_rtnetlink_message,
116   .cb_error = _cb_rtnetlink_error,
117   .cb_done = _cb_rtnetlink_done,
118   .cb_timeout = _cb_rtnetlink_timeout,
119 };
120
121 static struct avl_tree _rtnetlink_feedback;
122 static struct list_entity _rtnetlink_listener;
123
124 /* default wildcard route */
125 static const struct os_route_parameter OS_ROUTE_WILDCARD = { .family = AF_UNSPEC,
126   .src_ip = { ._type = AF_UNSPEC },
127   .gw = { ._type = AF_UNSPEC },
128   .type = OS_ROUTE_UNDEFINED,
129   .key =
130     {
131       .dst = { ._type = AF_UNSPEC },
132       .src = { ._type = AF_UNSPEC },
133     },
134   .table = RT_TABLE_UNSPEC,
135   .metric = -1,
136   .protocol = RTPROT_UNSPEC,
137   .if_index = 0 };
138
139 /* kernel version check */
140 static bool _is_kernel_3_11_0_or_better;
141
142 /**
143  * Initialize routing subsystem
144  * @return -1 if an error happened, 0 otherwise
145  */
146 static int
147 _init(void) {
148   if (os_system_linux_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) {
149     return -1;
150   }
151   if (os_system_linux_netlink_add_mc(&_rtnetlink_socket, _rtnetlink_mcast, ARRAYSIZE(_rtnetlink_mcast))) {
152     os_system_linux_netlink_remove(&_rtnetlink_socket);
153     return -1;
154   }
155   avl_init(&_rtnetlink_feedback, avl_comp_uint32, false);
156   list_init_head(&_rtnetlink_listener);
157
158   _is_kernel_3_11_0_or_better = os_system_linux_is_minimal_kernel(3, 11, 0);
159   return 0;
160 }
161
162 /**
163  * Cleanup all resources allocated by the routing subsystem
164  */
165 static void
166 _cleanup(void) {
167   struct os_route *rt, *rt_it;
168
169   avl_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) {
170     _routing_finished(rt, 1);
171   }
172
173   os_system_linux_netlink_remove(&_rtnetlink_socket);
174 }
175
176 /**
177  * Check if kernel supports source-specific routing
178  * @param af_family address family
179  * @return true if source-specific routing is supported for
180  *   address family
181  */
182 bool
183 os_routing_linux_supports_source_specific(int af_family) {
184   if (af_family == AF_INET) {
185     return false;
186   }
187
188   /* TODO: better check for source specific routing necessary! */
189   return _is_kernel_3_11_0_or_better;
190 }
191
192 /**
193  * Update an entry of the kernel routing table. This call will only trigger
194  * the change, the real change will be done as soon as the netlink socket is
195  * writable.
196  * @param route data of route to be set/removed
197  * @param set true if route should be set, false if it should be removed
198  * @param del_similar true if similar routes that block this one should be
199  *   removed.
200  * @return -1 if an error happened, 0 otherwise
201  */
202 int
203 os_routing_linux_set(struct os_route *route, bool set, bool del_similar) {
204   uint8_t buffer[UIO_MAXIOV];
205   struct nlmsghdr *msg;
206   unsigned char scope;
207   struct os_route os_rt;
208   int seq;
209   struct os_route_str rbuf;
210
211   memset(buffer, 0, sizeof(buffer));
212
213   /* copy route settings */
214   memcpy(&os_rt, route, sizeof(os_rt));
215
216   /* get pointers for netlink message */
217   msg = (void *)&buffer[0];
218
219   msg->nlmsg_flags = NLM_F_REQUEST;
220
221   /* set length of netlink message with rtmsg payload */
222   msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
223
224   /* normally all routing operations are UNIVERSE scope */
225   scope = RT_SCOPE_UNIVERSE;
226
227   if (set) {
228     msg->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
229     msg->nlmsg_type = RTM_NEWROUTE;
230   }
231   else {
232     msg->nlmsg_type = RTM_DELROUTE;
233
234     os_rt.p.protocol = 0;
235     netaddr_invalidate(&os_rt.p.src_ip);
236
237     if (del_similar) {
238       /* no interface necessary */
239       os_rt.p.if_index = 0;
240
241       /* as wildcard for fuzzy deletion */
242       scope = RT_SCOPE_NOWHERE;
243     }
244   }
245
246   if (netaddr_is_unspec(&os_rt.p.gw) && netaddr_get_address_family(&os_rt.p.key.dst) == AF_INET &&
247       netaddr_get_prefix_length(&os_rt.p.key.dst) == netaddr_get_maxprefix(&os_rt.p.key.dst)) {
248     /* use destination as gateway, to 'force' linux kernel to do proper source address selection */
249     memcpy(&os_rt.p.gw, &os_rt.p.key.dst, sizeof(os_rt.p.gw));
250   }
251
252   OONF_DEBUG(LOG_OS_ROUTING, "%sset route: %s", set ? "" : "re", os_routing_to_string(&rbuf, &os_rt.p));
253
254   if (_routing_set(msg, &os_rt, scope)) {
255     return -1;
256   }
257
258   /* cannot fail */
259   seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg);
260
261   if (route->cb_finished) {
262     route->_internal.nl_seq = seq;
263     route->_internal._node.key = &route->_internal.nl_seq;
264
265     OONF_ASSERT(!avl_is_node_added(&route->_internal._node),
266                 LOG_OS_ROUTING, "route %s is already in feedback list!",
267                 os_routing_to_string(&rbuf, &os_rt.p));
268     avl_insert(&_rtnetlink_feedback, &route->_internal._node);
269   }
270   return 0;
271 }
272
273 /**
274  * Request all routing data of a certain address family
275  * @param route pointer to routing filter
276  * @return -1 if an error happened, 0 otherwise
277  */
278 int
279 os_routing_linux_query(struct os_route *route) {
280   uint8_t buffer[UIO_MAXIOV];
281   struct nlmsghdr *msg;
282   struct rtgenmsg *rt_gen;
283   int seq;
284
285   OONF_ASSERT(route->cb_finished != NULL && route->cb_get != NULL, LOG_OS_ROUTING, "illegal route query");
286   memset(buffer, 0, sizeof(buffer));
287
288   /* get pointers for netlink message */
289   msg = (void *)&buffer[0];
290   rt_gen = NLMSG_DATA(msg);
291
292   msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
293
294   /* set length of netlink message with rtmsg payload */
295   msg->nlmsg_len = NLMSG_LENGTH(sizeof(*rt_gen));
296
297   msg->nlmsg_type = RTM_GETROUTE;
298   rt_gen->rtgen_family = route->p.family;
299
300   seq = os_system_linux_netlink_send(&_rtnetlink_socket, msg);
301   if (seq < 0) {
302     return -1;
303   }
304
305   route->_internal.nl_seq = seq;
306   route->_internal._node.key = &route->_internal.nl_seq;
307   avl_insert(&_rtnetlink_feedback, &route->_internal._node);
308   return 0;
309 }
310
311 /**
312  * Stop processing of a routing command
313  * @param route pointer to os_route
314  */
315 void
316 os_routing_linux_interrupt(struct os_route *route) {
317   if (os_routing_linux_is_in_progress(route)) {
318     _routing_finished(route, -1);
319   }
320 }
321
322 /**
323  * @param route os route
324  * @return true if route is being processed by the kernel,
325  *   false otherwise
326  */
327 bool
328 os_routing_linux_is_in_progress(struct os_route *route) {
329   return avl_is_node_added(&route->_internal._node);
330 }
331
332 /**
333  * Add routing change listener
334  * @param listener routing change listener
335  */
336 void
337 os_routing_linux_listener_add(struct os_route_listener *listener) {
338   list_add_tail(&_rtnetlink_listener, &listener->_internal._node);
339 }
340
341 /**
342  * Remove routing change listener
343  * @param listener routing change listener
344  */
345 void
346 os_routing_linux_listener_remove(struct os_route_listener *listener) {
347   list_remove(&listener->_internal._node);
348 }
349
350 /**
351  * Initializes a route with default values. Will zero all
352  * other fields in the struct.
353  * @param route route to be initialized
354  */
355 void
356 os_routing_linux_init_wildcard_route(struct os_route *route) {
357   memset(route, 0, sizeof(*route));
358   memcpy(&route->p, &OS_ROUTE_WILDCARD, sizeof(route->p));
359 }
360
361 /**
362  * Stop processing of a routing command and set error code
363  * for callback
364  * @param route pointer to os_route
365  * @param error error code, 0 if no error
366  */
367 static void
368 _routing_finished(struct os_route *route, int error) {
369   /* remove first to prevent any kind of recursive cleanup */
370   avl_remove(&_rtnetlink_feedback, &route->_internal._node);
371
372   if (route->cb_finished) {
373     route->cb_finished(route, error);
374   }
375 }
376
377 /**
378  * Initiatize the an netlink routing message
379  * @param msg pointer to netlink message header
380  * @param route data to be added to the netlink message
381  * @param rt_scope scope of route to be set/removed
382  * @return -1 if an error happened, 0 otherwise
383  */
384 static int
385 _routing_set(struct nlmsghdr *msg, struct os_route *route, unsigned char rt_scope) {
386   struct rtmsg *rt_msg;
387   size_t i;
388
389   /* calculate address af_type */
390   if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) {
391     route->p.family = netaddr_get_address_family(&route->p.key.dst);
392   }
393   if (netaddr_get_address_family(&route->p.gw) != AF_UNSPEC) {
394     if (route->p.family != AF_UNSPEC && route->p.family != netaddr_get_address_family(&route->p.gw)) {
395       return -1;
396     }
397     route->p.family = netaddr_get_address_family(&route->p.gw);
398   }
399   if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) {
400     if (route->p.family != AF_UNSPEC && route->p.family != netaddr_get_address_family(&route->p.src_ip)) {
401       return -1;
402     }
403     route->p.family = netaddr_get_address_family(&route->p.src_ip);
404   }
405
406   if (route->p.family == AF_UNSPEC) {
407     route->p.family = AF_INET;
408   }
409
410   /* initialize rtmsg payload */
411   rt_msg = NLMSG_DATA(msg);
412
413   rt_msg->rtm_family = route->p.family;
414   rt_msg->rtm_scope = rt_scope;
415   rt_msg->rtm_protocol = route->p.protocol;
416   rt_msg->rtm_table = route->p.table;
417
418   /* set default route type */
419   rt_msg->rtm_type = RTN_UNICAST;
420
421   /* set route type */
422   for (i = 0; i < ARRAYSIZE(_type_translation); i++) {
423     if (_type_translation[i].oonf == route->p.type) {
424       rt_msg->rtm_type = _type_translation[i].os_linux;
425       break;
426     }
427   }
428
429   /* add attributes */
430   if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) {
431     /* add src-ip */
432     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_PREFSRC, &route->p.src_ip)) {
433       return -1;
434     }
435   }
436
437   if (netaddr_get_address_family(&route->p.gw) != AF_UNSPEC) {
438     rt_msg->rtm_flags |= RTNH_F_ONLINK;
439
440     /* add gateway */
441     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_GATEWAY, &route->p.gw)) {
442       return -1;
443     }
444   }
445
446   if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) {
447     rt_msg->rtm_dst_len = netaddr_get_prefix_length(&route->p.key.dst);
448
449     /* add destination */
450     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_DST, &route->p.key.dst)) {
451       return -1;
452     }
453   }
454
455   if (netaddr_get_address_family(&route->p.key.src) == AF_INET6 && netaddr_get_prefix_length(&route->p.key.src) != 0) {
456     rt_msg->rtm_src_len = netaddr_get_prefix_length(&route->p.key.src);
457
458     /* add source-specific routing prefix */
459     if (os_system_linux_netlink_addnetaddr(&_rtnetlink_socket, msg, RTA_SRC, &route->p.key.src)) {
460       return -1;
461     }
462   }
463
464   if (route->p.metric != -1) {
465     /* add metric */
466     if (os_system_linux_netlink_addreq(
467           &_rtnetlink_socket, msg, RTA_PRIORITY, &route->p.metric, sizeof(route->p.metric))) {
468       return -1;
469     }
470   }
471
472   if (route->p.if_index) {
473     /* add interface*/
474     if (os_system_linux_netlink_addreq(
475           &_rtnetlink_socket, msg, RTA_OIF, &route->p.if_index, sizeof(route->p.if_index))) {
476       return -1;
477     }
478   }
479   return 0;
480 }
481
482 /**
483  * Parse a rtnetlink header into a os_route object
484  * @param route pointer to target os_route
485  * @param msg pointer to rtnetlink message header
486  * @return -1 if address family of rtnetlink is unknown,
487  *   1 if the entry should be ignored, 0 otherwise
488  */
489 static int
490 _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) {
491   struct rtmsg *rt_msg;
492   struct rtattr *rt_attr;
493   int rt_len;
494   size_t i;
495
496   rt_msg = NLMSG_DATA(msg);
497   rt_attr = (struct rtattr *)RTM_RTA(rt_msg);
498   rt_len = RTM_PAYLOAD(msg);
499
500   if ((rt_msg->rtm_flags & RTM_F_CLONED) != 0) {
501     OONF_DEBUG(LOG_OS_ROUTING, "Received a cloned route");
502     /* ignore cloned route events by returning the wildcard route */
503     return 1;
504   }
505
506   memcpy(&route->p, &OS_ROUTE_WILDCARD, sizeof(OS_ROUTE_WILDCARD));
507
508   route->p.protocol = rt_msg->rtm_protocol;
509   route->p.table = rt_msg->rtm_table;
510   route->p.family = rt_msg->rtm_family;
511
512   if (route->p.family != AF_INET && route->p.family != AF_INET6) {
513     OONF_WARN(LOG_OS_ROUTING, "Got illegal route address family: %d", route->p.family);
514     return -1;
515   }
516
517   /* get route type */
518   route->p.type = OS_ROUTE_UNDEFINED;
519   for (i = 0; i < ARRAYSIZE(_type_translation); i++) {
520     if (rt_msg->rtm_type == _type_translation[i].os_linux) {
521       route->p.type = _type_translation[i].oonf;
522       break;
523     }
524   }
525   if (route->p.type == OS_ROUTE_UNDEFINED) {
526     OONF_DEBUG(LOG_OS_ROUTING, "Got route type: %u", rt_msg->rtm_type);
527     return 2;
528   }
529
530   for (; RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr, rt_len)) {
531     switch (rt_attr->rta_type) {
532       case RTA_PREFSRC:
533         netaddr_from_binary(&route->p.src_ip, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
534         break;
535       case RTA_GATEWAY:
536         netaddr_from_binary(&route->p.gw, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
537         break;
538       case RTA_DST:
539         netaddr_from_binary_prefix(
540           &route->p.key.dst, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family, rt_msg->rtm_dst_len);
541         break;
542       case RTA_SRC:
543         netaddr_from_binary_prefix(
544           &route->p.key.src, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family, rt_msg->rtm_src_len);
545         break;
546       case RTA_PRIORITY:
547         memcpy(&route->p.metric, RTA_DATA(rt_attr), sizeof(route->p.metric));
548         break;
549       case RTA_OIF:
550         memcpy(&route->p.if_index, RTA_DATA(rt_attr), sizeof(route->p.if_index));
551         break;
552       default:
553         break;
554     }
555   }
556
557   if (netaddr_get_address_family(&route->p.key.dst) == AF_UNSPEC) {
558     memcpy(
559       &route->p.key.dst, route->p.family == AF_INET ? &NETADDR_IPV4_ANY : &NETADDR_IPV6_ANY, sizeof(route->p.key.dst));
560     netaddr_set_prefix_length(&route->p.key.dst, rt_msg->rtm_dst_len);
561   }
562   return 0;
563 }
564
565 /**
566  * Checks if a os_route object matches a routing filter
567  * @param filter pointer to filter
568  * @param route pointer to route object
569  * @return true if route matches the filter, false otherwise
570  */
571 static bool
572 _match_routes(struct os_route *filter, struct os_route *route) {
573   if (filter->p.family != AF_UNSPEC && filter->p.family != route->p.family) {
574     return false;
575   }
576   if (netaddr_get_address_family(&filter->p.src_ip) != AF_UNSPEC &&
577       memcmp(&filter->p.src_ip, &route->p.src_ip, sizeof(filter->p.src_ip)) != 0) {
578     return false;
579   }
580   if (filter->p.type != OS_ROUTE_UNDEFINED && filter->p.type != route->p.type) {
581     return false;
582   }
583   if (netaddr_get_address_family(&filter->p.gw) != AF_UNSPEC &&
584       memcmp(&filter->p.gw, &route->p.gw, sizeof(filter->p.gw)) != 0) {
585     return false;
586   }
587   if (netaddr_get_address_family(&filter->p.key.dst) != AF_UNSPEC &&
588       memcmp(&filter->p.key.dst, &route->p.key.dst, sizeof(filter->p.key.dst)) != 0) {
589     return false;
590   }
591   if (netaddr_get_address_family(&filter->p.key.src) != AF_UNSPEC &&
592       memcmp(&filter->p.key.src, &route->p.key.src, sizeof(filter->p.key.src)) != 0) {
593     return false;
594   }
595   if (filter->p.metric != -1 && filter->p.metric != route->p.metric) {
596     return false;
597   }
598   if (filter->p.table != RT_TABLE_UNSPEC && filter->p.table != route->p.table) {
599     return false;
600   }
601   if (filter->p.protocol != RTPROT_UNSPEC && filter->p.protocol != route->p.protocol) {
602     return false;
603   }
604   return filter->p.if_index == 0 || filter->p.if_index == route->p.if_index;
605 }
606
607 /**
608  * Handle incoming rtnetlink messages
609  * @param msg netlink message including header
610  */
611 static void
612 _cb_rtnetlink_message(struct nlmsghdr *msg) {
613   struct os_route_listener *listener;
614   struct os_route *filter;
615   struct os_route rt;
616   int result;
617
618 #ifdef OONF_LOG_DEBUG_INFO
619   struct os_route_str rbuf;
620 #endif
621   OONF_DEBUG(LOG_OS_ROUTING, "Got message: %d %d 0x%04x", msg->nlmsg_seq, msg->nlmsg_type, msg->nlmsg_flags);
622
623   if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) {
624     return;
625   }
626
627   if ((result = _routing_parse_nlmsg(&rt, msg))) {
628     if (result < 0) {
629       OONF_WARN(LOG_OS_ROUTING, "Error while processing route reply (result %d)", result);
630     }
631     return;
632   }
633
634   OONF_DEBUG(LOG_OS_ROUTING, "Content: %s", os_routing_to_string(&rbuf, &rt.p));
635
636   /*
637    * sometimes netlink messages from the kernel have a (random?) sequence number, so
638    * we deliver everything with seq==0 and with an unregistered sequence number
639    * to the attached listeners now
640    */
641   filter = NULL;
642   if (msg->nlmsg_seq != 0) {
643     /* check for feedback for ongoing route commands */
644     filter = avl_find_element(&_rtnetlink_feedback, &msg->nlmsg_seq, filter, _internal._node);
645   }
646
647   if (filter) {
648     if (filter->cb_get != NULL && _match_routes(filter, &rt)) {
649       filter->cb_get(filter, &rt);
650     }
651   }
652   else {
653     /* send route events to listeners */
654     list_for_each_element(&_rtnetlink_listener, listener, _internal._node) {
655       listener->cb_get(&rt, msg->nlmsg_type == RTM_NEWROUTE);
656     }
657   }
658 }
659
660 /**
661  * Handle feedback from netlink socket
662  * @param seq sequence number of netlink response
663  * @param err error value
664  */
665 static void
666 _cb_rtnetlink_error(uint32_t seq, int err) {
667   struct os_route *route;
668 #ifdef OONF_LOG_DEBUG_INFO
669   struct os_route_str rbuf;
670 #endif
671
672   route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node);
673   if (route) {
674     OONF_DEBUG(LOG_OS_ROUTING, "Route seqno %u failed: %s (%d) %s", seq, strerror(err), err,
675       os_routing_to_string(&rbuf, &route->p));
676
677     _routing_finished(route, err);
678   }
679   else {
680     OONF_DEBUG(LOG_OS_ROUTING, "Unknown route with seqno %u failed: %s (%d)", seq, strerror(err), err);
681   }
682 }
683
684 /**
685  * Handle ack timeout from netlink socket
686  */
687 static void
688 _cb_rtnetlink_timeout(void) {
689   struct os_route *route, *rt_it;
690
691   OONF_WARN(LOG_OS_ROUTING, "Netlink timeout for routing");
692
693   avl_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) {
694     _routing_finished(route, -1);
695   }
696 }
697
698 /**
699  * Handle done from multipart netlink messages
700  * @param seq netlink sequence number
701  */
702 static void
703 _cb_rtnetlink_done(uint32_t seq) {
704   struct os_route *route;
705 #ifdef OONF_LOG_DEBUG_INFO
706   struct os_route_str rbuf;
707 #endif
708
709   OONF_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq);
710
711   route = avl_find_element(&_rtnetlink_feedback, &seq, route, _internal._node);
712   if (route) {
713     OONF_DEBUG(LOG_OS_ROUTING, "Route %s with seqno %u done", os_routing_to_string(&rbuf, &route->p), seq);
714     _routing_finished(route, 0);
715   }
716 }