kernel-netlink: Allow blank source address in routes for passthrough policies
authorTobias Brunner <tobias@strongswan.org>
Tue, 25 Feb 2020 15:39:35 +0000 (16:39 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 10 Mar 2020 09:25:19 +0000 (10:25 +0100)
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c

index 9a91549..da22c0b 100644 (file)
@@ -2622,88 +2622,92 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
        );
 
        if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts,
-                                                                                 &route->src_ip, NULL) == SUCCESS)
+                                                                                 &route->src_ip, NULL) != SUCCESS)
        {
-               if (!ipsec->dst->is_anyaddr(ipsec->dst))
+               if (!route->pass)
                {
-                       route->gateway = charon->kernel->get_nexthop(charon->kernel,
-                                                                                               ipsec->dst, -1, ipsec->src,
-                                                                                               &route->if_name);
+                       free(route);
+                       return;
                }
-               else
-               {       /* for shunt policies */
-                       iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0);
-                       route->gateway = charon->kernel->get_nexthop(charon->kernel,
-                                                                                               iface, policy->sel.prefixlen_d,
-                                                                                               route->src_ip, &route->if_name);
-                       iface->destroy(iface);
-               }
-               route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
-               memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len);
-
-               /* get the interface to install the route for, if we haven't one yet.
-                * If we have a local address, use it. Otherwise (for shunt policies)
-                * use the route's source address. */
-               if (!route->if_name)
-               {
-                       iface = ipsec->src;
-                       if (iface->is_anyaddr(iface))
-                       {
-                               iface = route->src_ip;
-                       }
-                       if (!charon->kernel->get_interface(charon->kernel, iface,
-                                                                                          &route->if_name))
-                       {
-                               route_entry_destroy(route);
-                               return;
-                       }
+               /* allow blank source IP for passthrough policies */
+               route->src_ip = host_create_any(policy->sel.family);
+       }
+
+       if (!ipsec->dst->is_anyaddr(ipsec->dst))
+       {
+               route->gateway = charon->kernel->get_nexthop(charon->kernel,
+                                                                                       ipsec->dst, -1, ipsec->src,
+                                                                                       &route->if_name);
+       }
+       else
+       {       /* for shunt policies */
+               iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0);
+               route->gateway = charon->kernel->get_nexthop(charon->kernel,
+                                                                                       iface, policy->sel.prefixlen_d,
+                                                                                       route->src_ip, &route->if_name);
+               iface->destroy(iface);
+       }
+       route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
+       memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len);
+
+       /* get the interface to install the route for, if we haven't one yet.
+        * If we have a local address, use it. Otherwise (for shunt policies)
+        * use the route's source address. */
+       if (!route->if_name)
+       {
+               iface = ipsec->src;
+               if (iface->is_anyaddr(iface))
+               {
+                       iface = route->src_ip;
                }
-               if (policy->route)
+               if (!charon->kernel->get_interface(charon->kernel, iface,
+                                                                                  &route->if_name))
                {
-                       route_entry_t *old = policy->route;
-                       if (route_entry_equals(old, route))
-                       {
-                               route_entry_destroy(route);
-                               return;
-                       }
-                       /* uninstall previously installed route */
-                       if (charon->kernel->del_route(charon->kernel, old->dst_net,
-                                                                                 old->prefixlen, old->gateway,
-                                                                                 old->src_ip, old->if_name,
-                                                                                 old->pass) != SUCCESS)
-                       {
-                               DBG1(DBG_KNL, "error uninstalling route installed with policy "
-                                        "%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names,
-                                        policy->direction);
-                       }
-                       route_entry_destroy(old);
-                       policy->route = NULL;
+                       route_entry_destroy(route);
+                       return;
                }
-
-               DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts,
-                        route->gateway, route->src_ip, route->if_name);
-               switch (charon->kernel->add_route(charon->kernel, route->dst_net,
-                                                                                 route->prefixlen, route->gateway,
-                                                                                 route->src_ip, route->if_name,
-                                                                                 route->pass))
+       }
+       if (policy->route)
+       {
+               route_entry_t *old = policy->route;
+               if (route_entry_equals(old, route))
                {
-                       default:
-                               DBG1(DBG_KNL, "unable to install source route for %H",
-                                        route->src_ip);
-                               /* FALL */
-                       case ALREADY_DONE:
-                               /* route exists, do not uninstall */
-                               route_entry_destroy(route);
-                               break;
-                       case SUCCESS:
-                               /* cache the installed route */
-                               policy->route = route;
-                               break;
+                       route_entry_destroy(route);
+                       return;
                }
+               /* uninstall previously installed route */
+               if (charon->kernel->del_route(charon->kernel, old->dst_net,
+                                                                         old->prefixlen, old->gateway,
+                                                                         old->src_ip, old->if_name,
+                                                                         old->pass) != SUCCESS)
+               {
+                       DBG1(DBG_KNL, "error uninstalling route installed with policy "
+                                "%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names,
+                                policy->direction);
+               }
+               route_entry_destroy(old);
+               policy->route = NULL;
        }
-       else
+
+       DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts,
+                route->gateway, route->src_ip, route->if_name);
+       switch (charon->kernel->add_route(charon->kernel, route->dst_net,
+                                                                         route->prefixlen, route->gateway,
+                                                                         route->src_ip, route->if_name,
+                                                                         route->pass))
        {
-               free(route);
+               default:
+                       DBG1(DBG_KNL, "unable to install source route for %H",
+                                route->src_ip);
+                       /* FALL */
+               case ALREADY_DONE:
+                       /* route exists, do not uninstall */
+                       route_entry_destroy(route);
+                       break;
+               case SUCCESS:
+                       /* cache the installed route */
+                       policy->route = route;
+                       break;
        }
 }