[Openvpn-devel,L] Change in openvpn[master]: route: extended logic to omit gateway when unnecessary

Message ID 86d445f5c4c6a61c2c7ee28d5d981ebb4138ed23-HTML@gerrit.openvpn.net
State New
Headers show
Series [Openvpn-devel,L] Change in openvpn[master]: route: extended logic to omit gateway when unnecessary | expand

Commit Message

ordex (Code Review) July 18, 2024, 2:03 p.m. UTC
Attention is currently required from: flichtenheld, plaisthos.

Hello plaisthos, flichtenheld,

I'd like you to do a code review.
Please visit

    http://gerrit.openvpn.net/c/openvpn/+/678?usp=email

to review the following change.


Change subject: route: extended logic to omit gateway when unnecessary
......................................................................

route: extended logic to omit gateway when unnecessary

Extracted and extended the logic behind 'gateway_needed' both in
add_route() and add_route_ipv6(). Other than checking the dev-type,
special routes and if the gateway is on-link,
- set gateway_needed to true if the vpn instance is a multipoint server
  and DCO is enabled.
- set gateway_needed to false if the gateway is in the vpn subnet.
Additionally, extended support for these checks and conditions to DARWIN
and BSD-based operating systems.

These changes ensure that the gateway is only included when necessary,
optimizing route configuration and potentially reducing redundant route entries.

Change-Id: I3dbd61fef20d683734b55b73e40afe56c3b733e1
Signed-off-by: Marco Baffo <marco@mandelbit.com>
---
M src/openvpn/forward.c
M src/openvpn/init.c
M src/openvpn/init.h
M src/openvpn/route.c
M src/openvpn/route.h
M src/openvpn/tun.c
M src/openvpn/tun.h
7 files changed, 253 insertions(+), 128 deletions(-)



  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/78/678/1

Patch

diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 71b7167..35773ec 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -448,7 +448,7 @@ 
 check_add_routes_action(struct context *c, const bool errors)
 {
     bool route_status = do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
-                                 c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
+                                 c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx, c->mode == CM_TOP);
 
     int flags = (errors ? ISC_ERRORS : 0);
     flags |= (!route_status ? ISC_ROUTE_ERRORS : 0);
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index a49e563..b0a718e 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1689,13 +1689,14 @@ 
          const struct tuntap *tt,
          const struct plugin_list *plugins,
          struct env_set *es,
-         openvpn_net_ctx_t *ctx)
+         openvpn_net_ctx_t *ctx,
+         const bool is_multipoint)
 {
     bool ret = true;
     if (!options->route_noexec && ( route_list || route_ipv6_list ) )
     {
         ret = add_routes(route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS(options),
-                         es, ctx);
+                         es, ctx, is_multipoint);
         setenv_int(es, "redirect_gateway", route_did_redirect_default_gateway(route_list));
     }
 #ifdef ENABLE_MANAGEMENT
@@ -1898,7 +1899,7 @@ 
                                                  c->options.dev_node,
                                                  &gc);
             do_ifconfig(c->c1.tuntap, guess, c->c2.frame.tun_mtu, c->c2.es,
-                        &c->net_ctx);
+                        &c->net_ctx, c->mode == CM_TOP);
         }
 
         /* possibly add routes */
@@ -1906,7 +1907,7 @@ 
         {
             /* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */
             bool status = do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
-                                   c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
+                                   c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx, c->mode == CM_TOP);
             *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
         }
 #ifdef TARGET_ANDROID
@@ -1934,7 +1935,7 @@ 
             && ifconfig_order() == IFCONFIG_AFTER_TUN_OPEN)
         {
             do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name,
-                        c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
+                        c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx, c->mode == CM_TOP);
         }
 
         /* run the up script */
@@ -1960,7 +1961,7 @@ 
         if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
         {
             int status = do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
-                                  c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
+                                  c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx, c->mode == CM_TOP);
             *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
         }
 
