src/linux/kernel_routes.c
author Henning Rogge <hrogge@googlemail.com>
Fri Oct 16 17:18:44 2009 +0200 (2 years ago)
branchOLSRD_0_5_6_BRANCH
changeset 1881 438bd63a1ab8
parent 1874edfeedcc1c40
permissions -rw-r--r--
Fix segfault in "lost interface" monitor if interface goes down which is no OLSR interface
     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   struct olsr_if *tmp_if;
   106 
   107   iface = if_ifwithindex(ifi->ifi_index);
   108   if (iface == NULL) {
   109     return;
   110   }
   111   
   112   //all IFF flags: LOOPBACK,BROADCAST;POINTOPOINT;MULTICAST;NOARP;ALLMULTI;PROMISC;MASTER;SLAVE;DEBUG;DYNAMIC;AUTOMEDIA;PORTSEL;NOTRAILERS;UP;LOWER_UP;DORMANT
   113   /* check if interface is up and running? (a not running interface keeps its routes, so better not react like on ifdown!!??) */
   114   if (ifi->ifi_flags&IFF_UP) {
   115     OLSR_PRINTF(3,"interface %s changed but is still up! ", iface->int_name);
   116     return; //we are currently only interested in interfaces that are/go down
   117   } else {
   118     OLSR_PRINTF(1,"interface %s is down! ", iface->int_name);
   119   }
   120 
   121   //only for still configured interfaces (ifup has to be detected with regular interface polling)
   122   for (tmp_if = olsr_cnf->interfaces; tmp_if != NULL; tmp_if = tmp_if->next) {
   123     if (tmp_if->interf==iface) {
   124       OLSR_PRINTF(1,"-> removing %s from olsr config! ", iface->int_name);
   125       RemoveInterface(tmp_if,true);
   126       break;
   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  */