From: Heiko Hund Date: Tue, 8 Sep 2009 09:32:50 +0000 (+0200) Subject: Get starter default route via rtnetlink X-Git-Tag: 4.3.5rc1~159 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa5d446939b6ead13fbde58fdb17a972fa31e9c6;p=thirdparty%2Fstrongswan.git Get starter default route via rtnetlink 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. --- diff --git a/src/starter/interfaces.c b/src/starter/interfaces.c index 3fff65be72..d12bf0bdfc 100644 --- a/src/starter/interfaces.c +++ b/src/starter/interfaces.c @@ -1,5 +1,6 @@ /* 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 @@ -14,6 +15,7 @@ #include #include +#include #ifdef HAVE_SYS_SOCKIO_H #include #endif @@ -34,119 +36,142 @@ #include "files.h" /* - * discover the default route via /proc/net/route + * Get the default route information via rtnetlink */ 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; } - 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; + + 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; - 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; } } - fclose(fd); if (!defaultroute->defined) - { 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; }