@@ -2086,7 +2087,7 @@ 
 
             delete_routes(c->c1.route_list, c->c1.route_ipv6_list,
                           c->c1.tuntap, ROUTE_OPTION_FLAGS(&c->options),
-                          c->c2.es, &c->net_ctx);
+                          c->c2.es, &c->net_ctx, c->mode == CM_TOP);
         }
 
         /* actually close tun/tap device based on --down-pre flag */
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index ea7eb30..82eec7e 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -74,7 +74,7 @@ 
 bool do_route(const struct options *options, struct route_list *route_list,
               struct route_ipv6_list *route_ipv6_list, const struct tuntap *tt,
               const struct plugin_list *plugins, struct env_set *es,
-              openvpn_net_ctx_t *ctx);
+              openvpn_net_ctx_t *ctx, const bool is_multipoint);
 
 void close_instance(struct context *c);
 
diff --git a/src/openvpn/route.c b/src/openvpn/route.c
index bc8f561..2999cb6 100644
--- a/src/openvpn/route.c
+++ b/src/openvpn/route.c
@@ -927,7 +927,8 @@ 
            unsigned int flags,
            const struct route_gateway_info *rgi,
            const struct env_set *es,
-           openvpn_net_ctx_t *ctx)
+           openvpn_net_ctx_t *ctx,
+           const bool is_multipoint)
 {
     struct route_ipv4 r;
     CLEAR(r);
@@ -935,7 +936,7 @@ 
     r.network = network;
     r.netmask = netmask;
     r.gateway = gateway;
-    return add_route(&r, tt, flags, rgi, es, ctx);
+    return add_route(&r, tt, flags, rgi, es, ctx, is_multipoint);
 }
 
 static void
@@ -964,7 +965,8 @@ 
                   unsigned int flags,
                   const struct route_gateway_info *rgi,
                   const struct env_set *es,
-                  openvpn_net_ctx_t *ctx)
+                  openvpn_net_ctx_t *ctx,
+                  const bool is_multipoint)
 {
     int ret = true;
     for (int i = 0; i < rb->n_bypass; ++i)
@@ -972,7 +974,7 @@ 
         if (rb->bypass[i])
         {
             ret = add_route3(rb->bypass[i], IPV4_NETMASK_HOST, gateway, tt,
-                             flags | ROUTE_REF_GW, rgi, es, ctx) && ret;
+                             flags | ROUTE_REF_GW, rgi, es, ctx, is_multipoint) && ret;
         }
     }
     return ret;
@@ -1007,7 +1009,7 @@ 
 static bool
 redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt,
                               unsigned int flags, const struct env_set *es,
-                              openvpn_net_ctx_t *ctx)
+                              openvpn_net_ctx_t *ctx, const bool is_multipoint)
 {
     const char err[] = "NOTE: unable to redirect IPv4 default gateway --";
     bool ret = true;
@@ -1059,7 +1061,7 @@ 
                 {
                     ret = add_route3(rl->spec.remote_host, IPV4_NETMASK_HOST,
                                      rl->rgi.gateway.addr, tt, flags | ROUTE_REF_GW,
-                                     &rl->rgi, es, ctx);
+                                     &rl->rgi, es, ctx, is_multipoint);
                     rl->iflags |= RL_DID_LOCAL;
                 }
                 else
@@ -1071,7 +1073,7 @@ 
 
             /* route DHCP/DNS server traffic through original default gateway */
             ret = add_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags,
-                                    &rl->rgi, es, ctx) && ret;
+                                    &rl->rgi, es, ctx, is_multipoint) && ret;
 
             if (rl->flags & RG_REROUTE_GW)
             {
@@ -1079,11 +1081,11 @@ 
                 {
                     /* add new default route (1st component) */
                     ret = add_route3(0x00000000, 0x80000000, rl->spec.remote_endpoint,
-                                     tt, flags, &rl->rgi, es, ctx) && ret;
+                                     tt, flags, &rl->rgi, es, ctx, is_multipoint) && ret;
 
                     /* add new default route (2nd component) */
                     ret = add_route3(0x80000000, 0x80000000, rl->spec.remote_endpoint,
-                                     tt, flags, &rl->rgi, es, ctx) && ret;
+                                     tt, flags, &rl->rgi, es, ctx, is_multipoint) && ret;
                 }
                 else
                 {
@@ -1097,7 +1099,7 @@ 
 
                     /* add new default route */
                     ret = add_route3(0, 0, rl->spec.remote_endpoint, tt,
-                                     flags, &rl->rgi, es, ctx) && ret;
+                                     flags, &rl->rgi, es, ctx, is_multipoint) && ret;
                 }
             }
 
