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