detect interfaces that are going down (via rtnetlink)
[olsrd.git] / src / linux / kernel_routes.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org)
4  * Copyright (c) 2007, Sven-Ola for the policy routing stuff
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 #include "kernel_routes.h"
43 #include "ipc_frontend.h"
44 #include "log.h"
45
46 /* values for control flag to handle recursive route corrections 
47  *  currently only requires in linux specific kernel_routes.c */
48
49 #define RT_ORIG_REQUEST 0
50 #define RT_RETRY_AFTER_ADD_GATEWAY 1
51 #define RT_RETRY_AFTER_DELETE_SIMILAR 2
52 #define RT_DELETE_SIMILAR_ROUTE 3
53 #define RT_AUTO_ADD_GATEWAY_ROUTE 4
54 #define RT_DELETE_SIMILAR_AUTO_ROUTE 5
55
56 #if !LINUX_POLICY_ROUTING
57
58 static int delete_all_inet_gws(void);
59
60 #else /* !LINUX_POLICY_ROUTING */
61
62 #include <assert.h>
63 #include <linux/types.h>
64 #include <linux/rtnetlink.h>
65
66 extern struct rtnl_handle rth;
67
68 struct olsr_rtreq {
69   struct nlmsghdr n;
70   struct rtmsg r;
71   char buf[512];
72 };
73
74 #if LINUX_RTNETLINK_LISTEN
75 #include "ifnet.h"
76 #include "socket_parser.h"
77
78 int rtnetlink_register_socket(int rtnl_mgrp)
79 {
80   int sock = socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
81   struct sockaddr_nl addr;
82
83   if (sock<0) {
84     OLSR_PRINTF(1,"could not create rtnetlink socket! %d",sock);
85   }
86   else {
87     addr.nl_family = AF_NETLINK;
88     addr.nl_pid = 0; //kernel will assign appropiate number instead of pid (which is already used by primaray rtnetlink socket to add/delete routes)
89     addr.nl_groups = rtnl_mgrp;
90     if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0) {
91       OLSR_PRINTF(1,"could not bind socket! (%d %s)",errno,strerror(errno));
92     }
93     else {
94       add_olsr_socket(sock, &rtnetlink_read);
95     }
96     fcntl(sock, F_SETFL, O_NONBLOCK);
97   }
98   return sock;
99 }
100
101 static void netlink_process_link(struct nlmsghdr *h)
102 {
103   struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(h);
104   struct interface *iface;
105   
106   //all IFF flags: LOOPBACK,BROADCAST;POINTOPOINT;MULTICAST;NOARP;ALLMULTI;PROMISC;MASTER;SLAVE;DEBUG;DYNAMIC;AUTOMEDIA;PORTSEL;NOTRAILERS;UP;LOWER_UP;DORMANT
107
108   /* check if interface is up and running? (a not running interface keeps its routes, so better not react like on ifdown!!??) */
109   if (ifi->ifi_flags&IFF_UP) {
110     OLSR_PRINTF(3,"interface %s changed but is still up! ", iface->int_name);
111     return; //we are currently only interested in interfaces that are/go down
112   } else {
113     OLSR_PRINTF(1,"interface %s is down! ", iface->int_name);
114   }
115
116   iface = if_ifwithindex(ifi->ifi_index);
117
118   //only for still configured interfaces (ifup has to be detected with regular interface polling)
119   if ( iface != NULL ) {
120     struct olsr_if *tmp_if;
121     for (tmp_if = olsr_cnf->interfaces; tmp_if != NULL; tmp_if = tmp_if->next) {
122       if (tmp_if->interf==iface) {
123         OLSR_PRINTF(1,"-> removing %s from olsr config! ", iface->int_name);
124         RemoveInterface(tmp_if,true);
125         break;
126       }
127     }
128   }
129 }
130
131
132 void rtnetlink_read(int sock)
133 {
134   int len, plen;
135   struct iovec iov;
136   struct sockaddr_nl nladdr;
137   struct msghdr msg = {
138     &nladdr,
139     sizeof(nladdr),
140     &iov,
141     1,
142     NULL,
143     0,
144     0
145   };
146
147   char buffer[4096];
148   struct nlmsghdr *nlh = (struct nlmsghdr *)(ARM_NOWARN_ALIGN) buffer;
149   int ret;
150
151   iov.iov_base = (void *) buffer;
152   iov.iov_len = sizeof(buffer);
153
154   while (true) { //read until ret<0;
155     ret=recvmsg(sock, &msg, 0);
156     if (ret<0) {
157       if (errno != EAGAIN) OLSR_PRINTF(1,"\nnetlink listen error %u - %s",errno,strerror(errno));
158       return;
159     }
160     /*check message*/
161     len = nlh->nlmsg_len;
162     plen = len - sizeof(nlh);
163     if (len > ret || plen < 0) {
164       OLSR_PRINTF(1,"Malformed netlink message: "
165              "len=%d left=%d plen=%d",
166               len, ret, plen);
167       return;
168     }
169     if ( (nlh->nlmsg_type == RTM_NEWLINK) || ( nlh->nlmsg_type == RTM_DELLINK) ) netlink_process_link(nlh);
170   }
171 }
172
173 #endif /*linux_rtnetlink_listen*/
174
175 static void
176 olsr_netlink_addreq(struct olsr_rtreq *req, int type, const void *data, int len)
177 {
178   struct rtattr *rta = (struct rtattr *)(ARM_NOWARN_ALIGN)(((char *)req) + NLMSG_ALIGN(req->n.nlmsg_len));
179   req->n.nlmsg_len = NLMSG_ALIGN(req->n.nlmsg_len) + RTA_LENGTH(len);
180   assert(req->n.nlmsg_len < sizeof(struct olsr_rtreq));
181   rta->rta_type = type;
182   rta->rta_len = RTA_LENGTH(len);
183   memcpy(RTA_DATA(rta), data, len);
184 }
185
186 /* returns
187  * -1 on unrecoverable error (calling function will have to handle it)
188  *  0 on unexpected but recoverable rtnetlink behaviour
189  *    but some of the implemented recovery methods only cure symptoms, 
190  *    not the cause, like unintelligent ordering of inserted routes.
191  *  1 on success */
192 static int
193 olsr_netlink_route_int(const struct rt_entry *rt, uint8_t family, uint8_t rttable, __u16 cmd, uint8_t flag)
194 {
195   int ret = 1; /* helper variable for rtnetlink_message processing */
196   int rt_ret = -2;  /* if no response from rtnetlink it must be considered as failed! */
197   struct olsr_rtreq req;
198   struct iovec iov;
199   struct sockaddr_nl nladdr;
200   struct msghdr msg = {
201     &nladdr,
202     sizeof(nladdr),
203     &iov,
204     1,
205     NULL,
206     0,
207     0
208   };
209
210   uint32_t metric = 0;
211   const struct rt_nexthop *nexthop = NULL;
212   if ( ( cmd != RTM_NEWRULE ) && ( cmd != RTM_DELRULE ) ) {
213     if (FIBM_FLAT == olsr_cnf->fib_metric) {
214       metric = RT_METRIC_DEFAULT;
215     }
216     else {
217       metric = (RTM_NEWROUTE == cmd) ? rt->rt_best->rtp_metric.hops : rt->rt_metric.hops;
218     }
219
220     if (( RTM_NEWROUTE == cmd ) ||
221         (( RTM_DELROUTE == cmd ) && ( RT_DELETE_SIMILAR_ROUTE == flag || RT_DELETE_SIMILAR_AUTO_ROUTE == flag ))) {
222       nexthop = &rt->rt_best->rtp_nexthop;
223     }
224     else {
225       nexthop = &rt->rt_nexthop;
226     }
227   }
228
229   memset(&req, 0, sizeof(req));
230
231   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
232   req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
233   req.n.nlmsg_type = cmd;
234   req.r.rtm_family = family;
235   req.r.rtm_table = rttable;
236
237   /* RTN_UNSPEC would be the wildcard, but blackhole broadcast or nat roules should usually not conflict */
238   /* -> olsr only adds deletes unicast routes */
239   req.r.rtm_type = RTN_UNICAST;
240
241   /* wildcard to delete routes of all protos if no simlar-delete correct proto will get set below */
242   req.r.rtm_protocol = RTPROT_UNSPEC;
243
244   /* as wildcard for deletion */
245   req.r.rtm_scope = RT_SCOPE_NOWHERE;
246
247   if ( ( cmd != RTM_NEWRULE ) && ( cmd != RTM_DELRULE ) ) {
248     req.r.rtm_dst_len = rt->rt_dst.prefix_len;
249
250     /* do not specify much as we wanna delete similar/conflicting routes */
251     if ( ( flag != RT_DELETE_SIMILAR_ROUTE ) && ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE )) {
252       /* 0 gets replaced by OS-specifc default (3)
253        * 1 is reserved so we take 0 instead (this really makes some sense)
254        * other numbers are used 1:1 */
255       req.r.rtm_protocol = ( (olsr_cnf->rtproto<1) ? RTPROT_BOOT : ( (olsr_cnf->rtproto==1) ? 0 : olsr_cnf->rtproto) );
256       req.r.rtm_scope = RT_SCOPE_LINK;
257
258       /*add interface*/
259       olsr_netlink_addreq(&req, RTA_OIF, &nexthop->iif_index, sizeof(nexthop->iif_index));
260
261 #if SOURCE_IP_ROUTES
262       /* source ip here is based on now static olsr_cnf->main_addr in this olsr-0.5.6-r4, should be based on orignator-id in newer olsrds */
263       if (AF_INET == family) {
264         olsr_netlink_addreq(&req, RTA_PREFSRC, &olsr_cnf->main_addr.v4.s_addr, sizeof(olsr_cnf->main_addr.v4.s_addr));
265       }
266       else {
267         olsr_netlink_addreq(&req, RTA_PREFSRC, &olsr_cnf->main_addr.v6.s6_addr, sizeof(olsr_cnf->main_addr.v6.s6_addr));
268       }
269 #endif
270     }
271
272     /* metric is specified always as we can only delete one route per iteration, and wanna hit the correct one first */
273     if (FIBM_APPROX != olsr_cnf->fib_metric || (RTM_NEWROUTE == cmd) ) {
274       olsr_netlink_addreq(&req, RTA_PRIORITY, &metric, sizeof(metric));
275     }
276
277     /* make sure that netmask = /32 as this is an autogenarated route */
278     if (( flag == RT_AUTO_ADD_GATEWAY_ROUTE ) || (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) {
279       req.r.rtm_dst_len = 32;
280     }
281
282     /* for ipv4 or ipv6 we add gateway if one is specified, 
283     * or leave gateway away if we want to delete similar routes aswell, 
284     * or even use the gateway as target if we add a auto-generated route, 
285     * or if delete-similar to make insertion of auto-generated route possible */
286     if (AF_INET == family) {
287       if ( ( flag != RT_AUTO_ADD_GATEWAY_ROUTE ) && (flag != RT_DELETE_SIMILAR_ROUTE) && 
288            ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE) && (rt->rt_dst.prefix.v4.s_addr != nexthop->gateway.v4.s_addr) ) {
289         olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v4, sizeof(nexthop->gateway.v4));
290         req.r.rtm_scope = RT_SCOPE_UNIVERSE;
291       }
292       olsr_netlink_addreq(&req, RTA_DST, ( (flag == RT_AUTO_ADD_GATEWAY_ROUTE) || (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) ? 
293                           &nexthop->gateway.v4 : &rt->rt_dst.prefix.v4, sizeof(rt->rt_dst.prefix.v4));
294     } else {
295       if ( ( flag != RT_AUTO_ADD_GATEWAY_ROUTE ) && (flag != RT_DELETE_SIMILAR_ROUTE ) && ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE) 
296           && (0 != memcmp(&rt->rt_dst.prefix.v6, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6))) ) {
297         olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6));
298         req.r.rtm_scope = RT_SCOPE_UNIVERSE;
299       }
300       olsr_netlink_addreq(&req, RTA_DST, ( (flag == RT_AUTO_ADD_GATEWAY_ROUTE) || (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) ? 
301                           &nexthop->gateway.v6 : &rt->rt_dst.prefix.v6, sizeof(rt->rt_dst.prefix.v6));
302     }
303   } else {
304     /* add or delete a rule */
305     static uint32_t priority = 65535;
306
307     req.r.rtm_scope = RT_SCOPE_UNIVERSE;
308     olsr_netlink_addreq(&req, RTA_PRIORITY, &priority, sizeof(priority));
309   }
310
311   iov.iov_base = &req.n;
312   iov.iov_len = req.n.nlmsg_len;
313   memset(&nladdr, 0, sizeof(nladdr));
314   nladdr.nl_family = AF_NETLINK;
315   if (0 <= (ret = sendmsg(olsr_cnf->rtnl_s, &msg, 0))) {
316     iov.iov_base = req.buf;
317     iov.iov_len = sizeof(req.buf);
318     if (0 < (ret = recvmsg(olsr_cnf->rtnl_s, &msg, 0))) {
319       struct nlmsghdr *h = (struct nlmsghdr *)(ARM_NOWARN_ALIGN)req.buf;
320       while (NLMSG_OK(h, (unsigned int)ret)) {
321         if (NLMSG_DONE == h->nlmsg_type) {
322           /* seems to reached never */
323           olsr_syslog(OLSR_LOG_INFO, "_received NLMSG_DONE");
324           break;
325         }
326         if (NLMSG_ERROR == h->nlmsg_type) {
327           if (NLMSG_LENGTH(sizeof(struct nlmsgerr) <= h->nlmsg_len)) {
328             struct ipaddr_str ibuf;
329             struct ipaddr_str gbuf;
330             struct nlmsgerr *l_err = (struct nlmsgerr *)NLMSG_DATA(h);
331             errno = -l_err->error;
332             if (0 != errno) {
333               const char *const err_msg = strerror(errno);
334               struct ipaddr_str buf;
335               rt_ret = -1;
336
337               /* syslog debug output for various situations */
338               if ( cmd == RTM_NEWRULE ) {
339                 olsr_syslog(OLSR_LOG_ERR,"Error '%s' (%d) on inserting empty policy rule aimed to activate RtTable %u!", err_msg, errno, rttable);
340               }
341               else if ( cmd == RTM_DELRULE ) {
342                 olsr_syslog(OLSR_LOG_ERR,"Error '%s' (%d) on deleting empty policy rule aimed to activate rtTable %u!", err_msg, errno, rttable);
343               }
344               else if ( flag <= RT_RETRY_AFTER_DELETE_SIMILAR ) {
345                 if (rt->rt_dst.prefix.v4.s_addr!=nexthop->gateway.v4.s_addr) {
346                   olsr_syslog(OLSR_LOG_ERR, "error '%s' (%d) %s route to %s/%d via %s dev %s",
347                       err_msg, errno, (cmd == RTM_NEWROUTE) ? "add" : "del",
348                       olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), req.r.rtm_dst_len,
349                       olsr_ip_to_string(&gbuf,&nexthop->gateway), if_ifwithindex_name(nexthop->iif_index));
350                 }
351                 else {
352                   olsr_syslog(OLSR_LOG_ERR, "error '%s' (%d) %s route to %s/%d dev %s",
353                       err_msg, errno, (cmd == RTM_NEWROUTE) ? "add" : "del",
354                       olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), req.r.rtm_dst_len, if_ifwithindex_name(nexthop->iif_index));
355                 }
356               }
357               else if (flag == RT_AUTO_ADD_GATEWAY_ROUTE) {
358                 olsr_syslog(OLSR_LOG_ERR, ". error '%s' (%d) auto-add route to %s dev %s", err_msg, errno,
359                     olsr_ip_to_string(&ibuf,&nexthop->gateway), if_ifwithindex_name(nexthop->iif_index));
360               }
361               else if (flag == RT_DELETE_SIMILAR_ROUTE) {
362                 olsr_syslog(OLSR_LOG_ERR, ". error '%s' (%d) auto-delete route to %s dev %s", err_msg, errno,
363                     olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), if_ifwithindex_name(nexthop->iif_index));
364               }
365               else if (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) {
366                 olsr_syslog(OLSR_LOG_ERR, ". . error '%s' (%d) auto-delete similar route to %s dev %s", err_msg, errno,
367                     olsr_ip_to_string(&ibuf,&nexthop->gateway), if_ifwithindex_name(nexthop->iif_index));
368               }
369               else {
370                 /* should never happen */
371                 olsr_syslog(OLSR_LOG_ERR, "# invalid internal route delete/add flag (%d) used!", flag);
372               }
373             }
374             else {
375               /* netlink acks requests with an errno=0 NLMSG_ERROR response! */
376               rt_ret = 1;
377             }
378
379             /* resolve "File exist" (17) propblems (on orig and autogen routes)*/       
380             if ((errno == 17) && (cmd == RTM_NEWROUTE) && ((flag == RT_ORIG_REQUEST) || (flag == RT_AUTO_ADD_GATEWAY_ROUTE))) {
381               /* a similar route going over another gateway may be present, which has to be deleted! */
382               olsr_syslog(OLSR_LOG_ERR, ". auto-deleting similar routes to resolve 'File exists' (17) while adding route!");
383               rt_ret = RT_DELETE_SIMILAR_ROUTE; /* processing will contiune after this loop */
384             }
385             /* report success on "No such process" (3) */
386             else if ((errno == 3) && (cmd == RTM_DELROUTE) && (flag == RT_ORIG_REQUEST)) {
387               /* another similar (but slightly different) route may be present at this point
388               * , if so this will get solved when adding new route to this destination */
389               olsr_syslog(OLSR_LOG_ERR, ". ignoring 'No such process' (3) while deleting route!");
390               rt_ret = 0;
391             }
392             /* insert route to gateway on the fly if "Network unreachable" (128) on 2.4 kernels
393              * or on 2.6 kernel No such process (3) or Network unreachable (101) is reported in rtnetlink response
394              * do this only with flat metric, as using metric values inherited from 
395              * a target behind the gateway is really strange, and could lead to multiple routes!
396              * anyways if invalid gateway ips may happen we are f*cked up!!
397              * but if not, these on the fly generated routes are no problem, and will only get used when needed */
398             else if ( ( (errno == 3) || (errno == 101) || (errno == 128) )
399                 && (flag == RT_ORIG_REQUEST) && (FIBM_FLAT == olsr_cnf->fib_metric)
400                      && (cmd == RTM_NEWROUTE) && (rt->rt_dst.prefix.v4.s_addr!=nexthop->gateway.v4.s_addr)) {
401               if (errno == 128)  {
402                 olsr_syslog(OLSR_LOG_ERR, ". autogenerating route to handle 'Network unreachable' (128) while adding route!");
403               }
404               else if (errno == 101) {
405                 olsr_syslog(OLSR_LOG_ERR, ". autogenerating route to handle 'Network unreachable' (101) while adding route!");
406               }
407               else {
408                 olsr_syslog(OLSR_LOG_ERR, ". autogenerating route to handle 'No such process' (3) while adding route!");
409               }
410
411               /* processing will contiune after this loop */
412               rt_ret = RT_AUTO_ADD_GATEWAY_ROUTE;
413             }
414           }
415           /* report invalid message size */
416           else {
417             olsr_syslog(OLSR_LOG_INFO,"_received invalid netlink message size %lu != %u",
418                 (unsigned long int)sizeof(struct nlmsgerr), h->nlmsg_len);
419           }
420         }
421         /* log all other messages */
422         else {
423           olsr_syslog(OLSR_LOG_INFO,"_received %u Byte rtnetlink response of type %u with seqnr %u and flags %u from %u (%u)",
424               h->nlmsg_len, h->nlmsg_type, h->nlmsg_seq, h->nlmsg_flags, h->nlmsg_pid, NLMSG_ERROR);
425         }
426 /*
427  * The ARM compile complains about alignment. Copied
428  * from /usr/include/linux/netlink.h and adapted for ARM
429  */
430 #define MY_NLMSG_NEXT(nlh,len)   ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
431                                   (struct nlmsghdr*)(ARM_NOWARN_ALIGN)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
432         h = MY_NLMSG_NEXT(h, ret);
433       }
434     }
435   }
436   if ( rt_ret == RT_DELETE_SIMILAR_ROUTE ) {
437     /* delete all routes that may collide */
438
439     /* recursive call to delete simlar routes, using flag 2 to invoke deletion of similar, not only exact matches*/
440     rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_DELROUTE, 
441         flag == RT_AUTO_ADD_GATEWAY_ROUTE ? RT_DELETE_SIMILAR_AUTO_ROUTE : RT_DELETE_SIMILAR_ROUTE);
442
443     /* retry insert original route, if deleting similar succeeded, using flag=1 to prevent recursions */
444     if (rt_ret > 0) {
445       rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_RETRY_AFTER_DELETE_SIMILAR);
446     }
447     else {
448       olsr_syslog(OLSR_LOG_ERR, ". failed on auto-deleting similar route conflicting with above route!");
449     }
450
451     /* set appropriate return code for original request, while returning simple -1/1 if called recursive */
452     if (flag != RT_AUTO_ADD_GATEWAY_ROUTE) {
453       if (rt_ret > 0) {
454         /* successful recovery */
455         rt_ret = 0;
456       }
457       else {
458         /* unrecoverable error */
459         rt_ret = -1;
460       }
461     }
462   }
463   if ( rt_ret == RT_AUTO_ADD_GATEWAY_ROUTE ) {
464     /* autoadd route via gateway */
465
466     /* recursive call to invoke creation of a route to the gateway */
467     rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_AUTO_ADD_GATEWAY_ROUTE);
468
469     /* retry insert original route, if above succeeded without problems */
470     if (rt_ret > 0) {
471       rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_RETRY_AFTER_ADD_GATEWAY);
472     }
473     else {
474       olsr_syslog(OLSR_LOG_ERR, ". failed on inserting auto-generated route to gateway of above route!");
475     }
476
477     /* set appropriate return code for original request*/
478     if (rt_ret > 0) {
479       /* successful recovery */
480       rt_ret = 0;
481     }
482     else {
483       /* unrecoverable error */
484       rt_ret = -1;
485     }
486   }
487   /* send ipc update on success */
488   if ( ( cmd != RTM_NEWRULE ) && ( cmd != RTM_DELRULE )
489       && (flag = RT_ORIG_REQUEST) && (0 <= rt_ret && olsr_cnf->ipc_connections > 0) ) {
490     ipc_route_send_rtentry(&rt->rt_dst.prefix, &nexthop->gateway, metric,
491         RTM_NEWROUTE == cmd, if_ifwithindex_name(nexthop->iif_index));
492   }
493   if (rt_ret == -2) {
494     olsr_syslog(OLSR_LOG_ERR,"no rtnetlink response! (no system ressources left?, everything may happen now ...)");
495   }
496   return rt_ret;
497 }
498
499 /* external wrapper function for above patched multi purpose rtnetlink function */
500 int
501 olsr_netlink_rule(uint8_t family, uint8_t rttable, uint16_t cmd)
502 {
503   return olsr_netlink_route_int(NULL, family, rttable, cmd, RT_ORIG_REQUEST);
504 }
505
506 /* internal wrapper function for above patched function */
507 static int
508 olsr_netlink_route(const struct rt_entry *rt, uint8_t family, uint8_t rttable, __u16 cmd)
509 {
510   return olsr_netlink_route_int(rt, family, rttable, cmd, RT_ORIG_REQUEST);
511 }
512
513 #endif /* LINUX_POLICY_ROUTING */
514
515 /**
516  * Insert a route in the kernel routing table
517  *
518  * @param destination the route to add
519  *
520  * @return negative on error
521  */
522 int
523 olsr_ioctl_add_route(const struct rt_entry *rt)
524 {
525 #if !LINUX_POLICY_ROUTING
526   struct rtentry kernel_route;
527   union olsr_ip_addr mask;
528   int rslt;
529 #endif /* LINUX_POLICY_ROUTING */
530
531   OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
532
533 #if !LINUX_POLICY_ROUTING
534   memset(&kernel_route, 0, sizeof(struct rtentry));
535
536   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family = AF_INET;
537   ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family = AF_INET;
538   ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family = AF_INET;
539
540   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr = rt->rt_dst.prefix.v4;
541
542   if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
543     return -1;
544   }
545   ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr = mask.v4;
546
547   if (rt->rt_dst.prefix.v4.s_addr != rt->rt_best->rtp_nexthop.gateway.v4.s_addr) {
548     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr = rt->rt_best->rtp_nexthop.gateway.v4;
549   }
550
551   kernel_route.rt_flags = olsr_rt_flags(rt);
552   kernel_route.rt_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
553
554   /*
555    * Set interface
556    */
557   kernel_route.rt_dev = if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index);
558
559   /* delete existing default route before ? */
560   if ((olsr_cnf->del_gws) && (rt->rt_dst.prefix.v4.s_addr == INADDR_ANY) && (rt->rt_dst.prefix_len == INADDR_ANY)) {
561     delete_all_inet_gws();
562     olsr_cnf->del_gws = false;
563   }
564
565   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
566
567     /*
568      * Send IPC route update message
569      */
570     ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway, rt->rt_best->rtp_metric.hops, 1,
571                            if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
572   }
573
574   return rslt;
575 #else /* !LINUX_POLICY_ROUTING */
576   if (0 == olsr_cnf->rttable_default && 0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable) {
577     /*
578      * Users start whining about not having internet with policy
579      * routing activated and no static default route in table 254.
580      * We maintain a fallback defroute in the default=253 table.
581      */
582     olsr_netlink_route(rt, AF_INET, 253, RTM_NEWROUTE);
583   }
584   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0) {
585     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable_default, RTM_NEWROUTE);
586   }
587   else {
588     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_NEWROUTE);
589   }
590 #endif /* LINUX_POLICY_ROUTING */
591 }
592
593 /**
594  *Insert a route in the kernel routing table
595  *
596  *@param destination the route to add
597  *
598  *@return negative on error
599  */
600 int
601 olsr_ioctl_add_route6(const struct rt_entry *rt)
602 {
603 #if !LINUX_POLICY_ROUTING
604   struct in6_rtmsg kernel_route;
605   int rslt;
606
607   OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
608
609   memset(&kernel_route, 0, sizeof(struct in6_rtmsg));
610
611   kernel_route.rtmsg_dst = rt->rt_dst.prefix.v6;
612   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
613
614   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
615
616   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
617   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
618
619   /*
620    * set interface
621    */
622   kernel_route.rtmsg_ifindex = rt->rt_best->rtp_nexthop.iif_index;
623
624   /* XXX delete 0/0 route before ? */
625
626   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
627
628     /*
629      * Send IPC route update message
630      */
631     ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway, rt->rt_best->rtp_metric.hops, 1,
632                            if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
633   }
634   return rslt;
635 #else /* !LINUX_POLICY_ROUTING */
636   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0) {
637     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable_default, RTM_NEWROUTE);
638   }
639   else {
640     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_NEWROUTE);
641   }
642 #endif /* LINUX_POLICY_ROUTING */
643 }
644
645 /**
646  *Remove a route from the kernel
647  *
648  *@param destination the route to remove
649  *
650  *@return negative on error
651  */
652 int
653 olsr_ioctl_del_route(const struct rt_entry *rt)
654 {
655 #if !LINUX_POLICY_ROUTING
656   struct rtentry kernel_route;
657   union olsr_ip_addr mask;
658   int rslt;
659 #endif /* LINUX_POLICY_ROUTING */
660
661   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
662
663 #if !LINUX_POLICY_ROUTING
664   memset(&kernel_route, 0, sizeof(struct rtentry));
665
666   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family = AF_INET;
667   ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family = AF_INET;
668   ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family = AF_INET;
669
670   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr = rt->rt_dst.prefix.v4;
671
672   if (rt->rt_dst.prefix.v4.s_addr != rt->rt_nexthop.gateway.v4.s_addr) {
673     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr = rt->rt_nexthop.gateway.v4;
674   }
675
676   if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
677     return -1;
678   } else {
679     ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr = mask.v4;
680   }
681
682   kernel_route.rt_flags = olsr_rt_flags(rt);
683   kernel_route.rt_metric = olsr_fib_metric(&rt->rt_metric.hops);
684
685   /*
686    * Set interface
687    */
688   kernel_route.rt_dev = NULL;
689
690   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route)) >= 0) {
691
692     /*
693      * Send IPC route update message
694      */
695     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
696   }
697
698   return rslt;
699 #else /* !LINUX_POLICY_ROUTING */
700   if (0 == olsr_cnf->rttable_default && 0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable) {
701     /*
702      * Also remove the fallback default route
703      */
704     olsr_netlink_route(rt, AF_INET, 253, RTM_DELROUTE);
705   }
706   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0) {
707     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable_default, RTM_DELROUTE);
708   }
709   else {
710     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_DELROUTE);
711   }
712 #endif /* LINUX_POLICY_ROUTING */
713 }
714
715 /**
716  *Remove a route from the kernel
717  *
718  *@param destination the route to remove
719  *
720  *@return negative on error
721  */
722 int
723 olsr_ioctl_del_route6(const struct rt_entry *rt)
724 {
725 #if !LINUX_POLICY_ROUTING
726   struct in6_rtmsg kernel_route;
727   int rslt;
728 #endif /* LINUX_POLICY_ROUTING */
729
730   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
731
732 #if !LINUX_POLICY_ROUTING
733   memset(&kernel_route, 0, sizeof(struct in6_rtmsg));
734
735   kernel_route.rtmsg_dst = rt->rt_dst.prefix.v6;
736   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
737
738   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
739
740   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
741   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
742
743   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route) >= 0)) {
744
745     /*
746      * Send IPC route update message
747      */
748     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
749   }
750
751   return rslt;
752 #else /* !LINUX_POLICY_ROUTING */
753   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0) {
754     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable_default, RTM_DELROUTE);
755   }
756   else {
757     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_DELROUTE);
758   }
759 #endif /* LINUX_POLICY_ROUTING */
760 }
761
762 #if !LINUX_POLICY_ROUTING
763 static int
764 delete_all_inet_gws(void)
765 {
766   int s;
767   char buf[BUFSIZ], *cp, *cplim;
768   struct ifconf ifc;
769   struct ifreq *ifr;
770
771   OLSR_PRINTF(1, "Internet gateway detected...\nTrying to delete default gateways\n");
772
773   /* Get a socket */
774   if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
775     olsr_syslog(OLSR_LOG_ERR, "socket: %m");
776     return -1;
777   }
778
779   ifc.ifc_len = sizeof(buf);
780   ifc.ifc_buf = buf;
781   if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
782     olsr_syslog(OLSR_LOG_ERR, "ioctl (get interface configuration)");
783     close(s);
784     return -1;
785   }
786
787   ifr = ifc.ifc_req;
788   cplim = buf + ifc.ifc_len;    /*skip over if's with big ifr_addr's */
789   for (cp = buf; cp < cplim; cp += sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr)) {
790     struct rtentry kernel_route;
791     ifr = (struct ifreq *)cp;
792
793     if (strcmp(ifr->ifr_ifrn.ifrn_name, "lo") == 0) {
794       OLSR_PRINTF(1, "Skipping loopback...\n");
795       continue;
796     }
797
798     OLSR_PRINTF(1, "Trying 0.0.0.0/0 %s...", ifr->ifr_ifrn.ifrn_name);
799
800     memset(&kernel_route, 0, sizeof(struct rtentry));
801
802     ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr = 0;
803     ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family = AF_INET;
804     ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = 0;
805     ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family = AF_INET;
806
807     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr = INADDR_ANY;
808     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family = AF_INET;
809
810     kernel_route.rt_flags = RTF_UP | RTF_GATEWAY;
811
812     kernel_route.rt_dev = ifr->ifr_ifrn.ifrn_name;
813
814     if ((ioctl(s, SIOCDELRT, &kernel_route)) < 0)
815       OLSR_PRINTF(1, "NO\n");
816     else
817       OLSR_PRINTF(1, "YES\n");
818   }
819   close(s);
820   return 0;
821 }
822 #endif /* LINUX_POLICY_ROUTING */
823
824 /*
825  * Local Variables:
826  * c-basic-offset: 2
827  * indent-tabs-mode: nil
828  * End:
829  */