@@ -1112,7 +1114,8 @@ 
 undo_redirect_default_route_to_vpn(struct route_list *rl,
                                    const struct tuntap *tt, unsigned int flags,
                                    const struct env_set *es,
-                                   openvpn_net_ctx_t *ctx)
+                                   openvpn_net_ctx_t *ctx,
+                                   const bool is_multipoint)
 {
     if (rl && rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY)
     {
@@ -1173,7 +1176,7 @@ 
                 if (rl->rgi.flags & RGI_ADDR_DEFINED)
                 {
                     add_route3(0, 0, rl->rgi.gateway.addr, tt,
-                               flags | ROUTE_REF_GW, &rl->rgi, es, ctx);
+                               flags | ROUTE_REF_GW, &rl->rgi, es, ctx, is_multipoint);
                 }
             }
         }
@@ -1185,9 +1188,10 @@ 
 bool
 add_routes(struct route_list *rl, struct route_ipv6_list *rl6,
            const struct tuntap *tt, unsigned int flags,
-           const struct env_set *es, openvpn_net_ctx_t *ctx)
+           const struct env_set *es, openvpn_net_ctx_t *ctx,
+           const bool is_multipoint)
 {
-    bool ret = redirect_default_route_to_vpn(rl, tt, flags, es, ctx);
+    bool ret = redirect_default_route_to_vpn(rl, tt, flags, es, ctx, is_multipoint);
     if (rl && !(rl->iflags & RL_ROUTES_ADDED) )
     {
         struct route_ipv4 *r;
@@ -1220,7 +1224,7 @@ 
             {
                 delete_route(r, tt, flags, &rl->rgi, es, ctx);
             }
-            ret = add_route(r, tt, flags, &rl->rgi, es, ctx) && ret;
+            ret = add_route(r, tt, flags, &rl->rgi, es, ctx, is_multipoint) && ret;
         }
         rl->iflags |= RL_ROUTES_ADDED;
     }
@@ -1242,7 +1246,7 @@ 
             {
                 delete_route_ipv6(r, tt, flags, es, ctx);
             }
-            ret = add_route_ipv6(r, tt, flags, es, ctx) && ret;
+            ret = add_route_ipv6(r, tt, flags, es, ctx, is_multipoint) && ret;
         }
         rl6->iflags |= RL_ROUTES_ADDED;
     }
