diff --git a/src/ipv4.c b/src/ipv4.c index 01e34af69d29d0fa76868db4259b19c2d476982e..99d73081ff57a8c2260fe04aa712fa3c764e6459 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -107,8 +107,12 @@ static inline void route_destroy(struct rtentry *route) /* * Finds system IP route to a destination. * - * The passed route must have dest and mask set. If the route is found, the - * function fills the gtw and iface properties. + * The passed route must have dest and mask set. If the route is found, + * the function searches for a match in the routing table and returns + * that one. Note that dest and mask contain the network address and + * the mask of the corresponding routing table entry after calling. + * After calling ipv4_get_route it might be necessary to set dest + * and mask again to the desired values for further processing. */ static int ipv4_get_route(struct rtentry *route) { @@ -116,9 +120,22 @@ static int ipv4_get_route(struct rtentry *route) char buffer[0x1000]; char *start, *line; char *saveptr1 = NULL, *saveptr2 = NULL; + uint32_t rtdest, rtmask, rtgtw; + int rtfound = 0; log_debug("ip route show %s\n", ipv4_show_route(route)); + // store what we are looking for + rtdest = route_dest(route).s_addr; + rtmask = route_mask(route).s_addr; + rtgtw = route_gtw(route).s_addr; + + + // initialize the output record + route_dest(route).s_addr = inet_addr("0.0.0.0"); + route_mask(route).s_addr = inet_addr("0.0.0.0"); + route_gtw(route).s_addr = inet_addr("0.0.0.0"); + #ifdef __APPLE__ FILE *fp; int len = sizeof(buffer) - 1; @@ -144,8 +161,20 @@ static int ipv4_get_route(struct rtentry *route) unsigned short flag_table[256] = { 0 }; - // fill the table now (I'm still looking for a more elagant way to do this), - // also, not all flags might be allowed in the context of ipv4 + /* + * Fill the flag_table now. Unfortunately it is not easy + * to do this in a more elegant way. The problem here + * is that these are already preprocessor macros and + * we can't use them as arguments for another macro which + * would include the #ifdef statements. + * + * Also, not all flags might be allowed in the context + * of ipv4, and the code depends on which ones are + * actually implemented on the target platform, which + * might also be varying between OSX versions. + * + */ + #ifdef RTF_PROTO1 // Protocol specific routing flag #1 flag_table['1'] = RTF_PROTO1 & USHRT_MAX; #endif @@ -243,7 +272,7 @@ static int ipv4_get_route(struct rtentry *route) start++; #ifdef __APPLE__ - // Skip 3 more line + // Skip 3 more lines on Mac OSX start = index(start, '\n'); start = index(++start, '\n'); start = index(++start, '\n'); @@ -273,6 +302,10 @@ static int ipv4_get_route(struct rtentry *route) mask = UINT32_MAX; // "Destination" tmpstr = strtok_r(line, " ", &saveptr2); + if (strncmp(tmpstr, "Internet6", 9) == 0) { + // we have arrived at the end of ipv4 output + goto end; + } log_debug("- Destination: %s\n", tmpstr); // replace literal "default" route by IPV4 numbers-and-dots notation if (strncmp(tmpstr, "default", 7) == 0) { @@ -359,29 +392,89 @@ static int ipv4_get_route(struct rtentry *route) window = strtol(strtok_r(NULL, "\t", &saveptr2), NULL, 16); irtt = strtol(strtok_r(NULL, "\t", &saveptr2), NULL, 16); #endif + /* + * Now that we have parsed a routing entry, check if it + * matches the current argument to the function call. + * In rtentry we have integer representation, i.e. + * the most significant byte corresponds to the last + * number of dotted-number representation and vice versa. + * In this representation ( address & mask ) is the network + * address. + * The routing algorithm does the following: + * First, check if the network address we are looking for + * falls into the network for the current route. + * Therefore, calculate the network address for both, the + * current route and for the destination we are searching. + * If the destination is a smaller network (for instance a + * single host), we have to mask again with the netmask of + * the routing entry that we are checking in order to obtain + * the network address in the context of the current route. + * If both network addresses match, we have found a candidate + * for a route. + * However, there might be another route for a smaller network, + * therefore repeat this and only store the resulting route + * when the mask is at least as large as the one we may + * have already found in a previous iteration (a larger + * netmask corresponds to a smaller network in this + * representation, and has a higher prority by default). + * Also, only consider routing entries for which the + * netmask is not larger than the netmask used in the + * argument when calling the function - so that we can + * distinguish between different routing entries for subnets + * of different size but with the same network address. + * For routing entries with the same destination and + * the same netmask the metric can be used for adjusting + * the priority (this is not supported on mac). + * If the metric is larger than one found for this network + * size, skip the current route (smaller numbers denote + * less hops and therefore have a higher priority). + */ + + if (((dest & mask) == (rtdest & rtmask & mask)) + && (mask >= route_mask(route).s_addr) + && (mask <= rtmask)) { +#ifndef __APPLE__ + if (((mask == route_mask(route).s_addr) + && (metric <= route->rt_metric)) + || (rtfound == 0) + || (mask > route_mask(route).s_addr)) { +#endif + rtfound = 1; + // Requested route has been found + route_dest(route).s_addr = dest; + route_mask(route).s_addr = mask; + route_gtw(route).s_addr = gtw; + route->rt_flags = flags; + + strncpy(route_iface(route), iface, + ROUTE_IFACE_LEN - 1); - if (dest == route_dest(route).s_addr && - mask == route_mask(route).s_addr) { - // Requested route has been found - route_gtw(route).s_addr = gtw; - route->rt_flags = flags; #ifndef __APPLE__ - // we do not have these values from Mac OS X netstat, - // so stay with defaults denoted by values of 0 - route->rt_metric = metric; - route->rt_mtu = mtu; - route->rt_window = window; - route->rt_irtt = irtt; + // we do not have these values from Mac OS X netstat, + // so stay with defaults denoted by values of 0 + route->rt_metric = metric; + route->rt_mtu = mtu; + route->rt_window = window; + route->rt_irtt = irtt; + } #endif - strncpy(route_iface(route), iface, - ROUTE_IFACE_LEN - 1); - return 0; } line = strtok_r(NULL, "\n", &saveptr1); } - log_debug("Route not found.\n"); +#ifdef __APPLE__ +end: +#endif + if (rtfound==0) { + // should not occur anymore unless there is no default route + log_debug("Route not found.\n"); + // at least restore input values + route_dest(route).s_addr = rtdest; + route_mask(route).s_addr = rtmask; + route_gtw(route).s_addr = rtgtw; + return ERR_IPV4_NO_SUCH_ROUTE; + } - return ERR_IPV4_NO_SUCH_ROUTE; + return 0; } static int ipv4_set_route(struct rtentry *route) @@ -485,11 +578,16 @@ int ipv4_protect_tunnel_route(struct tunnel *tunnel) } - // Set the default route as the route to the tunnel gateway - char *iface = route_iface(gtw_rt); - memcpy(gtw_rt, def_rt, sizeof(*gtw_rt)); - route_iface(gtw_rt) = iface; - strncpy(route_iface(gtw_rt), route_iface(def_rt), ROUTE_IFACE_LEN - 1); + // Set the up a route to the tunnel gateway + route_dest(gtw_rt).s_addr = tunnel->config->gateway_ip.s_addr; + route_mask(gtw_rt).s_addr = inet_addr("255.255.255.255"); + ret = ipv4_get_route(gtw_rt); + if (ret != 0) { + log_warn("Could not get route to gateway (%s).\n", + err_ipv4_str(ret)); + log_warn("Protecting tunnel route has failed. But this can be working except for some cases.\n"); + goto err_destroy; + } route_dest(gtw_rt).s_addr = tunnel->config->gateway_ip.s_addr; route_mask(gtw_rt).s_addr = inet_addr("255.255.255.255"); gtw_rt->rt_flags |= RTF_HOST; @@ -497,6 +595,7 @@ int ipv4_protect_tunnel_route(struct tunnel *tunnel) tunnel->ipv4.route_to_vpn_is_added = 1; log_debug("Setting route to vpn server...\n"); + log_debug("ip route show %s\n", ipv4_show_route(gtw_rt)); ret = ipv4_set_route(gtw_rt); if (ret == ERR_IPV4_SEE_ERRNO && errno == EEXIST) { log_warn("Route to vpn server exists already.\n");