Port of RTNetlink code from 0.5.6 by Markus Kittenberger
authorHenning Rogge <hrogge@googlemail.com>
Mon, 13 Apr 2009 09:00:03 +0000 (11:00 +0200)
committerHenning Rogge <hrogge@googlemail.com>
Mon, 13 Apr 2009 09:00:03 +0000 (11:00 +0200)
src/linux/kernel_routes.c

index 6cd4864..246f74f 100644 (file)
 
 #include "olsr_logging.h"
 
+static int olsr_netlink_route_int(const struct rt_entry *, uint8_t, uint8_t, __u16, uint8_t);
+
+/* values for control flag to handle recursive route corrections
+ *  currently only requires in linux specific kernel_routes.c */
+
+#define RT_NONE 0
+#define RT_ORIG_REQUEST 1
+#define RT_RETRY_AFTER_ADD_GATEWAY 2
+#define RT_RETRY_AFTER_DELETE_SIMILAR 3
+#define RT_DELETE_SIMILAR_ROUTE 4
+#define RT_AUTO_ADD_GATEWAY_ROUTE 5
+#define RT_DELETE_SIMILAR_AUTO_ROUTE 6
+
 struct olsr_rtreq {
   struct nlmsghdr n;
   struct rtmsg    r;
@@ -60,17 +73,19 @@ struct olsr_ipadd_req {
 };
 
 
-static void olsr_netlink_addreq(struct nlmsghdr *n, size_t reqSize __attribute__((unused)), int type, const void *data, int len)
+static void olsr_netlink_addreq(struct nlmsghdr *n, size_t reqSize __attribute__ ((unused)), int type, const void *data, int len)
 {
   struct rtattr *rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
   n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_LENGTH(len);
-  assert(n->nlmsg_len < reqSize);
+  //produces strange compile error
+  //assert(n->nlmsg_len < reqSize);
   rta->rta_type = type;
   rta->rta_len = RTA_LENGTH(len);
   memcpy(RTA_DATA(rta), data, len);
 }
 
-static int olsr_netlink_send(struct nlmsghdr *n, char *buf, size_t bufSize) {
+/*rt_entry and nexthop  and family an d tble must only be specified with an flag != RT_NONE*/
+static int olsr_netlink_send(struct nlmsghdr *n, char *buf, size_t bufSize, uint8_t flag, const struct rt_entry *rt, const struct rt_nexthop *nexthop, uint8_t family, uint8_t rttable) {
   struct iovec iov;
   struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
   struct msghdr msg = {
@@ -82,7 +97,8 @@ static int olsr_netlink_send(struct nlmsghdr *n, char *buf, size_t bufSize) {
     .msg_controllen = 0,
     .msg_flags = 0
   };
-  int ret;
+  int ret = 1; /* helper variable for rtnetlink_message processing */
+  int rt_ret = -2;  /* if no response from rtnetlink it must be considered as failed! */
 
   iov.iov_base = n;
   iov.iov_len = n->nlmsg_len;
@@ -95,14 +111,85 @@ static int olsr_netlink_send(struct nlmsghdr *n, char *buf, size_t bufSize) {
       struct nlmsghdr* h = (struct nlmsghdr*)buf;
       while (NLMSG_OK(h, (unsigned int)ret)) {
         if (NLMSG_DONE == h->nlmsg_type) {
+          //seems to be never reached
+          //log this if it ever happens !!??
           break;
         }
         if (NLMSG_ERROR == h->nlmsg_type) {
           if (NLMSG_LENGTH(sizeof(struct nlmsgerr) <= h->nlmsg_len)) {
+#ifndef REMOVE_LOG_DEBUG
+            struct ipaddr_str ibuf;
+            struct ipaddr_str gbuf;
+#endif
             const struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
             errno = -l_err->error;
             if (0 != errno) {
+#ifndef REMOVE_LOG_DEBUG
+              const char *const err_msg = strerror(errno);
+#endif
               ret = -1;
+              rt_ret = -1;
+              if (flag != RT_NONE){
+                /* debug output for various situations */
+                if ( n->nlmsg_type == RTM_NEWRULE ) {
+                  OLSR_DEBUG(LOG_ROUTING,"Error '%s' (%d) on inserting empty policy rule aimed to activate RtTable %u!", err_msg, errno, rttable);
+                }
+                else if ( n->nlmsg_type == RTM_DELRULE ) {
+                  OLSR_DEBUG(LOG_ROUTING,"Error '%s' (%d) on deleting empty policy rule aimed to activate rtTable %u!", err_msg, errno, rttable);
+                }
+                else if ( flag <= RT_RETRY_AFTER_DELETE_SIMILAR ) {
+                  if (rt->rt_dst.prefix.v4.s_addr!=nexthop->gateway.v4.s_addr)
+                    OLSR_DEBUG(LOG_ROUTING, "error '%s' (%d) %s route to %s/%d via %s dev %s", err_msg, errno, (n->nlmsg_type == RTM_NEWROUTE) ? "add" : "del",
+                                olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), rt->rt_dst.prefix_len,
+                                olsr_ip_to_string(&gbuf,&nexthop->gateway), nexthop->interface->int_name);
+                  else OLSR_DEBUG(LOG_ROUTING, "error '%s' (%d) %s route to %s/%d dev %s", err_msg, errno, (n->nlmsg_type == RTM_NEWROUTE) ? "add" : "del",
+                                   olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), rt->rt_dst.prefix_len, nexthop->interface->int_name);
+                }
+                else if (flag == RT_AUTO_ADD_GATEWAY_ROUTE)
+                         OLSR_DEBUG(LOG_ROUTING, ". error '%s' (%d) auto-add route to %s dev %s", err_msg, errno,
+                                     olsr_ip_to_string(&ibuf,&nexthop->gateway), nexthop->interface->int_name);
+                else if (flag == RT_DELETE_SIMILAR_ROUTE)
+                         OLSR_DEBUG(LOG_ROUTING, ". error '%s' (%d) auto-delete route to %s dev %s", err_msg, errno,
+                                     olsr_ip_to_string(&ibuf,&rt->rt_dst.prefix), nexthop->interface->int_name);
+                else if (flag == RT_DELETE_SIMILAR_AUTO_ROUTE)
+                         OLSR_DEBUG(LOG_ROUTING, ". . error '%s' (%d) auto-delete similar route to %s dev %s", err_msg, errno,
+                                     olsr_ip_to_string(&ibuf,&nexthop->gateway), nexthop->interface->int_name);
+                else { /* should never happen */
+                  OLSR_DEBUG(LOG_ROUTING, "# invalid internal route delete/add flag (%d) used!", flag);
+                }
+              }
+            }
+            else { /* netlink acks requests with an errno=0 NLMSG_ERROR response! */
+              rt_ret = 1;
+            }
+            if (flag != RT_NONE){
+              /* resolve "File exist" (17) propblems (on orig and autogen routes)*/
+              if ((errno == 17) & ((flag == RT_ORIG_REQUEST) | (flag == RT_AUTO_ADD_GATEWAY_ROUTE)) & (n->nlmsg_type == RTM_NEWROUTE)) {
+                /* a similar route going over another gateway may be present, which has to be deleted! */
+                OLSR_DEBUG(LOG_ROUTING, ". auto-deleting similar routes to resolve 'File exists' (17) while adding route!");
+                rt_ret = RT_DELETE_SIMILAR_ROUTE; /* processing will contiune after this loop */
+              }
+              /* report success on "No such process" (3) */
+              else if ((errno == 3) & (n->nlmsg_type == RTM_DELROUTE) & (flag == RT_ORIG_REQUEST)) {
+                /* another similar (but slightly different) route may be present at this point
+                 * , if so this will get solved when adding new route to this destination */
+                OLSR_DEBUG(LOG_ROUTING, ". ignoring 'No such process' (3) while deleting route!");
+                rt_ret = 0;
+              }
+              /* insert route to gateway on the fly if "Network unreachable" (128) on 2.4 kernels
+               * or on 2.6 kernel No such process (3) is reported in rtnetlink response 
+               * do this only with flat metric, as using metric values inherited from
+               * a target behind the gateway is really strange, and could lead to multiple routes!
+               * anyways if invalid gateway ips may happen we are f*cked up!!
+               * but if not, these on the fly generated routes are no problem, and will only get used when needed 
+               * warning currently only for ipv4 */
+              else if ( ((errno == 3)|(errno == 128)) & (flag == RT_ORIG_REQUEST) & (FIBM_FLAT == olsr_cnf->fib_metric)
+                       & (n->nlmsg_type == RTM_NEWROUTE) & (rt->rt_dst.prefix.v4.s_addr!=nexthop->gateway.v4.s_addr)) {
+                if (errno == 128) OLSR_DEBUG(LOG_ROUTING, ". autogenerating route to handle 'Network unreachable' (128) while adding route!");
+                else OLSR_DEBUG(LOG_ROUTING, ". autogenerating route to handle 'No such process' (3) while adding route!");
+
+                rt_ret = RT_AUTO_ADD_GATEWAY_ROUTE; /* processing will contiune after this loop */
+              }
             }
           }
           break;
@@ -111,21 +198,80 @@ static int olsr_netlink_send(struct nlmsghdr *n, char *buf, size_t bufSize) {
       }
     }
   }
-  return ret;
+  if (flag != RT_NONE){
+    if ( rt_ret == RT_DELETE_SIMILAR_ROUTE ) {//delete all routes that may collide
+      /* recursive call to delete simlar routes, using flag 2 to invoke deletion of similar, not only exact matches*/
+      rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_DELROUTE,
+                                      flag == RT_AUTO_ADD_GATEWAY_ROUTE ? RT_DELETE_SIMILAR_AUTO_ROUTE : RT_DELETE_SIMILAR_ROUTE);
+
+      /* retry insert original route, if deleting similar succeeded, using flag=1 to prevent recursions */
+      if (rt_ret > 0) rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_RETRY_AFTER_DELETE_SIMILAR);
+      else OLSR_DEBUG(LOG_ROUTING, ". failed on auto-deleting similar route conflicting with above route!");
+  
+      /* set appropriate return code for original request, while returning simple -1/1 if called recursive */
+      if (flag != RT_AUTO_ADD_GATEWAY_ROUTE) {
+        if (rt_ret > 0) rt_ret = 0; /* successful recovery */
+        else rt_ret = -1; /* unrecoverable error */
+      }
+    }
+    if ( rt_ret == RT_AUTO_ADD_GATEWAY_ROUTE ) { /* autoadd route via gateway */
+      /* recursive call to invoke creation of a route to the gateway */
+      rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_AUTO_ADD_GATEWAY_ROUTE);
+
+      /* retry insert original route, if above succeeded without problems */
+      if (rt_ret > 0) rt_ret = olsr_netlink_route_int(rt, family, rttable, RTM_NEWROUTE, RT_RETRY_AFTER_ADD_GATEWAY);
+      else OLSR_DEBUG(LOG_ROUTING, ". failed on inserting auto-generated route to gateway of above route!");
+
+      /* set appropriate return code for original request*/
+      if (rt_ret > 0) rt_ret = 0; /* successful recovery */
+      else rt_ret = -1; /* unrecoverable error */
+    }
+    /* send ipc update on success (deprecated!?)
+    if ( ( n->nlmsg_type != RTM_NEWRULE ) & ( n->nlmsg_type != RTM_DELRULE ) & (flag = RT_ORIG_REQUEST) & (0 <= rt_ret && olsr_cnf->ipc_connections > 0)) {
+      ipc_route_send_rtentry(&rt->rt_dst.prefix, &nexthop->gateway, metric, RTM_NEWROUTE == n->nlmsg_type,
+                               if_ifwithindex_name(nexthop->iif_index));
+    }*/
+    if (rt_ret == -2) OLSR_DEBUG(LOG_ROUTING, "no rtnetlink response! (no system ressources left?, everything may happen now ...)");
+    return rt_ret;
+  }
+  else return ret; 
 }
 