@@ -1253,7 +1257,8 @@ 
 void
 delete_routes(struct route_list *rl, struct route_ipv6_list *rl6,
               const struct tuntap *tt, unsigned int flags,
-              const struct env_set *es, openvpn_net_ctx_t *ctx)
+              const struct env_set *es, openvpn_net_ctx_t *ctx,
+              const bool is_multipoint)
 {
     if (rl && rl->iflags & RL_ROUTES_ADDED)
     {
@@ -1265,7 +1270,7 @@ 
         rl->iflags &= ~RL_ROUTES_ADDED;
     }
 
-    undo_redirect_default_route_to_vpn(rl, tt, flags, es, ctx);
+    undo_redirect_default_route_to_vpn(rl, tt, flags, es, ctx, is_multipoint);
 
     if (rl)
     {
@@ -1561,13 +1566,47 @@ 
 }
 #endif
 
+static bool
+is_gateway_needed_ipv4(const struct route_ipv4 *r4,
+                       const struct route_gateway_info *rgi,
+                       const struct tuntap *tt,
+                       const bool is_multipoint)
+{
+
+#ifndef _WIN32
+
+    if (rgi && (rgi->flags & RGI_IFACE_DEFINED) && rgi->iface[0] != 0)  /* vpn server special route */
+    {
+        if (rgi->flags & RGI_ADDR_DEFINED && r4->gateway != 0)
+        {
+            return true;
+        }
+    }
+
+#endif
+
+    if (tt->type == DEV_TYPE_TAP
+        && !( (r4->flags & RT_METRIC_DEFINED) && r4->metric == 0 ) )
+    {
+        return true;
+    }
+
+    if (is_multipoint && !tt->options.disable_dco)
+    {
+        return true;
+    }
+
+    return false;
+}
+
 bool
 add_route(struct route_ipv4 *r,
           const struct tuntap *tt,
           unsigned int flags,
           const struct route_gateway_info *rgi,  /* may be NULL */
           const struct env_set *es,
-          openvpn_net_ctx_t *ctx)
+          openvpn_net_ctx_t *ctx,
+          const bool is_multipoint)
 {
     int status = 0;
     int is_local_route;
@@ -1581,15 +1620,11 @@ 
     struct argv argv = argv_new();
     struct gc_arena gc = gc_new();
 
-#if defined(TARGET_LINUX)
-    const char *iface = tt->actual_name;
-    if (rgi && rgi->iface[0] != '\0')  /* vpn server special route */
+#ifndef _WIN32
+    const char *device = tt->actual_name;
+    if (rgi && (rgi->flags & RGI_IFACE_DEFINED))  /* vpn server special route */
     {
-        iface = rgi->iface;
-        if (r->gateway != 0)
-        {
-            gateway_needed = true;
-        }
+        device = rgi->iface;
     }
 #endif
 
@@ -1607,10 +1642,7 @@ 
         goto done;
     }
 
-    if (tt->type == DEV_TYPE_TAP && !(r->flags & RT_METRIC_DEFINED && r->metric == 0))
-    {
-        gateway_needed = true;
-    }
+    gateway_needed = is_gateway_needed_ipv4(r, rgi, tt, is_multipoint);
 
     if (gateway_needed && r->gateway == 0)
     {
@@ -1627,7 +1659,7 @@ 
 
     if (is_on_link(is_local_route, flags, rgi))
     {
-        iface = rgi->iface;
+        device = rgi->iface;
     }
 
     if (r->flags & RT_METRIC_DEFINED)
@@ -1638,7 +1670,7 @@ 
     status = RTA_SUCCESS;
     int ret = net_route_v4_add(ctx, &r->network, netmask_to_netbits2(r->netmask),
                                gateway_needed ? &r->gateway : NULL,
-                               iface, 0, metric);
+                               device, 0, metric);
     if (ret == -EEXIST)
     {
         msg(D_ROUTE, "NOTE: Linux route add command failed because route exists");
@@ -1655,7 +1687,7 @@ 
 
     if (rgi)
     {
-        snprintf(out, sizeof(out), "%s %s %s dev %s", network, netmask, gateway, rgi->iface);
+        snprintf(out, sizeof(out), "%s %s %s dev %s", network, netmask, gateway, device);
     }
     else
     {
@@ -1759,7 +1791,7 @@ 
                                     "ERROR: Solaris route add command failed");
     status = ret ? RTA_SUCCESS : RTA_ERROR;
 
-#elif defined(TARGET_FREEBSD)
+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
 
     argv_printf(&argv, "%s add",
                 ROUTE_PATH);
@@ -1771,40 +1803,23 @@ 
     }
 #endif
 
