@@ -314,6 +314,10 @@
3. Use ``--ifconfig-pool`` allocation for dynamic IP (last
choice).
+ When DCO is enabled and the IP is not in contained in the network specified
+ by ``--ifconfig``, OpenVPN will install a /32 host route for the ``local``
+ IP address.
+
--ifconfig-ipv6-push args
for ``--client-config-dir`` per-client static IPv6 interface
configuration, see ``--client-config-dir`` and ``--ifconfig-push`` for
@@ -324,6 +328,10 @@
ifconfig-ipv6-push ipv6addr/bits ipv6remote
+ When DCO is enabled and the IP is not in contained in the network specified
+ by ``--ifconfig-ipv6``, OpenVPN will install a /128 host route for the
+ ``ipv6addr`` IP address.
+
--multihome
Configure a multi-homed UDP server. This option needs to be used when a
server has more than one IP address (e.g. multiple interfaces, or
@@ -664,6 +664,14 @@
return;
}
+#if defined(_WIN32)
+ if (addr->type & MR_ONLINK_DCO_ADDR)
+ {
+ /* Windows does not need these extra routes, so we ignore/skip them */
+ return;
+ }
+#endif
+
struct context *c = &mi->context;
if (addrtype == MR_ADDR_IPV6)
{
@@ -671,8 +679,14 @@
dco_win_add_iroute_ipv6(&c->c1.tuntap->dco, addr->v6.addr, addr->netbits,
c->c2.tls_multi->peer_id);
#else
+ const struct in6_addr *gateway = &mi->context.c2.push_ifconfig_ipv6_local;
+ if (addr->type & MR_ONLINK_DCO_ADDR)
+ {
+ gateway = NULL;
+ }
+
net_route_v6_add(&m->top.net_ctx, &addr->v6.addr, addr->netbits,
- &mi->context.c2.push_ifconfig_ipv6_local, c->c1.tuntap->actual_name, 0,
+ gateway, c->c1.tuntap->actual_name, 0,
DCO_IROUTE_METRIC);
#endif
}
@@ -683,7 +697,13 @@
c->c2.tls_multi->peer_id);
#else
in_addr_t dest = htonl(addr->v4.addr);
- net_route_v4_add(&m->top.net_ctx, &dest, addr->netbits, &mi->context.c2.push_ifconfig_local,
+ const in_addr_t *gateway = &mi->context.c2.push_ifconfig_local;
+ if (addr->type & MR_ONLINK_DCO_ADDR)
+ {
+ gateway = NULL;
+ }
+
+ net_route_v4_add(&m->top.net_ctx, &dest, addr->netbits, gateway,
c->c1.tuntap->actual_name, 0, DCO_IROUTE_METRIC);
#endif
}
@@ -714,6 +734,20 @@
DCO_IROUTE_METRIC);
#endif
}
+
+#if !defined(_WIN32)
+ /* Check if we added a host route as the assigned client IP address was
+ * not in the on link scope defined by --ifconfig */
+ in_addr_t ifconfig_local = mi->context.c2.push_ifconfig_local;
+
+ if (multi_check_push_ifconfig_extra_route(mi, htonl(ifconfig_local)))
+ {
+ /* On windows we do not install these routes, so we also do not need to delete them */
+ net_route_v4_del(&m->top.net_ctx, &ifconfig_local,
+ 32, NULL, c->c1.tuntap->actual_name, 0,
+ DCO_IROUTE_METRIC);
+ }
+#endif
}
if (mi->context.c2.push_ifconfig_ipv6_defined)
@@ -728,6 +762,18 @@
DCO_IROUTE_METRIC);
#endif
}
+
+ /* Checked if we added a host route as the assigned client IP address was
+ * outside the --ifconfig-ipv6 tun interface config */
+#if !defined(_WIN32)
+ struct in6_addr *dest = &mi->context.c2.push_ifconfig_ipv6_local;
+ if (multi_check_push_ifconfig_ipv6_extra_route(mi, dest))
+ {
+ /* On windows we do not install these routes, so we also do not need to delete them */
+ net_route_v6_del(&m->top.net_ctx, dest, 128, NULL,
+ c->c1.tuntap->actual_name, 0, DCO_IROUTE_METRIC);
+ }
+#endif
}
#endif /* if defined(TARGET_LINUX) || defined(TARGET_FREEBSD) || defined(_WIN32) */
}
@@ -20,6 +20,7 @@
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
+
#ifndef MROUTE_H
#define MROUTE_H
@@ -74,6 +75,9 @@
/* Address type mask indicating that proto # is part of address */
#define MR_WITH_PROTO 32
+/* MRoute is an on link/scope address needed for DCO on Unix platforms */
+#define MR_ONLINK_DCO_ADDR 64
+
struct mroute_addr
{
uint8_t len; /* length of address */
@@ -42,6 +42,7 @@
#include "ssl_ncp.h"
#include "vlan.h"
#include "auth_token.h"
+#include "route.h"
#include <inttypes.h>
#include <string.h>
@@ -1231,11 +1232,18 @@
management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
}
#endif
- if (!primary)
+ if (primary && multi_check_push_ifconfig_extra_route(mi, addr.v4.addr))
{
- /* "primary" is the VPN ifconfig address of the peer and already
- * known to DCO, so only install "extra" iroutes (primary = false)
- */
+ /* "primary" is the VPN ifconfig address of the peer */
+ /* if it does not fall into the network defined by ifconfig_local
+ * we install this as extra onscope address on the interface */
+ addr.netbits = 32;
+ addr.type |= MR_ONLINK_DCO_ADDR;
+
+ dco_install_iroute(m, mi, &addr);
+ }
+ else if (!primary)
+ {
ASSERT(netbits >= 0); /* DCO requires populated netbits */
dco_install_iroute(m, mi, &addr);
}
@@ -1269,7 +1277,17 @@
management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
}
#endif
- if (!primary)
+ if (primary && multi_check_push_ifconfig_ipv6_extra_route(mi, &addr.v6.addr))
+ {
+ /* "primary" is the VPN ifconfig address of the peer */
+ /* if it does not fall into the network defined by ifconfig_local
+ * we install this as extra onscope address on the interface */
+ addr.netbits = 128;
+ addr.type |= MR_ONLINK_DCO_ADDR;
+
+ dco_install_iroute(m, mi, &addr);
+ }
+ else if (!primary)
{
/* "primary" is the VPN ifconfig address of the peer and already
* known to DCO, so only install "extra" iroutes (primary = false)
@@ -4391,3 +4409,49 @@
}
}
}
+
+bool
+multi_check_push_ifconfig_extra_route(struct multi_instance *mi, in_addr_t dest)
+{
+ struct options *o = &mi->context.options;
+ in_addr_t local_addr, local_netmask;
+
+ if (!o->ifconfig_local || !o->ifconfig_remote_netmask)
+ {
+ /* If we do not have a local address, we just return false as
+ * this check doesn't make sense. */
+ return false;
+ }
+
+ /* if it falls into the network defined by ifconfig_local we assume
+ * it is already known to DCO and only install "extra" iroutes */
+ inet_pton(AF_INET, o->ifconfig_local, &local_addr);
+ inet_pton(AF_INET, o->ifconfig_remote_netmask, &local_netmask);
+
+ return (local_addr & local_netmask) != (dest & local_netmask);
+}
+
+bool
+multi_check_push_ifconfig_ipv6_extra_route(struct multi_instance *mi,
+ struct in6_addr *dest)
+{
+ struct options *o = &mi->context.options;
+
+ if (!o->ifconfig_ipv6_local || !o->ifconfig_ipv6_netbits)
+ {
+ /* If we do not have a local address, we just return false as
+ * this check doesn't make sense. */
+ return false;
+ }
+
+ /* if it falls into the network defined by ifconfig_local we assume
+ * it is already known to DCO and only install "extra" iroutes */
+ struct in6_addr ifconfig_local;
+ if (inet_pton(AF_INET6, o->ifconfig_ipv6_local, &ifconfig_local) != 1)
+ {
+ return false;
+ }
+
+ return (!ipv6_net_contains_host(&ifconfig_local, o->ifconfig_ipv6_netbits,
+ dest));
+}
\ No newline at end of file
@@ -665,6 +665,33 @@
return ret;
}
+/**
+ * Determines if the ifconfig_push_local address falls into the range of the local
+ * IP addresses of the VPN interface (ifconfig_local with ifconfig_remote_netmask)
+ *
+ * @param mi The multi-instance to check this condition for
+ * @param dest The destination IP address to check
+ *
+ * @return Returns true if ifconfig_push is outside that range and requires an extra
+ * route to be installed.
+ */
+bool
+multi_check_push_ifconfig_extra_route(struct multi_instance *mi, in_addr_t dest);
+
+/**
+ * Determines if the ifconfig_ipv6_local address falls into the range of the local
+ * IP addresses of the VPN interface (ifconfig_local with ifconfig_remote_netmask)
+ *
+ * @param mi The multi-instance to check this condition for
+ * @param dest The destination IPv6 address to check
+ *
+ * @return Returns true if ifconfig_push is outside that range and requires an extra
+ * route to be installed.
+ */
+bool
+multi_check_push_ifconfig_ipv6_extra_route(struct multi_instance *mi,
+ struct in6_addr *dest);
+
/*
* Check for signals.
*/