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