-    argv_printf_cat(&argv, "-net %s %s %s",
-                    network,
-                    gateway,
-                    netmask);
-
-    /* FIXME -- add on-link support for FreeBSD */
-
-    argv_msg(D_ROUTE, &argv);
-    bool ret = openvpn_execve_check(&argv, es, 0,
-                                    "ERROR: FreeBSD route add command failed");
-    status = ret ? RTA_SUCCESS : RTA_ERROR;
-
-#elif defined(TARGET_DRAGONFLY)
-
-    argv_printf(&argv, "%s add",
-                ROUTE_PATH);
-
-#if 0
-    if (r->flags & RT_METRIC_DEFINED)
+    if (gateway_needed)
     {
-        argv_printf_cat(&argv, "-rtt %d", r->metric);
+        argv_printf_cat(&argv, "-net %s %s %s",
+                        network,
+                        gateway,
+                        netmask);
     }
-#endif
-
-    argv_printf_cat(&argv, "-net %s %s %s",
-                    network,
-                    gateway,
-                    netmask);
-
-    /* FIXME -- add on-link support for Dragonfly */
+    else
+    {
+        argv_printf_cat(&argv, "-net %s -iface %s",
+                        network,
+                        device);
+    }
 
     argv_msg(D_ROUTE, &argv);
     bool ret = openvpn_execve_check(&argv, es, 0,
-                                    "ERROR: DragonFly route add command failed");
+                                    "ERROR: BSD route add command failed");
     status = ret ? RTA_SUCCESS : RTA_ERROR;
 
 #elif defined(TARGET_DARWIN)
@@ -1830,10 +1845,19 @@ 
     }
     else
     {
-        argv_printf_cat(&argv, "-net %s %s %s",
-                        network,
-                        gateway,
-                        netmask);
+        if (gateway_needed)
+        {
+            argv_printf_cat(&argv, "-net %s %s %s",
+                            network,
+                            gateway,
+                            netmask);
+        }
+        else
+        {
+            argv_printf_cat(&argv, "-net %s -interface %s",
+                            network,
+                            device);
+        }
     }
 
     argv_msg(D_ROUTE, &argv);
@@ -1853,11 +1877,19 @@ 
     }
 #endif
 
-    argv_printf_cat(&argv, "-net %s %s -netmask %s",
-                    network,
-                    gateway,
-                    netmask);
-
+    if (gateway_needed)
+    {
+        argv_printf_cat(&argv, "-net %s %s -netmask %s",
+                        network,
+                        gateway,
+                        netmask);
+    }
+    else
+    {
+        argv_printf_cat(&argv, "-net %s -interface %s",
+                        network,
+                        device);
+    }
     /* FIXME -- add on-link support for OpenBSD/NetBSD */
 
     argv_msg(D_ROUTE, &argv);
@@ -1923,10 +1955,98 @@ 
     }
 }
 
