Get starter default route via rtnetlink
authorHeiko Hund <hhund@astaro.com>
Tue, 8 Sep 2009 09:32:50 +0000 (11:32 +0200)
committerMartin Willi <martin@strongswan.org>
Tue, 15 Sep 2009 10:55:25 +0000 (12:55 +0200)
This patch changes the way routes are fetched from the kernel by starter.

The way it's currently done (via /proc) is limited to routes in the
"main" routing table. Routes from the "default" table are never seen by
starter. Starter may miss the default route even if it's set. Thus, default
routes are now read from the "main" and the "default" table.

The way this code behaves if more than one default route is found is slightly
different to before. Instead of bailing out it just chooses the one with the best
metric. I thought this was be a reasonable change.

src/starter/interfaces.c

index 3fff65b..d12bf0b 100644 (file)
@@ -1,5 +1,6 @@
 /* strongSwan IPsec interfaces management
  * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
 /* strongSwan IPsec interfaces management
  * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
+ *               2009 Heiko Hund - Astaro AG
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -14,6 +15,7 @@
 
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <linux/rtnetlink.h>
 #ifdef HAVE_SYS_SOCKIO_H
 #include <sys/sockio.h>
 #endif
 #ifdef HAVE_SYS_SOCKIO_H
 #include <sys/sockio.h>
 #endif
 #include "files.h"
 
 /*
 #include "files.h"
 
 /*
- * discover the default route via /proc/net/route
+ * Get the default route information via rtnetlink
  */
 void
 get_defaultroute(defaultroute_t *defaultroute)
 {
  */
 void
 get_defaultroute(defaultroute_t *defaultroute)
 {
-       FILE *fd;
-       char line[BUF_LEN];
-       bool first = TRUE;
-
-       memset(defaultroute, 0, sizeof(defaultroute_t));
+       union {
+               struct {
+                       struct nlmsghdr nh;
+                       struct rtmsg    rt;
+               } m;
+               char buf[4096];
+       } rtu;
+
+       struct nlmsghdr *nh;
+       uint32_t best_metric = ~0;
+       ssize_t msglen;
+       int fd;
+
+       bzero(&rtu, sizeof(rtu));
+       rtu.m.nh.nlmsg_len = NLMSG_LENGTH(sizeof(rtu.m.rt));
+       rtu.m.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       rtu.m.nh.nlmsg_type = RTM_GETROUTE;
+       rtu.m.rt.rtm_family = AF_INET;
+       rtu.m.rt.rtm_table = RT_TABLE_UNSPEC;
+       rtu.m.rt.rtm_protocol = RTPROT_UNSPEC;
+       rtu.m.rt.rtm_type = RTN_UNICAST;
+
+       fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (fd == -1)
+       {
+               plog("could not create rtnetlink socket");
+               return;
+       }
 
 
-       fd = fopen("/proc/net/route", "r");
+       if (send(fd, &rtu, rtu.m.nh.nlmsg_len, 0) == -1)
+       {
+               plog("could not write to rtnetlink socket");
+               close(fd);
+               return;
+       }
 
 
-       if (!fd)
+       msglen = recv(fd, &rtu, sizeof(rtu), MSG_WAITALL);
+       if (msglen == -1)
        {
        {
-               plog("could not open 'proc/net/route'");
+               plog("could not read from rtnetlink socket");
+               close(fd);
                return;
        }
 
                return;
        }
 
-       while (fgets(line, sizeof(line), fd) != 0)
+       close(fd);
+
+       for (nh = &rtu.m.nh; NLMSG_OK(nh, msglen); nh = NLMSG_NEXT(nh, msglen))
        {
        {
-               char iface[11];
-               char destination[9];
-               char gateway[11];
-               char flags[5];
-               char mask[9];
-
-               int refcnt;
-               int use;
-               int metric;
-               int items;
-
-               /* proc/net/route returns IP addresses in host order */
-               strcpy(gateway, "0h");
-
-               /* skip the header line */
-               if (first)
+               struct rtmsg *rt;
+               struct rtattr *rta;
+               uint32_t rtalen, metric = 0;
+               struct in_addr gw = { .s_addr = INADDR_ANY };
+               int iface_idx = -1;
+
+               if (nh->nlmsg_type == NLMSG_ERROR)
                {
                {
-                       first = FALSE;
-                       continue;
+                       plog("error from rtnetlink");
+                       return;
                }
 
                }
 
-               /* parsing a single line of proc/net/route */
-               items = sscanf(line, "%10s\t%8s\t%8s\t%5s\t%d\t%d\t%d\t%8s\t"
-                                        , iface, destination, gateway+2, flags, &refcnt, &use, &metric, mask);
-               if (items < 8)
-               {
-                       plog("parsing error while scanning /proc/net/route");
+               if (nh->nlmsg_type == NLMSG_DONE)
+                       break;
+
+               rt = NLMSG_DATA(nh);
+               if ( rt->rtm_dst_len != 0
+               ||  (rt->rtm_table != RT_TABLE_MAIN
+                 && rt->rtm_table != RT_TABLE_DEFAULT) )
                        continue;
                        continue;
+
+               rta = RTM_RTA(rt);
+               rtalen = RTM_PAYLOAD(nh);
+               while ( RTA_OK(rta, rtalen) )
+               {
+                       switch (rta->rta_type)
+                       {
+                       case RTA_GATEWAY:
+                               gw = *(struct in_addr *) RTA_DATA(rta);
+                               break;
+                       case RTA_OIF:
+                               iface_idx = *(int *) RTA_DATA(rta);
+                               break;
+                       case RTA_PRIORITY:
+                               metric = *(uint32_t *) RTA_DATA(rta);
+                               break;
+                       }
+                       rta = RTA_NEXT(rta, rtalen);
                }
 
                }
 
-               /* check for defaultroute (destination 0.0.0.0 and mask 0.0.0.0) */
-               if (streq(destination, "00000000") && streq(mask, "00000000"))
+               if (metric < best_metric
+               &&  gw.s_addr != INADDR_ANY
+               &&  iface_idx != -1)
                {
                {
-                       if (defaultroute->defined)
+                       struct ifreq req;
+
+                       fd = socket(AF_INET, SOCK_DGRAM, 0);
+                       if (fd < 0)
                        {
                        {
-                               plog("multiple default routes - cannot cope with %%defaultroute!!!");
+                               plog("could not open AF_INET socket");
                                defaultroute->defined = FALSE;
                                defaultroute->defined = FALSE;
-                               fclose(fd);
-                               return;
+                               break;
                        }
                        }
-                       ttoaddr(gateway, strlen(gateway), AF_INET, &defaultroute->nexthop);
-                       strncpy(defaultroute->iface, iface, IFNAMSIZ);
+                       bzero(&req, sizeof(req));
+                       req.ifr_ifindex = iface_idx;
+                       ioctl(fd, SIOCGIFNAME, &req);
+                       ioctl(fd, SIOCGIFADDR, &req);
+                       close(fd);
+
+                       strncpy(defaultroute->iface, req.ifr_name, IFNAMSIZ);
+                       defaultroute->addr.u.v4 = *((struct sockaddr_in *) &req.ifr_addr);
+                       defaultroute->nexthop.u.v4.sin_family = AF_INET;
+                       defaultroute->nexthop.u.v4.sin_addr = gw;
+
+                       DBG(DBG_CONTROL,
+                               char addr[20];
+                               char nexthop[20];
+                               addrtot(&defaultroute->addr, 0, addr, sizeof(addr));
+                               addrtot(&defaultroute->nexthop, 0, nexthop, sizeof(nexthop));
+
+                               DBG_log(
+                                       ( !defaultroute->defined
+                                       ? "Default route found: iface=%s, addr=%s, nexthop=%s"
+                                       : "Better default route: iface=%s, addr=%s, nexthop=%s"
+                                       ), defaultroute->iface, addr, nexthop
+                               )
+                       );
+
+                       best_metric = metric;
                        defaultroute->defined = TRUE;
                }
        }
                        defaultroute->defined = TRUE;
                }
        }
-       fclose(fd);
 
        if (!defaultroute->defined)
 
        if (!defaultroute->defined)
-       {
                plog("no default route - cannot cope with %%defaultroute!!!");
                plog("no default route - cannot cope with %%defaultroute!!!");
-       }
-       else
-       {
-               char addr_buf[20], nexthop_buf[20];
-               struct ifreq physreq;
-
-               int sock = socket(AF_INET, SOCK_DGRAM, 0);
-
-               /* determine IP address of iface */
-               if (sock < 0)
-               {
-                       plog("could not open SOCK_DGRAM socket");
-                       defaultroute->defined = FALSE;
-                       return;
-               }
-               memset ((void*)&physreq, 0, sizeof(physreq));
-               strncpy(physreq.ifr_name, defaultroute->iface, IFNAMSIZ);
-               ioctl(sock, SIOCGIFADDR, &physreq);
-               close(sock);
-               defaultroute->addr.u.v4 = *((struct sockaddr_in *)&physreq.ifr_addr);
-
-               addrtot(&defaultroute->addr, 0, addr_buf, sizeof(addr_buf));
-               addrtot(&defaultroute->nexthop, 0, nexthop_buf, sizeof(nexthop_buf));
-
-               DBG(DBG_CONTROL,
-                       DBG_log("Default route found: iface=%s, addr=%s, nexthop=%s"
-                               , defaultroute->iface, addr_buf, nexthop_buf)
-               )
-
-               /* for backwards-compatibility with the awk shell scripts
-                * store the defaultroute in /var/run/ipsec.info
-                */
-               fd = fopen(INFO_FILE, "w");
-
-               if (fd)
-               {
-                       fprintf(fd, "defaultroutephys=%s\n", defaultroute->iface );
-                       fprintf(fd, "defaultroutevirt=ipsec0\n");
-                       fprintf(fd, "defaultrouteaddr=%s\n", addr_buf);
-                       fprintf(fd, "defaultroutenexthop=%s\n", nexthop_buf);
-                       fclose(fd);
-               }
-       }
-       return;
 }
 }