Move some setup functions from os_routing to os_net
[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
49 #include "common/common_types.h"
50 #include "core/olsr_subsystem.h"
51 #include "core/os_routing.h"
52 #include "core/os_system.h"
53
54 /* prototypes */
55 static int _init(void);
56 static void _cleanup(void);
57
58 static int _routing_set(struct nlmsghdr *msg, struct os_route *route,
59     unsigned char rt_type, unsigned char rt_scope);
60
61 static void _routing_finished(struct os_route *route, int error);
62 static void _cb_rtnetlink_message(struct nlmsghdr *);
63 static void _cb_rtnetlink_error(uint32_t seq, int error);
64 static void _cb_rtnetlink_done(uint32_t seq);
65 static void _cb_rtnetlink_timeout(void);
66
67 /* netlink socket for route set/get commands */
68 struct os_system_netlink _rtnetlink_socket = {
69   .cb_message = _cb_rtnetlink_message,
70   .cb_error = _cb_rtnetlink_error,
71   .cb_done = _cb_rtnetlink_done,
72   .cb_timeout = _cb_rtnetlink_timeout,
73 };
74 struct list_entity _rtnetlink_feedback;
75
76 /* subsystem definition */
77 struct oonf_subsystem oonf_os_routing_subsystem = {
78   .init = _init,
79   .cleanup = _cleanup,
80 };
81
82 /* default wildcard route */
83 const struct os_route OS_ROUTE_WILDCARD = {
84   .family = AF_UNSPEC,
85   .src = { ._type = AF_UNSPEC },
86   .gw = { ._type = AF_UNSPEC },
87   .dst = { ._type = AF_UNSPEC },
88   .table = RT_TABLE_UNSPEC,
89   .metric = -1,
90   .protocol = RTPROT_UNSPEC,
91   .if_index = 0
92 };
93
94 /**
95  * Initialize routing subsystem
96  * @return -1 if an error happened, 0 otherwise
97  */
98 static int
99 _init(void) {
100   if (os_system_netlink_add(&_rtnetlink_socket, NETLINK_ROUTE)) {
101     return -1;
102   }
103   list_init_head(&_rtnetlink_feedback);
104   return 0;
105 }
106
107 /**
108  * Cleanup all resources allocated by the routing subsystem
109  */
110 static void
111 _cleanup(void) {
112   struct os_route *rt, *rt_it;
113
114   list_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) {
115     _routing_finished(rt, 1);
116   }
117
118   os_system_netlink_remove(&_rtnetlink_socket);
119 }
120
121 /**
122  * Update an entry of the kernel routing table. This call will only trigger
123  * the change, the real change will be done as soon as the netlink socket is
124  * writable.
125  * @param route data of route to be set/removed
126  * @param set true if route should be set, false if it should be removed
127  * @param del_similar true if similar routes that block this one should be
128  *   removed.
129  * @return -1 if an error happened, 0 otherwise
130  */
131 int
132 os_routing_set(struct os_route *route, bool set, bool del_similar) {
133   uint8_t buffer[UIO_MAXIOV];
134   struct nlmsghdr *msg;
135   unsigned char scope;
136   struct os_route os_rt;
137   int seq;
138
139   memset(buffer, 0, sizeof(buffer));
140
141   /* copy route settings */
142   memcpy(&os_rt, route, sizeof(os_rt));
143
144   /* get pointers for netlink message */
145   msg = (void *)&buffer[0];
146
147   msg->nlmsg_flags = NLM_F_REQUEST;
148
149   /* set length of netlink message with rtmsg payload */
150   msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
151
152   /* normally all routing operations are UNIVERSE scope */
153   scope = RT_SCOPE_UNIVERSE;
154
155   if (set) {
156     msg->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
157     msg->nlmsg_type = RTM_NEWROUTE;
158   } else {
159     msg->nlmsg_type = RTM_DELROUTE;
160
161     os_rt.protocol = 0;
162     netaddr_invalidate(&os_rt.src);
163
164     if (del_similar) {
165       /* no interface necessary */
166       os_rt.if_index = 0;
167
168       /* as wildcard for fuzzy deletion */
169       scope = RT_SCOPE_NOWHERE;
170     }
171   }
172
173   if (netaddr_get_address_family(&os_rt.gw) == AF_UNSPEC
174       && netaddr_get_prefix_length(&os_rt.dst) == netaddr_get_maxprefix(&os_rt.dst)) {
175     /* use destination as gateway, to 'force' linux kernel to do proper source address selection */
176     os_rt.gw = os_rt.dst;
177   }
178
179   if (_routing_set(msg, &os_rt, RTN_UNICAST, scope)) {
180     return -1;
181   }
182
183   /* cannot fail */
184   seq = os_system_netlink_send(&_rtnetlink_socket, msg);
185
186   if (route->cb_finished) {
187     list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
188     route->_internal.nl_seq = seq;
189   }
190   return 0;
191 }
192
193 /**
194  * Request all routing dataof a certain address family
195  * @param route pointer to routing filter
196  * @return -1 if an error happened, 0 otherwise
197  */
198 int
199 os_routing_query(struct os_route *route) {
200   uint8_t buffer[UIO_MAXIOV];
201   struct nlmsghdr *msg;
202   struct rtgenmsg *rt_gen;
203   int seq;
204
205   assert (route->cb_finished != NULL && route->cb_get != NULL);
206   memset(buffer, 0, sizeof(buffer));
207
208   /* get pointers for netlink message */
209   msg = (void *)&buffer[0];
210   rt_gen = NLMSG_DATA(msg);
211
212   msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
213
214   /* set length of netlink message with rtmsg payload */
215   msg->nlmsg_len = NLMSG_LENGTH(sizeof(*rt_gen));
216
217   msg->nlmsg_type = RTM_GETROUTE;
218   rt_gen->rtgen_family = route->family;
219
220   seq = os_system_netlink_send(&_rtnetlink_socket, msg);
221   if (seq < 0) {
222     return -1;
223   }
224
225   list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
226   route->_internal.nl_seq = seq;
227   return 0;
228 }
229
230 /**
231  * Stop processing of a routing command
232  * @param route pointer to os_route
233  */
234 void
235 os_routing_interrupt(struct os_route *route) {
236   _routing_finished(route, -1);
237 }
238
239 /**
240  * Stop processing of a routing command and set error code
241  * for callback
242  * @param route pointer to os_route
243  * @param error error code, 0 if no error
244  */
245 static void
246 _routing_finished(struct os_route *route, int error) {
247   if (list_is_node_added(&route->_internal._node)) {
248     /* remove first to prevent any kind of recursive cleanup */
249     list_remove(&route->_internal._node);
250
251     if (route->cb_finished) {
252       route->cb_finished(route, error);
253     }
254   }
255 }
256
257 /**
258  * Initiatize the an netlink routing message
259  * @param msg pointer to netlink message header
260  * @param route data to be added to the netlink message
261  * @param scope scope of route to be set/removed
262  * @return -1 if an error happened, 0 otherwise
263  */
264 static int
265 _routing_set(struct nlmsghdr *msg, struct os_route *route,
266     unsigned char rt_type, unsigned char rt_scope) {
267   struct rtmsg *rt_msg;
268
269   /* calculate address af_type */
270   if (netaddr_get_address_family(&route->dst) != AF_UNSPEC) {
271     route->family = netaddr_get_address_family(&route->dst);
272   }
273   if (netaddr_get_address_family(&route->gw) != AF_UNSPEC) {
274     if (route->family  != AF_UNSPEC
275         && route->family  != netaddr_get_address_family(&route->gw)) {
276       return -1;
277     }
278     route->family  = netaddr_get_address_family(&route->gw);
279   }
280   if (netaddr_get_address_family(&route->src) != AF_UNSPEC) {
281     if (route->family  != AF_UNSPEC && route->family  != netaddr_get_address_family(&route->src)) {
282       return -1;
283     }
284     route->family  = netaddr_get_address_family(&route->src);
285   }
286
287   if (route->family  == AF_UNSPEC) {
288     route->family  = AF_INET;
289   }
290
291   /* initialize rtmsg payload */
292   rt_msg = NLMSG_DATA(msg);
293
294   rt_msg->rtm_family = route->family ;
295   rt_msg->rtm_scope = rt_scope;
296   rt_msg->rtm_type = rt_type;
297   rt_msg->rtm_protocol = route->protocol;
298   rt_msg->rtm_table = route->table;
299
300   /* add attributes */
301   if (netaddr_get_address_family(&route->src) != AF_UNSPEC) {
302     rt_msg->rtm_src_len = netaddr_get_prefix_length(&route->src);
303
304     /* add src-ip */
305     if (os_system_netlink_addnetaddr(msg, RTA_PREFSRC, &route->src)) {
306       return -1;
307     }
308   }
309
310   if (netaddr_get_address_family(&route->gw) != AF_UNSPEC) {
311     rt_msg->rtm_flags |= RTNH_F_ONLINK;
312
313     /* add gateway */
314     if (os_system_netlink_addnetaddr(msg, RTA_GATEWAY, &route->gw)) {
315       return -1;
316     }
317   }
318
319   if (netaddr_get_address_family(&route->dst) != AF_UNSPEC) {
320     rt_msg->rtm_dst_len = netaddr_get_prefix_length(&route->dst);
321
322     /* add destination */
323     if (os_system_netlink_addnetaddr(msg, RTA_DST, &route->dst)) {
324       return -1;
325     }
326   }
327
328   if (route->metric != -1) {
329     /* add metric */
330     if (os_system_netlink_addreq(msg, RTA_PRIORITY, &route->metric, sizeof(route->metric))) {
331       return -1;
332     }
333   }
334
335   if (route->if_index) {
336     /* add interface*/
337     if (os_system_netlink_addreq(msg, RTA_OIF, &route->if_index, sizeof(route->if_index))) {
338       return -1;
339     }
340   }
341   return 0;
342 }
343
344 /**
345  * Parse a rtnetlink header into a os_route object
346  * @param route pointer to target os_route
347  * @param msg pointer to rtnetlink message header
348  * @return -1 if address family of rtnetlink is unknown,
349  *   0 otherwise
350  */
351 static int
352 _routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) {
353   struct rtmsg *rt_msg;
354   struct rtattr *rt_attr;
355   int rt_len;
356
357   rt_msg = NLMSG_DATA(msg);
358   rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
359   rt_len = RTM_PAYLOAD(msg);
360
361   memcpy(route, &OS_ROUTE_WILDCARD, sizeof(*route));
362
363   route->protocol = rt_msg->rtm_protocol;
364   route->table = rt_msg->rtm_table;
365   route->family = rt_msg->rtm_family;
366
367   if (route->family != AF_INET && route->family != AF_INET6) {
368     return -1;
369   }
370
371   for(; RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) {
372     switch(rt_attr->rta_type) {
373       case RTA_SRC:
374         netaddr_from_binary_prefix(&route->src, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
375             rt_msg->rtm_family, rt_msg->rtm_src_len);
376         break;
377       case RTA_GATEWAY:
378         netaddr_from_binary(&route->gw, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
379         break;
380       case RTA_DST:
381         netaddr_from_binary_prefix(&route->dst, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr),
382             rt_msg->rtm_family, rt_msg->rtm_dst_len);
383         break;
384       case RTA_PRIORITY:
385         memcpy(&route->metric, RTA_DATA(rt_attr), sizeof(route->metric));
386         break;
387       case RTA_OIF:
388         memcpy(&route->if_index, RTA_DATA(rt_attr), sizeof(route->if_index));
389         break;
390       default:
391         break;
392     }
393   }
394
395   if (netaddr_get_address_family(&route->dst) == AF_UNSPEC) {
396     memcpy(&route->dst, route->family == AF_INET ? &NETADDR_IPV4_ANY : &NETADDR_IPV6_ANY,
397         sizeof(route->dst));
398     netaddr_set_prefix_length(&route->dst, rt_msg->rtm_dst_len);
399   }
400   return 0;
401 }
402
403 /**
404  * Checks if a os_route object matches a routing filter
405  * @param filter pointer to filter
406  * @param route pointer to route object
407  * @return true if route matches the filter, false otherwise
408  */
409 static bool
410 _match_routes(struct os_route *filter, struct os_route *route) {
411   if (filter->family != route->family) {
412     return false;
413   }
414   if (netaddr_get_address_family(&filter->src) != AF_UNSPEC
415       && memcmp(&filter->src, &route->src, sizeof(filter->src)) != 0) {
416     return false;
417   }
418   if (netaddr_get_address_family(&filter->gw) != AF_UNSPEC
419       && memcmp(&filter->gw, &route->gw, sizeof(filter->gw)) != 0) {
420     return false;
421   }
422   if (netaddr_get_address_family(&filter->dst) != AF_UNSPEC
423       && memcmp(&filter->dst, &route->dst, sizeof(filter->dst)) != 0) {
424     return false;
425   }
426   if (filter->metric != -1 && filter->metric != route->metric) {
427     return false;
428   }
429   if (filter->table != RT_TABLE_UNSPEC && filter->table != route->table) {
430     return false;
431   }
432   if (filter->protocol != RTPROT_UNSPEC && filter->protocol != route->protocol) {
433     return false;
434   }
435   return filter->if_index == 0 || filter->if_index == route->if_index;
436 }
437
438 /**
439  * Handle incoming rtnetlink messages
440  * @param msg
441  */
442 static void
443 _cb_rtnetlink_message(struct nlmsghdr *msg) {
444   struct os_route *filter;
445   struct os_route rt;
446
447   OLSR_DEBUG(LOG_OS_ROUTING, "Got message: %d %d", msg->nlmsg_seq, msg->nlmsg_type);
448
449   if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) {
450     return;
451   }
452
453   if (_routing_parse_nlmsg(&rt, msg)) {
454     OLSR_WARN(LOG_OS_ROUTING, "Error while processing route reply");
455     return;
456   }
457
458   list_for_each_element(&_rtnetlink_feedback, filter, _internal._node) {
459     OLSR_DEBUG_NH(LOG_OS_ROUTING, "  Compare with seq: %d", filter->_internal.nl_seq);
460     if (msg->nlmsg_seq == filter->_internal.nl_seq) {
461       if (filter->cb_get != NULL && _match_routes(filter, &rt)) {
462         filter->cb_get(filter, &rt);
463       }
464       break;
465     }
466   }
467 }
468
469 /**
470  * Handle feedback from netlink socket
471  * @param seq
472  * @param error
473  */
474 static void
475 _cb_rtnetlink_error(uint32_t seq, int error) {
476   struct os_route *route;
477
478   OLSR_DEBUG(LOG_OS_ROUTING, "Got feedback: %d %d", seq, error);
479
480   /* transform into errno number */
481   error = -error;
482
483   list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
484     if (seq == route->_internal.nl_seq) {
485       _routing_finished(route, error);
486       break;
487     }
488   }
489 }
490
491 /**
492  * Handle ack timeout from netlink socket
493  */
494 static void
495 _cb_rtnetlink_timeout(void) {
496   struct os_route *route, *rt_it;
497
498   OLSR_DEBUG(LOG_OS_ROUTING, "Got timeout");
499
500   list_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) {
501     _routing_finished(route, -1);
502   }
503 }
504
505 /**
506  * Handle done from multipart netlink messages
507  * @param seq
508  */
509 static void
510 _cb_rtnetlink_done(uint32_t seq) {
511   struct os_route *route;
512
513   OLSR_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq);
514
515   list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
516     if (seq == route->_internal.nl_seq) {
517       _routing_finished(route, 0);
518       break;
519     }
520   }
521 }