+static inline struct in6_addr
+netbits_to_netmask_ipv6(const int netbits)
+{
+    struct in6_addr netmask = {{{0}}};
+
+    if (netbits > 0 && netbits <= 128)
+    {
+        const int full_bytes = netbits / 8;
+        int i;
+        for (i = 0; i < full_bytes; i++)
+        {
+            netmask.s6_addr[i] = 0xFF;
+        }
+        if (netbits % 8)
+        {
+            netmask.s6_addr[i] = (0xFF << (8 - (netbits % 8)));
+        }
+    }
+
+    return netmask;
+}
+
+static bool
+is_gateway_in_vpn_subnet_ipv6(const struct in6_addr *gateway,
+                              const struct in6_addr *vpn_subnet,
+                              int netbits)
+{
+    struct in6_addr netmask = netbits_to_netmask_ipv6(netbits);
+
+    for (int i = 0; i < 16; i++)
+    {
+        if ((gateway->s6_addr[i] & netmask.s6_addr[i]) != (vpn_subnet->s6_addr[i] & netmask.s6_addr[i]))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool
+is_gateway_needed_ipv6(const struct route_ipv6 *r6,
+                       const struct tuntap *tt,
+                       const char *network,
+                       const bool is_multipoint)
+{
+
+#ifndef _WIN32
+    if (r6->iface != NULL)   /* VPN server special route */
+    {
+        if (!IN6_IS_ADDR_UNSPECIFIED(&r6->gateway))
+        {
+            return true;
+        }
+    }
+#endif
+
+    /*
+     * Filter out routes which are essentially no-ops
+     * (not currently done for IPv6)
+     */
+
+    /* On "tun" interface, we never set a gateway if the operating system
+     * can do "route to interface" - it does not add value, as the target
+     * dev already fully qualifies the route destination on point-to-point
+     * interfaces.   OTOH, on "tap" interface, we must always set the
+     * gateway unless the route is to be an on-link network
+     */
+    if (tt->type == DEV_TYPE_TAP
+        && !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) )
+    {
+        return true;
+    }
+
+    /* if is server and dco enabled, the gateway is needed*/
+    if (is_multipoint && !tt->options.disable_dco)
+    {
+        return true;
+    }
+
+    if (is_gateway_in_vpn_subnet_ipv6(&r6->gateway, &tt->local_ipv6, tt->netbits_ipv6))
+    {
+        msg(D_ROUTE, "Ignoring gateway in VPN subnet for route %s/%d", network, r6->netbits);
+        return false;
+    }
+
+    return false;
+}
+
 bool
 add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt,
                unsigned int flags, const struct env_set *es,
-               openvpn_net_ctx_t *ctx)
+               openvpn_net_ctx_t *ctx, const bool is_multipoint)
 {
     int status = 0;
     bool gateway_needed = false;
@@ -1941,13 +2061,9 @@ 
 
 #ifndef _WIN32
     const char *device = tt->actual_name;
-    if (r6->iface != NULL)              /* vpn server special route */
+    if (r6->iface != NULL)
     {
         device = r6->iface;
-        if (!IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) )
-        {
-            gateway_needed = true;
-        }
     }
 #endif
 
@@ -1955,6 +2071,8 @@ 
     const char *network = print_in6_addr( r6->network, 0, &gc);
     const char *gateway = print_in6_addr( r6->gateway, 0, &gc);
 
+    gateway_needed = is_gateway_needed_ipv6(r6, tt, network, is_multipoint);
+
 #if defined(TARGET_DARWIN)    \
     || defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)    \
     || defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
@@ -1982,23 +2100,6 @@ 
         r6->adapter_index ? r6->adapter_index : tt->adapter_index);
 #endif
 