-static int olsr_netlink_route(const struct rt_entry *rt, uint8_t family, uint8_t rttable, __u16 cmd)
+/*external wrapper function for above patched multi purpose rtnetlink function
+ * int
+ * olsr_netlink_rule(uint8_t family, uint8_t rttable, uint16_t cmd)
+ * {
+ *   struct rt_entry rt;
+ *     return olsr_netlink_route_int(&rt, family, rttable, cmd, RT_ORIG_REQUEST);
+ *     }
+ *     */
+
+/*internal wrapper function for above patched function*/
+static int
+olsr_netlink_route(const struct rt_entry *rt, uint8_t family, uint8_t rttable, __u16 cmd)
+{
+  return olsr_netlink_route_int(rt, family, rttable, cmd, RT_ORIG_REQUEST);
+}
+
+/* returns
+ *  -1 on unrecoverable error (calling function will have to handle it)
+ *  0 on unexpected but recoverable rtnetlink behaviour
+ *  but some of the implemented recovery methods only cure symptoms,
+ *  not the cause, like unintelligent ordering of inserted routes.
+ *  1 on success */
+static int 
+olsr_netlink_route_int(const struct rt_entry *rt, uint8_t family, uint8_t rttable, __u16 cmd, uint8_t flag)
 {
   int ret = 0;
   struct olsr_rtreq req;
-  uint32_t metric = FIBM_FLAT != olsr_cnf->fib_metric
-    ? (RTM_NEWROUTE == cmd
-       ? rt->rt_best->rtp_metric.hops
-       : rt->rt_metric.hops)
-    : RT_METRIC_DEFAULT;
-  const struct rt_nexthop* nexthop = RTM_NEWROUTE == cmd
-    ? &rt->rt_best->rtp_nexthop
-    : &rt->rt_nexthop;
+  uint32_t metric = ((cmd != RTM_NEWRULE) | (cmd != RTM_DELRULE)) ?
+    FIBM_FLAT != olsr_cnf->fib_metric ?
+      ((RTM_NEWROUTE == cmd) ? rt->rt_best->rtp_metric.hops : rt->rt_metric.hops)
+      : RT_METRIC_DEFAULT
+      : 0;
+  const struct rt_nexthop *nexthop = ( ( cmd != RTM_NEWRULE ) | ( cmd != RTM_DELRULE ) ) ?
+                                       ( RTM_NEWROUTE == cmd ) ? &rt->rt_best->rtp_nexthop : &rt->rt_nexthop
+                                       : NULL;
 
   memset(&req, 0, sizeof(req));
   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
@@ -133,38 +279,67 @@ static int olsr_netlink_route(const struct rt_entry *rt, uint8_t family, uint8_t
   req.n.nlmsg_type = cmd;
   req.r.rtm_family = family;
   req.r.rtm_table = rttable;
-  req.r.rtm_protocol = olsr_cnf->rtproto > 0 ? olsr_cnf->rtproto : RTPROT_BOOT;
-  req.r.rtm_scope = RT_SCOPE_LINK;
-  req.r.rtm_type = RTN_UNICAST;
+  /* RTN_UNSPEC would be the wildcard, but blackhole broadcast or nat roules should usually not conflict */
+  req.r.rtm_type = RTN_UNICAST; /* -> olsr only adds/deletes unicast routes */
+  req.r.rtm_protocol = RTPROT_UNSPEC; /* wildcard to delete routes of all protos if no simlar-delete correct proto will get set below */
+  req.r.rtm_scope = RT_SCOPE_NOWHERE; /* as wildcard for deletion */
   req.r.rtm_dst_len = rt->rt_dst.prefix_len;
 
-  /* 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*/
-  if (AF_INET == family) {
-    olsr_netlink_addreq(&req.n, sizeof(req), RTA_PREFSRC, &olsr_cnf->router_id.v4, sizeof(olsr_cnf->router_id.v4));
-  }
-  else {
-    olsr_netlink_addreq(&req.n, sizeof(req), RTA_PREFSRC, &olsr_cnf->router_id.v6, sizeof(olsr_cnf->router_id.v6));
-  }
-
   if (NULL != nexthop->interface) {
-    if (AF_INET == family) {
-      if (ip4cmp(&rt->rt_dst.prefix.v4, &nexthop->gateway.v4) != 0) {
-        olsr_netlink_addreq(&req.n, sizeof(req), RTA_GATEWAY, &nexthop->gateway.v4, sizeof(nexthop->gateway.v4));
-        req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+    if (( cmd != RTM_NEWRULE ) & ( cmd != RTM_DELRULE ) ) {
+      req.r.rtm_dst_len = rt->rt_dst.prefix_len;
+
+      /* do not specify much as we wanna delete similar/conflicting routes */
+      if ( ( flag != RT_DELETE_SIMILAR_ROUTE ) & ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE )) {
+        /* 0 gets replaced by OS-specifc default (3)
+         * 1 is reserved so we take 0 instead (this really makes some sense)
+         * other numbers are used 1:1 */
+        req.r.rtm_protocol = ( (olsr_cnf->rtproto<1) ? RTPROT_BOOT : ( (olsr_cnf->rtproto==1) ? 0 : olsr_cnf->rtproto) );
+        req.r.rtm_scope = RT_SCOPE_LINK;
+  
+        /*add interface*/
+        olsr_netlink_addreq(&req.n, sizeof(req), RTA_OIF, &nexthop->interface->if_index, sizeof(nexthop->interface->if_index));
+
+        #if SOURCE_IP_ROUTES
+        if (AF_INET == family) olsr_netlink_addreq(&req.n, sizeof(req), RTA_PREFSRC, &olsr_cnf->router_id.v4, sizeof(olsr_cnf->router_id.v4));
+        else olsr_netlink_addreq(&req.n, sizeof(req), RTA_PREFSRC, &olsr_cnf->router_id.v6, sizeof(&olsr_cnf->router_id.v6));
+        #endif
       }
-      olsr_netlink_addreq(&req.n, sizeof(req), RTA_DST, &rt->rt_dst.prefix.v4, sizeof(rt->rt_dst.prefix.v4));
-    } else {
-      if (ip6cmp(&rt->rt_dst.prefix.v6, &nexthop->gateway.v6) != 0) {
-        olsr_netlink_addreq(&req.n, sizeof(req), RTA_GATEWAY, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6));
-        req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+
+      /* metric is specified always as we can only delete one route per iteration, and wanna hit the correct one first */
+      if (FIBM_APPROX != olsr_cnf->fib_metric || (RTM_NEWROUTE == cmd) ) {
+        olsr_netlink_addreq(&req.n, sizeof(req), RTA_PRIORITY, &metric, sizeof(metric));
       }
-      olsr_netlink_addreq(&req.n, sizeof(req), RTA_DST, &rt->rt_dst.prefix.v6, sizeof(rt->rt_dst.prefix.v6));
-    }
-    if (FIBM_APPROX != olsr_cnf->fib_metric || RTM_NEWROUTE == cmd) {
-      olsr_netlink_addreq(&req.n, sizeof(req), RTA_PRIORITY, &metric, sizeof(metric));
+
+      /* make sure that netmask = /32 as this is an autogenarated route */
+      if (( flag == RT_AUTO_ADD_GATEWAY_ROUTE ) | (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) req.r.rtm_dst_len = 32;
+
+      /* for ipv4 or ipv6 we add gateway if one is specified,
+       * or leave gateway away if we want to delete similar routes aswell,
+       * or even use the gateway as target if we add a auto-generated route,
+       * or if delete-similar to make insertion of auto-generated route possible */
+      if (AF_INET == family) {
+        if ( ( flag != RT_AUTO_ADD_GATEWAY_ROUTE ) & (flag != RT_DELETE_SIMILAR_ROUTE) &
+             ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE) & (rt->rt_dst.prefix.v4.s_addr != nexthop->gateway.v4.s_addr) ) {
+          olsr_netlink_addreq(&req.n, sizeof(req), RTA_GATEWAY, &nexthop->gateway.v4, sizeof(nexthop->gateway.v4));
+          req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+        }
+        olsr_netlink_addreq(&req.n, sizeof(req), RTA_DST, ( (flag == RT_AUTO_ADD_GATEWAY_ROUTE) | (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) ?
+                            &nexthop->gateway.v4 : &rt->rt_dst.prefix.v4, sizeof(rt->rt_dst.prefix.v4));
+      } else {
+        if ( ( flag != RT_AUTO_ADD_GATEWAY_ROUTE ) & (flag != RT_DELETE_SIMILAR_ROUTE ) & ( flag != RT_DELETE_SIMILAR_AUTO_ROUTE)
+            & (0 != memcmp(&rt->rt_dst.prefix.v6, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6))) ) {
+          olsr_netlink_addreq(&req.n, sizeof(req), RTA_GATEWAY, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6));
+          req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+        }
+        olsr_netlink_addreq(&req.n, sizeof(req), RTA_DST, ( (flag == RT_AUTO_ADD_GATEWAY_ROUTE) | (flag == RT_DELETE_SIMILAR_AUTO_ROUTE) ) ?
+                            &nexthop->gateway.v6 : &rt->rt_dst.prefix.v6, sizeof(rt->rt_dst.prefix.v6));
+      }
+    } else {//add or delete a rule
+      static uint32_t priority = 65535;
+      req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+      olsr_netlink_addreq(&req.n, sizeof(req), RTA_PRIORITY, &priority, sizeof(priority));
     }
-    olsr_netlink_addreq(&req.n, sizeof(req), RTA_OIF, &nexthop->interface->if_index,
-                        sizeof(nexthop->interface->if_index));
   }
   else {
     /*
@@ -172,7 +347,7 @@ static int olsr_netlink_route(const struct rt_entry *rt, uint8_t family, uint8_t
      */
     req.r.rtm_scope = RT_SCOPE_NOWHERE;
   }
-  ret = olsr_netlink_send(&req.n, req.buf, sizeof(req.buf));
+  ret = olsr_netlink_send(&req.n, req.buf, sizeof(req.buf), flag, rt, nexthop, family, rttable);
   if (0 <= ret && olsr_cnf->ipc_connections > 0) {
     ipc_route_send_rtentry(&rt->rt_dst.prefix,
          &nexthop->gateway,
@@ -275,7 +450,7 @@ int olsr_create_lo_interface(union olsr_ip_addr *ip) {
 
   req.ifa.ifa_index = if_nametoindex("lo");
 
-  return olsr_netlink_send(&req.n, req.buf, sizeof(req.buf));
+  return olsr_netlink_send(&req.n, req.buf, sizeof(req.buf), RT_NONE, NULL, NULL, 0, 0);
 }
 
 int olsr_delete_lo_interface(union olsr_ip_addr *ip) {
@@ -296,7 +471,7 @@ int olsr_delete_lo_interface(union olsr_ip_addr *ip) {
 
   req.ifa.ifa_index = if_nametoindex("lo");
 
-  return olsr_netlink_send(&req.n, req.buf, sizeof(req.buf));
+  return olsr_netlink_send(&req.n, req.buf, sizeof(req.buf), RT_NONE, NULL, NULL, 0, 0);
 }
 
 /*