d89e40cdf06d1de35e9fc262bc6c5dace2bc99f7
[oonf.git] / src-api / core / os_linux / os_routing_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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 /* must be first because of a problem with linux/rtnetlink.h */
43 #include <sys/socket.h>
44
45 /* and now the rest of the includes */
46 #include <linux/netlink.h>
47 #include <linux/rtnetlink.h>
48 #include <sys/utsname.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <unistd.h>
52
53 #include "common/common_types.h"
54 #include "core/olsr_timer.h"
55 #include "core/olsr_subsystem.h"
56 #include "core/os_routing.h"
57 #include "core/os_system.h"
58
59 /* ip forwarding */
60 #define PROC_IPFORWARD_V4 "/proc/sys/net/ipv4/ip_forward"
61 #define PROC_IPFORWARD_V6 "/proc/sys/net/ipv6/conf/all/forwarding"
62
63 /* Redirect proc entry */
64 #define PROC_IF_REDIRECT "/proc/sys/net/ipv4/conf/%s/send_redirects"
65 #define PROC_ALL_REDIRECT "/proc/sys/net/ipv4/conf/all/send_redirects"
66
67 /* IP spoof proc entry */
68 #define PROC_IF_SPOOF "/proc/sys/net/ipv4/conf/%s/rp_filter"
69 #define PROC_ALL_SPOOF "/proc/sys/net/ipv4/conf/all/rp_filter"
70
71 /* prototypes */
72 static int _init(void);
73 static void _cleanup(void);
74
75 static int _routing_set(struct nlmsghdr *msg, struct os_route *route,
76     unsigned char rt_type, unsigned char rt_scope);
77
78 static bool _is_at_least_linuxkernel_2_6_31(void);
79 static int _os_linux_writeToProc(const char *file, char *old, char value);
80
81 static void _routing_finished(struct os_route *route, int error);
82 static void _cb_rtnetlink_message(struct nlmsghdr *);
83 static void _cb_rtnetlink_error(uint32_t seq, int error);
84 static void _cb_rtnetlink_done(uint32_t seq);
85 static void _cb_rtnetlink_timeout(void);
86
87 /* global procfile state before initialization */
88 static char _original_rp_filter;
89 static char _original_icmp_redirect;
90 static char _original_ipv4_forward;
91 static char _original_ipv6_forward;
92
93 /* counter of mesh interfaces for ip_forward configuration */
94 static int _mesh_count = 0;
95
96 /* netlink socket for route set/get commands */
97 struct os_system_netlink _rtnetlink_socket = {
98   .cb_message = _cb_rtnetlink_message,
99   .cb_error = _cb_rtnetlink_error,
100   .cb_done = _cb_rtnetlink_done,
101   .cb_timeout = _cb_rtnetlink_timeout,
102 };
103 struct list_entity _rtnetlink_feedback;
104
105 /* subsystem definition */
106 struct oonf_subsystem oonf_os_routing_subsystem = {
107   .init = _init,
108   .cleanup = _cleanup,
109 };
110
111 /* default wildcard route */
112 const struct os_route OS_ROUTE_WILDCARD = {
113   .family = AF_UNSPEC,
114   .src = { ._type = AF_UNSPEC },
115   .gw = { ._type = AF_UNSPEC },
116   .dst = { ._type = AF_UNSPEC },
117   .table = RT_TABLE_UNSPEC,
118   .metric = -1,
119   .protocol = RTPROT_UNSPEC,
120   .if_index = 0
121 };
122
123 /**
124  * Initialize routing subsystem
125  * @return -1 if an error happened, 0 otherwise
126  */
127 static int
128 _init(void) {
129   if (os_system_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) {
130     return -1;
131   }
132
133   if (_os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0')) {
134     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
135         "You should manually ensure that ICMP redirects are disabled!");
136   }
137
138   /* check kernel version and disable global rp_filter */
139   if (_is_at_least_linuxkernel_2_6_31()) {
140     if (_os_linux_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
141       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable global rp_filter "
142           "(necessary for kernel 2.6.31 and newer)! You should manually "
143           "ensure that rp_filter is disabled!");
144     }
145   }
146
147   list_init_head(&_rtnetlink_feedback);
148   return 0;
149 }
150
151 /**
152  * Cleanup all resources allocated by the routing subsystem
153  */
154 static void
155 _cleanup(void) {
156   struct os_route *rt, *rt_it;
157
158   list_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) {
159     _routing_finished(rt, 1);
160   }
161
162   if (_original_icmp_redirect != 0
163       && _os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0') != 0) {
164     OLSR_WARN(LOG_OS_SYSTEM,
165         "WARNING! Could not restore ICMP redirect flag %s to %c!",
166         PROC_ALL_REDIRECT, _original_icmp_redirect);
167   }
168
169   /* check kernel version and disable global rp_filter */
170   if (_os_linux_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
171     OLSR_WARN(LOG_OS_SYSTEM,
172         "WARNING! Could not restore global rp_filter flag %s to %c!",
173         PROC_ALL_SPOOF, _original_rp_filter);
174   }
175
176   if (_mesh_count > 0) {
177     if (_os_linux_writeToProc(PROC_IPFORWARD_V4, NULL, _original_ipv4_forward)) {
178       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore %s to %c!",
179           PROC_IPFORWARD_V4, _original_ipv4_forward);
180     }
181     if (_os_linux_writeToProc(PROC_IPFORWARD_V6, NULL, _original_ipv6_forward)) {
182       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore flag %s to %c",
183           PROC_IPFORWARD_V6, _original_ipv6_forward);
184     }
185   }
186
187   os_system_netlink_remove(&_rtnetlink_socket);
188 }
189
190 /**
191  * Initialize interface for mesh usage
192  * @param interf pointer to interface object
193  * @return -1 if an error happened, 0 otherwise
194  */
195 int
196 os_routing_init_mesh_if(struct olsr_interface *interf) {
197   char procfile[FILENAME_MAX];
198   char old_redirect = 0, old_spoof = 0;
199
200   if (!oonf_subsystem_is_initialized(&oonf_os_routing_subsystem)) {
201     /* make interface listener work without routing core */
202     return 0;
203   }
204
205   /* handle global ip_forward setting */
206   _mesh_count++;
207   if (_mesh_count == 1) {
208     if (_os_linux_writeToProc(PROC_IPFORWARD_V4, &_original_ipv4_forward, '1')) {
209       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not activate ip_forward for ipv4! "
210           "You should manually ensure that ip_forward for ipv4 is activated!");
211     }
212     if (_os_linux_writeToProc(PROC_IPFORWARD_V6, &_original_ipv6_forward, '1')) {
213       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not activate ip_forward for ipv6! "
214           "You should manually ensure that ip_forward for ipv6 is activated!");
215     }
216   }
217
218   /* Generate the procfile name */
219   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->data.name);
220
221   if (_os_linux_writeToProc(procfile, &old_redirect, '0')) {
222     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
223         "You should manually ensure that ICMP redirects are disabled!");
224   }
225
226   /* Generate the procfile name */
227   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->data.name);
228
229   if (_os_linux_writeToProc(procfile, &old_spoof, '0')) {
230     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable the IP spoof filter! "
231         "You should mannually ensure that IP spoof filtering is disabled!");
232   }
233
234   interf->_original_state = (old_redirect << 8) | (old_spoof);
235   return 0;
236 }
237
238 /**
239  * Cleanup interface after mesh usage
240  * @param interf pointer to interface object
241  */
242 void
243 os_routing_cleanup_mesh_if(struct olsr_interface *interf) {
244   char restore_redirect, restore_spoof;
245   char procfile[FILENAME_MAX];
246
247   if (!oonf_subsystem_is_initialized(&oonf_os_routing_subsystem)) {
248     /* make interface listener work without routing core */
249     return;
250   }
251
252   restore_redirect = (interf->_original_state >> 8) & 255;
253   restore_spoof = (interf->_original_state & 255);
254
255   /* Generate the procfile name */
256   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->data.name);
257
258   if (restore_redirect != 0
259       && _os_linux_writeToProc(procfile, NULL, restore_redirect) != 0) {
260     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore ICMP redirect flag %s to %c",
261         procfile, restore_redirect);
262   }
263
264   /* Generate the procfile name */
265   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->data.name);
266
267   if (restore_spoof != 0
268       && _os_linux_writeToProc(procfile, NULL, restore_spoof) != 0) {
269     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore IP spoof flag %s to %c",
270         procfile, restore_spoof);
271   }
272
273   /* handle global ip_forward setting */
274   _mesh_count--;
275   if (_mesh_count == 0) {
276     if (_os_linux_writeToProc(PROC_IPFORWARD_V4, NULL, _original_ipv4_forward)) {
277       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore %s to %c!",
278           PROC_IPFORWARD_V4, _original_ipv4_forward);
279     }
280     if (_os_linux_writeToProc(PROC_IPFORWARD_V6, NULL, _original_ipv6_forward)) {
281       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore flag %s to %c",
282           PROC_IPFORWARD_V6, _original_ipv6_forward);
283     }
284   }
285
286   interf->_original_state = 0;
287   return;
288 }
289
290 /**
291  * Update an entry of the kernel routing table. This call will only trigger
292  * the change, the real change will be done as soon as the netlink socket is
293  * writable.
294  * @param route data of route to be set/removed
295  * @param set true if route should be set, false if it should be removed
296  * @param del_similar true if similar routes that block this one should be
297  *   removed.
298  * @return -1 if an error happened, 0 otherwise
299  */
300 int
301 os_routing_set(struct os_route *route, bool set, bool del_similar) {
302   uint8_t buffer[UIO_MAXIOV];
303   struct nlmsghdr *msg;
304   unsigned char scope;
305   struct os_route os_rt;
306   int seq;
307
308   memset(buffer, 0, sizeof(buffer));
309
310   /* copy route settings */
311   memcpy(&os_rt, route, sizeof(os_rt));
312
313   /* get pointers for netlink message */
314   msg = (void *)&buffer[0];
315
316   msg->nlmsg_flags = NLM_F_REQUEST;
317
318   /* set length of netlink message with rtmsg payload */
319   msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
320
321   /* normally all routing operations are UNIVERSE scope */
322   scope = RT_SCOPE_UNIVERSE;
323
324   if (set) {
325     msg->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
326     msg->nlmsg_type = RTM_NEWROUTE;
327   } else {
328     msg->nlmsg_type = RTM_DELROUTE;
329
330     os_rt.protocol = 0;
331     netaddr_invalidate(&os_rt.src);
332
333     if (del_similar) {
334       /* no interface necessary */
335       os_rt.if_index = 0;
336
337       /* as wildcard for fuzzy deletion */
338       scope = RT_SCOPE_NOWHERE;
339     }
340   }
341
342   if (netaddr_get_address_family(&os_rt.gw) == AF_UNSPEC
343       && netaddr_get_prefix_length(&os_rt.dst) == netaddr_get_maxprefix(&os_rt.dst)) {
344     /* use destination as gateway, to 'force' linux kernel to do proper source address selection */
345     os_rt.gw = os_rt.dst;
346   }
347
348   if (_routing_set(msg, &os_rt, RTN_UNICAST, scope)) {
349     return -1;
350   }
351
352   /* cannot fail */
353   seq = os_system_netlink_send(&_rtnetlink_socket, msg);
354
355   if (route->cb_finished) {
356     list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
357     route->_internal.nl_seq = seq;
358   }
359   return 0;
360 }
361
362 /**
363  * Request all routing dataof a certain address family
364  * @param route pointer to routing filter
365  * @return -1 if an error happened, 0 otherwise
366  */
367 int
368 os_routing_query(struct os_route *route) {
369   uint8_t buffer[UIO_MAXIOV];
370   struct nlmsghdr *msg;
371   struct rtgenmsg *rt_gen;
372   int seq;
373
374   assert (route->cb_finished != NULL && route->cb_get != NULL);
375   memset(buffer, 0, sizeof(buffer));
376
377   /* get pointers for netlink message */
378   msg = (void *)&buffer[0];
379   rt_gen = NLMSG_DATA(msg);
380
381   msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
382
383   /* set length of netlink message with rtmsg payload */
384   msg->nlmsg_len = NLMSG_LENGTH(sizeof(*rt_gen));
385
386   msg->nlmsg_type = RTM_GETROUTE;
387   rt_gen->rtgen_family = route->family;
388
389   seq = os_system_netlink_send(&_rtnetlink_socket, msg);
390   if (seq < 0) {
391     return -1;
392   }
393
394   list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
395   route->_internal.nl_seq = seq;
396   return 0;
397 }
398
399 /**
400  * Stop processing of a routing command
401  * @param route pointer to os_route
402  */
403 void
404 os_routing_interrupt(struct os_route *route) {
405   _routing_finished(route, -1);
406 }
407
408 /**
409  * Stop processing of a routing command and set error code
410  * for callback
411  * @param route pointer to os_route
412  * @param error error code, 0 if no error
413  */
414 static void
415 _routing_finished(struct os_route *route, int error) {
416   if (list_is_node_added(&route->_internal._node)) {
417     /* remove first to prevent any kind of recursive cleanup */
418     list_remove(&route->_internal._node);
419
420     if (route->cb_finished) {
421       route->cb_finished(route, error);
422     }
423   }
424 }
425
426 /**
427  * Initiatize the an netlink routing message
428  * @param msg pointer to netlink message header
429  * @param route data to be added to the netlink message
430  * @param scope scope of route to be set/removed
431  * @return -1 if an error happened, 0 otherwise
432  */
433 static int
434 _routing_set(struct nlmsghdr *msg, struct os_route *route,
435     unsigned char rt_type, unsigned char rt_scope) {
436   struct rtmsg *rt_msg;
437
438   /* calculate address af_type */
439   if (netaddr_get_address_family(&route->dst) != AF_UNSPEC) {
440     route->family = netaddr_get_address_family(&route->dst);
441   }
442   if (netaddr_get_address_family(&route->gw) != AF_UNSPEC) {
443     if (route->family  != AF_UNSPEC
444         && route->family  != netaddr_get_address_family(&route->gw)) {
445       return -1;
446     }
447     route->family  = netaddr_get_address_family(&route->gw);
448   }
449   if (netaddr_get_address_family(&route->src) != AF_UNSPEC) {
450     if (route->family  != AF_UNSPEC && route->family  != netaddr_get_address_family(&route->src)) {
451       return -1;
452     }
453     route->family  = netaddr_get_address_family(&route->src);
454   }
455
456   if (route->family  == AF_UNSPEC) {
457     route->family  = AF_INET;
458   }
459
460   /* initialize rtmsg payload */
461   rt_msg = NLMSG_DATA(msg);
462
463   rt_msg->rtm_family = route->family ;
464   rt_msg->rtm_scope = rt_scope;
465   rt_msg->rtm_type = rt_type;
466   rt_msg->rtm_protocol = route->protocol;
467   rt_msg->rtm_table = route->table;
468
469   /* add attributes */
470   if (netaddr_get_address_family(&route->src) != AF_UNSPEC) {
471     rt_msg->rtm_src_len = netaddr_get_prefix_length(&route->src);
472
473     /* add src-ip */
474     if (os_system_netlink_addnetaddr(msg, RTA_PREFSRC, &route->src)) {
475       return -1;
476     }
477   }
478
479   if (netaddr_get_address_family(&route->gw) != AF_UNSPEC) {
480     rt_msg->rtm_flags |= RTNH_F_ONLINK;
481
482     /* add gateway */
483     if (os_system_netlink_addnetaddr(msg, RTA_GATEWAY, &route->gw)) {
484       return -1;
485     }
486   }
487
488   if (netaddr_get_address_family(&route->dst) != AF_UNSPEC) {
489     rt_msg->rtm_dst_len = netaddr_get_prefix_length(&route->dst);
490
491     /* add destination */
492     if (os_system_netlink_addnetaddr(msg, RTA_DST, &route->dst)) {
493       return -1;
494     }
495   }
496
497   if (route->metric != -1) {
498     /* add metric */
499     if (os_system_netlink_addreq(msg, RTA_PRIORITY, &route->metric, sizeof(route->metric))) {
500       return -1;
501     }
502   }
503
504   if (route->if_index) {
505     /* add interface*/
506     if (os_system_netlink_addreq(msg, RTA_OIF, &route->if_index, sizeof(route->if_index))) {
507       return -1;
508     }
509   }
510   return 0;
511 }
512
513 /**
514  * Parse a rtnetlink header into a os_route object
515  * @param route pointer to target os_route
516  * @param msg pointer to rtnetlink message header
517  * @return -1 if address family of rtnetlink is unknown,
518  *   0 otherwise
519  */
520 static int
521 _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) {
522   struct rtmsg *rt_msg;
523   struct rtattr *rt_attr;
524   int rt_len;
525
526   rt_msg = NLMSG_DATA(msg);
527   rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
528   rt_len = RTM_PAYLOAD(msg);
529
530   memcpy(route, &OS_ROUTE_WILDCARD, sizeof(*route));
531
532   route->protocol = rt_msg->rtm_protocol;
533   route->table = rt_msg->rtm_table;
534   route->family = rt_msg->rtm_family;
535
536   if (route->family != AF_INET && route->family != AF_INET6) {
537     return -1;
538   }
539
540   for(; RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) {
541     switch(rt_attr->rta_type) {
542       case RTA_SRC:
543         netaddr_from_binary_prefix(&route->src, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
544             rt_msg->rtm_family, rt_msg->rtm_src_len);
545         break;
546       case RTA_GATEWAY:
547         netaddr_from_binary(&route->gw, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
548         break;
549       case RTA_DST:
550         netaddr_from_binary_prefix(&route->dst, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
551             rt_msg->rtm_family, rt_msg->rtm_dst_len);
552         break;
553       case RTA_PRIORITY:
554         memcpy(&route->metric, RTA_DATA(rt_attr), sizeof(route->metric));
555         break;
556       case RTA_OIF:
557         memcpy(&route->if_index, RTA_DATA(rt_attr), sizeof(route->if_index));
558         break;
559       default:
560         break;
561     }
562   }
563
564   if (netaddr_get_address_family(&route->dst) == AF_UNSPEC) {
565     memcpy(&route->dst, route->family == AF_INET ? &NETADDR_IPV4_ANY : &NETADDR_IPV6_ANY,
566         sizeof(route->dst));
567     netaddr_set_prefix_length(&route->dst, rt_msg->rtm_dst_len);
568   }
569   return 0;
570 }
571
572 /**
573  * Checks if a os_route object matches a routing filter
574  * @param filter pointer to filter
575  * @param route pointer to route object
576  * @return true if route matches the filter, false otherwise
577  */
578 static bool
579 _match_routes(struct os_route *filter, struct os_route *route) {
580   if (filter->family != route->family) {
581     return false;
582   }
583   if (netaddr_get_address_family(&filter->src) != AF_UNSPEC
584       && memcmp(&filter->src, &route->src, sizeof(filter->src)) != 0) {
585     return false;
586   }
587   if (netaddr_get_address_family(&filter->gw) != AF_UNSPEC
588       && memcmp(&filter->gw, &route->gw, sizeof(filter->gw)) != 0) {
589     return false;
590   }
591   if (netaddr_get_address_family(&filter->dst) != AF_UNSPEC
592       && memcmp(&filter->dst, &route->dst, sizeof(filter->dst)) != 0) {
593     return false;
594   }
595   if (filter->metric != -1 && filter->metric != route->metric) {
596     return false;
597   }
598   if (filter->table != RT_TABLE_UNSPEC && filter->table != route->table) {
599     return false;
600   }
601   if (filter->protocol != RTPROT_UNSPEC && filter->protocol != route->protocol) {
602     return false;
603   }
604   return filter->if_index == 0 || filter->if_index == route->if_index;
605 }
606
607 /**
608  * Handle incoming rtnetlink messages
609  * @param msg
610  */
611 static void
612 _cb_rtnetlink_message(struct nlmsghdr *msg) {
613   struct os_route *filter;
614   struct os_route rt;
615
616   OLSR_DEBUG(LOG_OS_ROUTING, "Got message: %d %d", msg->nlmsg_seq, msg->nlmsg_type);
617
618   if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) {
619     return;
620   }
621
622   if (_routing_parse_nlmsg(&rt, msg)) {
623     OLSR_WARN(LOG_OS_ROUTING, "Error while processing route reply");
624     return;
625   }
626
627   list_for_each_element(&_rtnetlink_feedback, filter, _internal._node) {
628     OLSR_DEBUG_NH(LOG_OS_ROUTING, "  Compare with seq: %d", filter->_internal.nl_seq);
629     if (msg->nlmsg_seq == filter->_internal.nl_seq) {
630       if (filter->cb_get != NULL && _match_routes(filter, &rt)) {
631         filter->cb_get(filter, &rt);
632       }
633       break;
634     }
635   }
636 }
637
638 /**
639  * Handle feedback from netlink socket
640  * @param seq
641  * @param error
642  */
643 static void
644 _cb_rtnetlink_error(uint32_t seq, int error) {
645   struct os_route *route;
646
647   OLSR_DEBUG(LOG_OS_ROUTING, "Got feedback: %d %d", seq, error);
648
649   /* transform into errno number */
650   error = -error;
651
652   list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
653     if (seq == route->_internal.nl_seq) {
654       _routing_finished(route, error);
655       break;
656     }
657   }
658 }
659
660 /**
661  * Handle ack timeout from netlink socket
662  */
663 static void
664 _cb_rtnetlink_timeout(void) {
665   struct os_route *route, *rt_it;
666
667   OLSR_DEBUG(LOG_OS_ROUTING, "Got timeout");
668
669   list_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) {
670     _routing_finished(route, -1);
671   }
672 }
673
674 /**
675  * Handle done from multipart netlink messages
676  * @param seq
677  */
678 static void
679 _cb_rtnetlink_done(uint32_t seq) {
680   struct os_route *route;
681
682   OLSR_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq);
683
684   list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
685     if (seq == route->_internal.nl_seq) {
686       _routing_finished(route, 0);
687       break;
688     }
689   }
690 }
691
692 /**
693  * Overwrite a numeric entry in the procfile system and keep the old
694  * value.
695  * @param file pointer to filename (including full path)
696  * @param old pointer to memory to store old value
697  * @param value new value
698  * @return -1 if an error happened, 0 otherwise
699  */
700 static int
701 _os_linux_writeToProc(const char *file, char *old, char value) {
702   int fd;
703   char rv;
704
705   if ((fd = open(file, O_RDWR)) < 0) {
706     goto writetoproc_error;
707   }
708
709   if (read(fd, &rv, 1) != 1) {
710     goto writetoproc_error;
711   }
712
713   if (rv != value) {
714     if (lseek(fd, SEEK_SET, 0) == -1) {
715       goto writetoproc_error;
716     }
717
718     if (write(fd, &value, 1) != 1) {
719       goto writetoproc_error;
720     }
721
722     OLSR_DEBUG(LOG_OS_SYSTEM, "Writing '%c' (was %c) to %s", value, rv, file);
723   }
724
725   if (close(fd) != 0) {
726     goto writetoproc_error;
727   }
728
729   if (old && rv != value) {
730     *old = rv;
731   }
732
733   return 0;
734
735 writetoproc_error:
736   OLSR_WARN(LOG_OS_SYSTEM,
737     "Error, cannot read proc entry %s: %s (%d)\n",
738     file, strerror(errno), errno);
739   return -1;
740 }
741
742 /**
743  * @return true if linux kernel is at least 2.6.31
744  */
745 static bool
746 _is_at_least_linuxkernel_2_6_31(void) {
747   struct utsname uts;
748   char *next;
749   int first = 0, second = 0, third = 0;
750
751   memset(&uts, 0, sizeof(uts));
752   if (uname(&uts)) {
753     OLSR_WARN(LOG_OS_SYSTEM,
754         "Error, could not read kernel version: %s (%d)\n",
755         strerror(errno), errno);
756     return false;
757   }
758
759   first = strtol(uts.release, &next, 10);
760   /* check for linux 3.x */
761   if (first >= 3) {
762     return true;
763   }
764
765   if (*next != '.') {
766     goto kernel_parse_error;
767   }
768
769   second = strtol(next+1, &next, 10);
770   if (*next != '.') {
771     goto kernel_parse_error;
772   }
773
774   third = strtol(next+1, NULL, 10);
775
776   /* better or equal than linux 2.6.31 ? */
777   return first == 2 && second == 6 && third >= 31;
778
779 kernel_parse_error:
780   OLSR_WARN(LOG_OS_SYSTEM,
781       "Error, cannot parse kernel version: %s\n", uts.release);
782   return false;
783 }