-    /*
-     * Filter out routes which are essentially no-ops
-     * (not currently done for IPv6)
-     */
-
-    /* On "tun" interface, we never set a gateway if the operating system
-     * can do "route to interface" - it does not add value, as the target
-     * dev already fully qualifies the route destination on point-to-point
-     * interfaces.   OTOH, on "tap" interface, we must always set the
-     * gateway unless the route is to be an on-link network
-     */
-    if (tt->type == DEV_TYPE_TAP
-        && !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) )
-    {
-        gateway_needed = true;
-    }
-
     if (gateway_needed && IN6_IS_ADDR_UNSPECIFIED(&r6->gateway))
     {
         msg(M_WARN, "ROUTE6 WARNING: " PACKAGE_NAME " needs a gateway "
@@ -2011,6 +2112,7 @@ 
 
 #if defined(TARGET_LINUX)
     int metric = -1;
+
     if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0))
     {
         metric = r6->metric;
@@ -2121,9 +2223,18 @@ 
 
 #elif defined(TARGET_OPENBSD)
 
-    argv_printf(&argv, "%s add -inet6 %s -prefixlen %d %s",
+    argv_printf(&argv, "%s add -inet6 %s -prefixlen %d ",
                 ROUTE_PATH,
-                network, r6->netbits, gateway );
+                network, r6->netbits);
+
+    if (gateway_needed)
+    {
+        argv_printf_cat(&argv, "%s", gateway);
+    }
+    else
+    {
+        argv_printf_cat(&argv, "-iface %s", device)
+    }
 
     argv_msg(D_ROUTE, &argv);
     bool ret = openvpn_execve_check(&argv, es, 0,
@@ -2132,9 +2243,18 @@ 
 
 #elif defined(TARGET_NETBSD)
 
-    argv_printf(&argv, "%s add -inet6 %s/%d %s",
+    argv_printf(&argv, "%s add -inet6 %s/%d ",
                 ROUTE_PATH,
-                network, r6->netbits, gateway );
+                network, r6->netbits);
+
+    if (gateway_needed)
+    {
+        argv_printf_cat(&argv, "%s", gateway);
+    }
+    else
+    {
+        argv_printf_cat(&argv, "-iface %s", device)
+    }
 
     argv_msg(D_ROUTE, &argv);
     bool ret = openvpn_execve_check(&argv, es, 0,
diff --git a/src/openvpn/route.h b/src/openvpn/route.h
index 421e7d2..ca2cbcd 100644
--- a/src/openvpn/route.h
+++ b/src/openvpn/route.h
@@ -271,13 +271,13 @@ 
 
 void route_ipv6_clear_host_bits( struct route_ipv6 *r6 );
 
-bool add_route_ipv6(struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es, openvpn_net_ctx_t *ctx);
+bool add_route_ipv6(struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es, openvpn_net_ctx_t *ctx, const bool is_multipoint);
 
 void delete_route_ipv6(const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es, openvpn_net_ctx_t *ctx);
 
 bool add_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags,
                const struct route_gateway_info *rgi, const struct env_set *es,
-               openvpn_net_ctx_t *ctx);
+               openvpn_net_ctx_t *ctx, const bool is_multipoint);
 
 void add_route_to_option_list(struct route_option_list *l,
                               const char *network,
@@ -312,14 +312,16 @@ 
 
 bool add_routes(struct route_list *rl, struct route_ipv6_list *rl6,
                 const struct tuntap *tt, unsigned int flags,
-                const struct env_set *es, openvpn_net_ctx_t *ctx);
+                const struct env_set *es, openvpn_net_ctx_t *ctx,
+                const bool is_multipoint);
 
 void delete_routes(struct route_list *rl,
                    struct route_ipv6_list *rl6,
                    const struct tuntap *tt,
                    unsigned int flags,
                    const struct env_set *es,
-                   openvpn_net_ctx_t *ctx);
+                   openvpn_net_ctx_t *ctx,
+                   const bool is_multipoint);
 
 void setenv_routes(struct env_set *es, const struct route_list *rl);
 
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index ce3d882..3082d89 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1020,7 +1020,7 @@ 
  */
 static void
 add_route_connected_v6_net(struct tuntap *tt,
-                           const struct env_set *es)
+                           const struct env_set *es, const bool is_multipoint)
 {
     struct route_ipv6 r6;
 
@@ -1030,7 +1030,7 @@ 
     r6.gateway = tt->local_ipv6;
     r6.metric  = 0;                     /* connected route */
     r6.flags   = RT_DEFINED | RT_METRIC_DEFINED;
-    add_route_ipv6(&r6, tt, 0, es, NULL);
+    add_route_ipv6(&r6, tt, 0, es, NULL, is_multipoint);
 }
 
 void
@@ -1087,7 +1087,8 @@ 
  */
 static void
 do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu,
-                 const struct env_set *es, openvpn_net_ctx_t *ctx)
+                 const struct env_set *es, openvpn_net_ctx_t *ctx,
+                 const bool is_multipoint)
 {
 #if !defined(TARGET_LINUX)
     struct argv argv = argv_new();
@@ -1205,7 +1206,7 @@ 
 #if defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) \
     || defined(TARGET_DARWIN)
     /* and, hooray, we explicitly need to add a route... */
-    add_route_connected_v6_net(tt, es);
+    add_route_connected_v6_net(tt, es, is_multipoint);
 #endif
 #elif defined(TARGET_AIX)
     argv_printf(&argv, "%s %s inet6 %s/%d mtu %d up", IFCONFIG_PATH, ifname,
@@ -1231,7 +1232,7 @@ 
         do_address_service(true, AF_INET6, tt);
         if (tt->type == DEV_TYPE_TUN)
         {
-            add_route_connected_v6_net(tt, es);
+            add_route_connected_v6_net(tt, es, is_multipoint);
         }
         do_dns_service(true, AF_INET6, tt);
         do_set_mtu_service(tt, AF_INET6, tun_mtu);
@@ -1259,7 +1260,7 @@ 
         netsh_command(&argv, 4, M_FATAL);
         if (tt->type == DEV_TYPE_TUN)
         {
-            add_route_connected_v6_net(tt, es);
+            add_route_connected_v6_net(tt, es, is_multipoint);
         }
         /* set ipv6 dns servers if any are specified */
         netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, tt->adapter_index);
@@ -1291,7 +1292,7 @@ 
  */
 static void
 do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu,
-                 const struct env_set *es, openvpn_net_ctx_t *ctx)
+                 const struct env_set *es, openvpn_net_ctx_t *ctx, const bool is_multipoint)
 {
 #if !defined(_WIN32) && !defined(TARGET_ANDROID)
     /*
@@ -1415,7 +1416,7 @@ 
         r.netmask = tt->remote_netmask;
         r.gateway = tt->local;
         r.metric = 0;
-        add_route(&r, tt, 0, NULL, es, NULL);
+        add_route(&r, tt, 0, NULL, es, NULL, is_multipoint);
     }
 
 #elif defined(TARGET_OPENBSD)
@@ -1462,7 +1463,7 @@ 
         r.network = tt->local & tt->remote_netmask;
         r.netmask = tt->remote_netmask;
         r.gateway = remote_end;
-        add_route(&r, tt, 0, NULL, es, NULL);
+        add_route(&r, tt, 0, NULL, es, NULL, is_multipoint);
     }
 
 #elif defined(TARGET_NETBSD)
@@ -1504,7 +1505,7 @@ 
         r.network = tt->local & tt->remote_netmask;
         r.netmask = tt->remote_netmask;
         r.gateway = remote_end;
-        add_route(&r, tt, 0, NULL, es, NULL);
+        add_route(&r, tt, 0, NULL, es, NULL, is_multipoint);
     }
 
 #elif defined(TARGET_DARWIN)
@@ -1554,7 +1555,7 @@ 
         r.network = tt->local & tt->remote_netmask;
         r.netmask = tt->remote_netmask;
         r.gateway = tt->local;
-        add_route(&r, tt, 0, NULL, es, NULL);
+        add_route(&r, tt, 0, NULL, es, NULL, is_multipoint);
     }
 
 #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
@@ -1648,7 +1649,7 @@ 
 /* execute the ifconfig command through the shell */
 void
 do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
-            const struct env_set *es, openvpn_net_ctx_t *ctx)
+            const struct env_set *es, openvpn_net_ctx_t *ctx, const bool is_multipoint)
 {
     msg(D_LOW, "do_ifconfig, ipv4=%d, ipv6=%d", tt->did_ifconfig_setup,
         tt->did_ifconfig_ipv6_setup);
@@ -1668,12 +1669,12 @@ 
 
     if (tt->did_ifconfig_setup)
     {
-        do_ifconfig_ipv4(tt, ifname, tun_mtu, es, ctx);
+        do_ifconfig_ipv4(tt, ifname, tun_mtu, es, ctx, is_multipoint);
     }
 
     if (tt->did_ifconfig_ipv6_setup)
     {
-        do_ifconfig_ipv6(tt, ifname, tun_mtu, es, ctx);
+        do_ifconfig_ipv6(tt, ifname, tun_mtu, es, ctx, is_multipoint);
     }
 
     /* release resources potentially allocated during interface setup */
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 33b9552..f1ad9cf 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -319,7 +319,7 @@ 
  * @param ctx       the networking API opaque context
  */
 void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
-                 const struct env_set *es, openvpn_net_ctx_t *ctx);
+                 const struct env_set *es, openvpn_net_ctx_t *ctx, const bool is_multipoint);
 
 /**
  * undo_ifconfig - undo configuration of the tunnel interface
@@ -754,6 +754,7 @@ 
 }
 
 const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
+
 bool tun_name_is_fixed(const char *dev);
 
 static inline bool