Attention is currently required from: plaisthos.

Hello plaisthos,

I'd like you to reexamine a change. Please visit

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

to look at the new patch set (#3).


Change subject: PUSH_UPDATE: add DCO and iroutes support on server
......................................................................

PUSH_UPDATE: add DCO and iroutes support on server

Renamed update_vhash() to update_vaddr().
Moved the -ifconfig logic from send_single_push_update() to update_vaddr().
Changed canary logic to use the current value of ifconfig (if present) as
canary. Added logic in the update_vaddr() to not unlearn and re-learn the same
address.

Added logic to update the peer's virtual address(es) in the DCO to the current
value(s) after sending a PUSH_UPDATE message on Linux.
Added dco_multi_update_peer_addr() and dco_update_peer_addr() to do so.

Added logic to update iroutes after changing the peer's virtual address(es).
Divided dco_delete_iroutes() in _v4() and _v6() to better updating iroutes.

Change-Id: I88f37e47026a7d7ea0f7c6bde1d79d32c14e5297
Signed-off-by: Marco Baffo <[email protected]>
---
M src/openvpn/dco.c
M src/openvpn/dco.h
M src/openvpn/dco_freebsd.c
M src/openvpn/dco_linux.c
M src/openvpn/dco_win.c
M src/openvpn/multi.c
M src/openvpn/multi.h
M src/openvpn/push_util.c
M tests/unit_tests/openvpn/test_push_update_msg.c
9 files changed, 377 insertions(+), 142 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/50/1450/3

diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 6a1a5c9..eccfac2 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -658,6 +658,41 @@
     return 0;
 }

+int
+dco_multi_update_peer_addr(struct multi_context *m, struct multi_instance *mi, 
int flags)
+{
+    if (!flags)
+    {
+        return 0;
+    }
+
+    struct context *c = &mi->context;
+    int peer_id = c->c2.tls_multi->dco_peer_id;
+
+    /* In server mode we need to fetch the remote addresses from the push 
config */
+    struct in_addr vpn_ip4 = { 0 };
+    struct in_addr *vpn_addr4 = NULL;
+    if (c->c2.push_ifconfig_defined)
+    {
+        vpn_ip4.s_addr = htonl(c->c2.push_ifconfig_local);
+        vpn_addr4 = &vpn_ip4;
+    }
+
+    struct in6_addr *vpn_addr6 = NULL;
+    if (c->c2.push_ifconfig_ipv6_defined)
+    {
+        vpn_addr6 = &c->c2.push_ifconfig_ipv6_local;
+    }
+
+    int ret = dco_update_peer_addr(&c->c1.tuntap->dco, peer_id, vpn_addr4, 
vpn_addr6, flags);
+    if (ret < 0)
+    {
+        return ret;
+    }
+
+    return 0;
+}
+
 void
 dco_install_iroute(struct multi_context *m, struct multi_instance *mi, struct 
mroute_addr *addr)
 {
@@ -723,7 +758,7 @@
 }

 void
-dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+dco_delete_iroutes_v4(struct multi_context *m, struct multi_instance *mi)
 {
 #if defined(TARGET_LINUX) || defined(TARGET_FREEBSD) || defined(_WIN32)
     if (!dco_enabled(&m->top.options))
@@ -734,7 +769,7 @@

     struct context *c = &mi->context;

-    if (mi->context.c2.push_ifconfig_defined)
+    if (c->c2.push_ifconfig_defined)
     {
         for (const struct iroute *ir = c->options.iroutes; ir; ir = ir->next)
         {
@@ -742,7 +777,7 @@
             dco_win_del_iroute_ipv4(&c->c1.tuntap->dco, htonl(ir->network), 
ir->netbits);
 #else
             net_route_v4_del(&m->top.net_ctx, &ir->network, ir->netbits,
-                             &mi->context.c2.push_ifconfig_local, 
c->c1.tuntap->actual_name, 0,
+                             &c->c2.push_ifconfig_local, 
c->c1.tuntap->actual_name, 0,
                              DCO_IROUTE_METRIC);
 #endif
         }
@@ -750,7 +785,7 @@
 #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;
+        in_addr_t ifconfig_local = c->c2.push_ifconfig_local;

         if (multi_check_push_ifconfig_extra_route(mi, htonl(ifconfig_local)))
         {
@@ -761,8 +796,22 @@
         }
 #endif
     }
+#endif /* if defined(TARGET_LINUX) || defined(TARGET_FREEBSD) || 
defined(_WIN32) */
+}

-    if (mi->context.c2.push_ifconfig_ipv6_defined)
+void
+dco_delete_iroutes_v6(struct multi_context *m, struct multi_instance *mi)
+{
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD) || defined(_WIN32)
+    if (!dco_enabled(&m->top.options))
+    {
+        return;
+    }
+    ASSERT(TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN);
+
+    struct context *c = &mi->context;
+
+    if (c->c2.push_ifconfig_ipv6_defined)
     {
         for (const struct iroute_ipv6 *ir6 = c->options.iroutes_ipv6; ir6; ir6 
= ir6->next)
         {
@@ -770,7 +819,7 @@
             dco_win_del_iroute_ipv6(&c->c1.tuntap->dco, ir6->network, 
ir6->netbits);
 #else
             net_route_v6_del(&m->top.net_ctx, &ir6->network, ir6->netbits,
-                             &mi->context.c2.push_ifconfig_ipv6_local, 
c->c1.tuntap->actual_name, 0,
+                             &c->c2.push_ifconfig_ipv6_local, 
c->c1.tuntap->actual_name, 0,
                              DCO_IROUTE_METRIC);
 #endif
         }
@@ -778,7 +827,7 @@
         /* 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;
+        struct in6_addr *dest = &c->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 */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index cd6e32a..3bc28a8 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -191,6 +191,30 @@
                  int keepalive_timeout, int mss);

 /**
+ * Update the peer's virtual address(es) in the DCO to the current value(s)
+ *
+ * @param dco       DCO device contect
+ * @param peerid    the ID of the peer to be modified
+ * @param vpn_ipv4  new IPv4 or NULL to remove the outdated one, only used 
when `(1 << AF_INET)` flag is present
+ * @param vpn_ipv6  new IPv6 or NULL to remove the outdated one, only used 
when `(1 << AF_INET6)` flag is present
+ * @param flags     `(1 << AF_INET)`, `(1 << AF_INET6)` or `(1 << AF_INET) | 
(1 << AF_INET6)` depending on wich address has changed
+ * @return int      the DCO message status on success or a negative error code 
otherwise
+ */
+int
+dco_update_peer_addr(dco_context_t *dco, unsigned int peerid, struct in_addr 
*vpn_ipv4, struct in6_addr *vpn_ipv6, int flags);
+
+/**
+ * Update the peer's virtual address(es) in the DCO to the current value(s)
+ *
+ * @param m         server multi context
+ * @param mi        peer multi instnace
+ * @param flags     `(1 << AF_INET)`, `(1 << AF_INET6)` or `(1 << AF_INET) | 
(1 << AF_INET6)` depending on wich address has changed
+ * @return int      0 on success or a negative error code otherwise
+ */
+int
+dco_multi_update_peer_addr(struct multi_context *m, struct multi_instance *mi, 
int flags);
+
+/**
  * Remove a peer from DCO
  *
  * @param c         the main instance context of the peer to remove
@@ -218,12 +242,20 @@
                         struct mroute_addr *addr);

 /**
- * Remove all routes added through the specified client
+ * Remove all IPv4 routes added through the specified client
  *
  * @param m         the server context
  * @param mi        the client instance for which routes have to be removed
  */
-void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+void dco_delete_iroutes_v4(struct multi_context *m, struct multi_instance *mi);
+
+/**
+ * Remove all IPv6 routes added through the specified client
+ *
+ * @param m         the server context
+ * @param mi        the client instance for which routes have to be removed
+ */
+void dco_delete_iroutes_v6(struct multi_context *m, struct multi_instance *mi);

 /**
  * Update traffic statistics for all peers
@@ -361,7 +393,12 @@
 }

 static inline void
-dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+dco_delete_iroutes_v4(struct multi_context *m, struct multi_instance *mi)
+{
+}
+
+static inline void
+dco_delete_iroutes_v6(struct multi_context *m, struct multi_instance *mi)
 {
 }

diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c
index d1ad092..af20ab8 100644
--- a/src/openvpn/dco_freebsd.c
+++ b/src/openvpn/dco_freebsd.c
@@ -559,6 +559,12 @@
     return ret;
 }

+int
+dco_update_peer_addr(dco_context_t *dco, unsigned int peerid, struct in_addr 
*vpn_ipv4, struct in6_addr *vpn_ipv6, int flags)
+{
+    return 0;
+}
+
 static void
 dco_update_peer_stat(struct multi_context *m, uint32_t peerid, const nvlist_t 
*nvl)
 {
diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c
index 2664029..eb9e4f9 100644
--- a/src/openvpn/dco_linux.c
+++ b/src/openvpn/dco_linux.c
@@ -684,6 +684,50 @@
     return ret;
 }

+int
+dco_update_peer_addr(dco_context_t *dco, unsigned int peerid, struct in_addr 
*vpn_ipv4, struct in6_addr *vpn_ipv6, int flags)
+{
+    msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
+
+    if (!flags)
+    {
+        return 0;
+    }
+
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_PEER_SET);
+    if (!nl_msg)
+    {
+        return -ENOMEM;
+    }
+
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_A_PEER);
+    int ret = -EMSGSIZE;
+    NLA_PUT_U32(nl_msg, OVPN_A_PEER_ID, peerid);
+
+    if (flags & (1 << AF_INET))
+    {
+        NLA_PUT_U32(nl_msg, OVPN_A_PEER_VPN_IPV4, vpn_ipv4 ? vpn_ipv4->s_addr 
: 0);
+    }
+
+    if (flags & (1 << AF_INET6))
+    {
+        struct in6_addr addr6;
+        if (!vpn_ipv6)
+        {
+            CLEAR(addr6);
+            vpn_ipv6 = &addr6;
+        }
+        NLA_PUT(nl_msg, OVPN_A_PEER_VPN_IPV6, sizeof(struct in6_addr), 
vpn_ipv6);
+    }
+
+    nla_nest_end(nl_msg, attr);
+    ret = ovpn_nl_msg_send(dco, nl_msg, __func__);
+
+nla_put_failure:
+    nlmsg_free(nl_msg);
+    return ret;
+}
+
 /* This function parses the reply provided by the kernel to the 
CTRL_CMD_GETFAMILY
  * message. We parse the reply and we retrieve the multicast group ID 
associated
  * with the "ovpn-dco" netlink family.
diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c
index 94f043f..1a11927 100644
--- a/src/openvpn/dco_win.c
+++ b/src/openvpn/dco_win.c
@@ -526,6 +526,12 @@
 }

 int
+dco_update_peer_addr(dco_context_t *dco, unsigned int peerid, struct in_addr 
*vpn_ipv4, struct in6_addr *vpn_ipv6, int flags)
+{
+    return 0;
+}
+
+int
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t 
slot,
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const 
uint8_t *decrypt_key,
             const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 329d0a3..878871f 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -476,6 +476,40 @@
     set_prefix(mi);
 }

+static void
+multi_del_iroutes_v4(struct multi_context *m, struct multi_instance *mi)
+{
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) != DEV_TYPE_TUN)
+    {
+        return;
+    }
+
+    const struct iroute *ir;
+
+    dco_delete_iroutes_v4(m, mi);
+    for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+    {
+        mroute_helper_del_iroute46(m->route_helper, ir->netbits);
+    }
+}
+
+static void
+multi_del_iroutes_v6(struct multi_context *m, struct multi_instance *mi)
+{
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) != DEV_TYPE_TUN)
+    {
+        return;
+    }
+
+    const struct iroute_ipv6 *ir6;
+
+    dco_delete_iroutes_v6(m, mi);
+    for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next)
+    {
+        mroute_helper_del_iroute46(m->route_helper, ir6->netbits);
+    }
+}
+
 /*
  * Tell the route helper about deleted iroutes so
  * that it can update its mask of currently used
@@ -484,23 +518,8 @@
 static void
 multi_del_iroutes(struct multi_context *m, struct multi_instance *mi)
 {
-    const struct iroute *ir;
-    const struct iroute_ipv6 *ir6;
-
-    dco_delete_iroutes(m, mi);
-
-    if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
-    {
-        for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
-        {
-            mroute_helper_del_iroute46(m->route_helper, ir->netbits);
-        }
-
-        for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = 
ir6->next)
-        {
-            mroute_helper_del_iroute46(m->route_helper, ir6->netbits);
-        }
-    }
+    multi_del_iroutes_v4(m, mi);
+    multi_del_iroutes_v6(m, mi);
 }

 static void
@@ -1282,6 +1301,64 @@
     return owner;
 }

+static void
+multi_add_iroutes_v4(struct multi_context *m, struct multi_instance *mi)
+{
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) != DEV_TYPE_TUN)
+    {
+        return;
+    }
+
+    struct gc_arena gc = gc_new();
+    const struct iroute *ir;
+
+    mi->did_iroutes = true;
+    for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+    {
+        if (ir->netbits >= 0)
+        {
+            msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
+                print_in_addr_t(ir->network, 0, &gc), ir->netbits,
+                multi_instance_string(mi, false, &gc));
+        }
+        else
+        {
+            msg(D_MULTI_LOW, "MULTI: internal route %s -> %s",
+                print_in_addr_t(ir->network, 0, &gc), 
multi_instance_string(mi, false, &gc));
+        }
+
+        mroute_helper_add_iroute46(m->route_helper, ir->netbits);
+
+        multi_learn_in_addr_t(m, mi, ir->network, ir->netbits, false);
+    }
+    gc_free(&gc);
+}
+
+static void
+multi_add_iroutes_v6(struct multi_context *m, struct multi_instance *mi)
+{
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) != DEV_TYPE_TUN)
+    {
+        return;
+    }
+
+    struct gc_arena gc = gc_new();
+    const struct iroute_ipv6 *ir6;
+
+    mi->did_iroutes = true;
+    for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next)
+    {
+        msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
+            print_in6_addr(ir6->network, 0, &gc), ir6->netbits,
+            multi_instance_string(mi, false, &gc));
+
+        mroute_helper_add_iroute46(m->route_helper, ir6->netbits);
+
+        multi_learn_in6_addr(m, mi, ir6->network, ir6->netbits, false);
+    }
+    gc_free(&gc);
+}
+
 /*
  * A new client has connected, add routes (server -> client)
  * to internal routing table.
@@ -1289,42 +1366,8 @@
 static void
 multi_add_iroutes(struct multi_context *m, struct multi_instance *mi)
 {
-    struct gc_arena gc = gc_new();
-    const struct iroute *ir;
-    const struct iroute_ipv6 *ir6;
-    if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
-    {
-        mi->did_iroutes = true;
-        for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
-        {
-            if (ir->netbits >= 0)
-            {
-                msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
-                    print_in_addr_t(ir->network, 0, &gc), ir->netbits,
-                    multi_instance_string(mi, false, &gc));
-            }
-            else
-            {
-                msg(D_MULTI_LOW, "MULTI: internal route %s -> %s",
-                    print_in_addr_t(ir->network, 0, &gc), 
multi_instance_string(mi, false, &gc));
-            }
-
-            mroute_helper_add_iroute46(m->route_helper, ir->netbits);
-
-            multi_learn_in_addr_t(m, mi, ir->network, ir->netbits, false);
-        }
-        for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = 
ir6->next)
-        {
-            msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
-                print_in6_addr(ir6->network, 0, &gc), ir6->netbits,
-                multi_instance_string(mi, false, &gc));
-
-            mroute_helper_add_iroute46(m->route_helper, ir6->netbits);
-
-            multi_learn_in6_addr(m, mi, ir6->network, ir6->netbits, false);
-        }
-    }
-    gc_free(&gc);
+    multi_add_iroutes_v4(m, mi);
+    multi_add_iroutes_v6(m, mi);
 }

 /*
@@ -4327,9 +4370,10 @@
 }

 /* Function to unlearn previous ifconfig of a client in the server 
multi_context after a PUSH_UPDATE */
-void
+static void
 unlearn_ifconfig(struct multi_context *m, struct multi_instance *mi)
 {
+    multi_del_iroutes_v4(m, mi);
     in_addr_t old_addr = 0;
     old_addr = htonl(mi->context.c2.push_ifconfig_local);
     multi_unlearn_in_addr_t(m, mi, old_addr);
@@ -4339,9 +4383,10 @@
 }

 /* Function to unlearn previous ifconfig-ipv6 of a client in the server 
multi_context after a PUSH_UPDATE */
-void
+static void
 unlearn_ifconfig_ipv6(struct multi_context *m, struct multi_instance *mi)
 {
+    multi_del_iroutes_v6(m, mi);
     struct in6_addr old_addr6;
     CLEAR(old_addr6);
     old_addr6 = mi->context.c2.push_ifconfig_ipv6_local;
@@ -4352,59 +4397,118 @@
 }

 /**
- * Update the vhash with new IP/IPv6 addresses in the multi_context when a
- * push-update message containing ifconfig/ifconfig-ipv6 options is sent
- * from the server.
+ * When a push-update message containing ifconfig/ifconfig-ipv6 options is sent
+ * from the server, update the vhash with new IP/IPv6 addresses in the
+ * multi_context and update the virtual peer addresses in the DCO.
+ * Also remove the iroutes and readd it with the new IP if needed.
  *
  * @param m         The multi_context
  * @param mi        The multi_instance of the client we are updating
- * @param new_ip    The new IPv4 address or NULL if no change
- * @param new_ipv6  The new IPv6 address or NULL if no change
+ * @param new_ip    The new IPv4 address, NULL for -ifconfig or the current 
IPv4 if no change
+ * @param new_ipv6  The new IPv6 address, NULL for -ifconfig-ipv6 or the 
current IPv6 if no change
  */
 void
-update_vhash(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6)
+update_vaddr(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6)
 {
+    /* to avoid resetting an IP to its current value, the 
dco_update_peer_addr() function used to update the DCO,
+     * needs to know if actually an IPv4 or an IPv6 has been modified
+     */
+    int update_flags = 0;
+
+    /* -ifconfig */
+    if (!new_ip && mi->context.c2.push_ifconfig_defined)
+    {
+        unlearn_ifconfig(m, mi);
+        update_flags |= (1 << AF_INET);
+    }
+    /* -ifconfig-ipv6 */
+    if (!new_ipv6 && mi->context.c2.push_ifconfig_ipv6_defined)
+    {
+        unlearn_ifconfig_ipv6(m, mi);
+        update_flags |= (1 << AF_INET6);
+    }
+
     if (new_ip)
     {
-        /* Remove old IP */
-        if (mi->context.c2.push_ifconfig_defined)
-        {
-            unlearn_ifconfig(m, mi);
-        }
-
-        /* Add new IP */
         struct in_addr new_addr;
         CLEAR(new_addr);
-        if (inet_pton(AF_INET, new_ip, &new_addr) == 1
-            && multi_learn_in_addr_t(m, mi, ntohl(new_addr.s_addr), -1, true))
+        if (inet_pton(AF_INET, new_ip, &new_addr) == 1)
         {
-            mi->context.c2.push_ifconfig_defined = true;
-            mi->context.c2.push_ifconfig_local = ntohl(new_addr.s_addr);
-            /* set our client's VPN endpoint for status reporting purposes */
-            mi->reporting_addr = mi->context.c2.push_ifconfig_local;
+            new_addr.s_addr = ntohl(new_addr.s_addr);
+            /* if no previous IP or the new IP is different */
+            if (!mi->context.c2.push_ifconfig_defined
+                || (mi->context.c2.push_ifconfig_defined
+                    && new_addr.s_addr != mi->context.c2.push_ifconfig_local))
+            {
+                /* Remove old IP */
+                if (mi->context.c2.push_ifconfig_defined)
+                {
+                    unlearn_ifconfig(m, mi);
+                }
+
+                /* Add new IP */
+                if (multi_learn_in_addr_t(m, mi, new_addr.s_addr, -1, true))
+                {
+                    mi->context.c2.push_ifconfig_defined = true;
+                    mi->context.c2.push_ifconfig_local = new_addr.s_addr;
+                    /* set our client's VPN endpoint for status reporting 
purposes */
+                    mi->reporting_addr = mi->context.c2.push_ifconfig_local;
+                    update_flags |= (1 << AF_INET);
+                }
+            }
         }
     }

     if (new_ipv6)
     {
-        /* Remove old IPv6 */
-        if (mi->context.c2.push_ifconfig_ipv6_defined)
-        {
-            unlearn_ifconfig_ipv6(m, mi);
-        }
-
-        /* Add new IPv6 */
         struct in6_addr new_addr6;
         CLEAR(new_addr6);
-        if (inet_pton(AF_INET6, new_ipv6, &new_addr6) == 1
-            && multi_learn_in6_addr(m, mi, new_addr6, -1, true))
+        if (inet_pton(AF_INET6, new_ipv6, &new_addr6) == 1)
         {
-            mi->context.c2.push_ifconfig_ipv6_defined = true;
-            mi->context.c2.push_ifconfig_ipv6_local = new_addr6;
-            /* set our client's VPN endpoint for status reporting purposes */
-            mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local;
+            /* if no previous IPv6 or the new IPv6 is different */
+            if (!mi->context.c2.push_ifconfig_ipv6_defined
+                || (mi->context.c2.push_ifconfig_ipv6_defined
+                    && memcmp(&new_addr6, 
&mi->context.c2.push_ifconfig_ipv6_local, sizeof(struct in6_addr))))
+            {
+                /* Remove old IPv6 */
+                if (mi->context.c2.push_ifconfig_ipv6_defined)
+                {
+                    unlearn_ifconfig_ipv6(m, mi);
+                }
+
+                /* Add new IPv6 */
+                if (multi_learn_in6_addr(m, mi, new_addr6, -1, true))
+                {
+                    mi->context.c2.push_ifconfig_ipv6_defined = true;
+                    mi->context.c2.push_ifconfig_ipv6_local = new_addr6;
+                    /* set our client's VPN endpoint for status reporting 
purposes */
+                    mi->reporting_addr_ipv6 = 
mi->context.c2.push_ifconfig_ipv6_local;
+                    update_flags |= (1 << AF_INET);
+                }
+            }
         }
     }
+
+    if (!update_flags)
+    {
+        return;
+    }
+
+    /* we update the virtual addresses of the peer in the DCO */
+    if (dco_enabled(&m->top.options))
+    {
+        dco_multi_update_peer_addr(m, mi, update_flags);
+    }
+
+    /* we add the iroutes with the new VPN IP */
+    if (update_flags & (1 << AF_INET) && new_ip)
+    {
+        multi_add_iroutes_v4(m, mi);
+    }
+    if (update_flags & (1 << AF_INET6) && new_ipv6)
+    {
+        multi_add_iroutes_v6(m, mi);
+    }
 }

 bool
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 1209dfb..7bda347 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -714,8 +714,6 @@
 #endif

 void
-update_vhash(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6);
-void unlearn_ifconfig(struct multi_context *m, struct multi_instance *mi);
-void unlearn_ifconfig_ipv6(struct multi_context *m, struct multi_instance *mi);
+update_vaddr(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6);

 #endif /* MULTI_H */
diff --git a/src/openvpn/push_util.c b/src/openvpn/push_util.c
index 51c7b5f..bcab1d0 100644
--- a/src/openvpn/push_util.c
+++ b/src/openvpn/push_util.c
@@ -151,14 +151,35 @@
     struct options o;
     CLEAR(o);

-    /* Set canary values to detect ifconfig options in push-update messages.
-     * These placeholder strings will be overwritten to NULL by the option
-     * parser if -ifconfig or -ifconfig-ipv6 options are present in the
-     * push-update.
+    /* Set canary values to detect -ifconfig and -ifconfig-ipv6 options in
+     * push-update messages. These placeholder strings will be overwritten
+     * to NULL by the option parser if -ifconfig or -ifconfig-ipv6 options
+     * are present in the push-update, by the a new value if present, or
+     * they will remain equals to the current values otherwise.
+     * If the current values are not defined they will remain NULL.
      */
-    const char *canary = "canary";
-    o.ifconfig_local = canary;
-    o.ifconfig_ipv6_local = canary;
+
+    char ifconfig_local[INET_ADDRSTRLEN];
+    struct in_addr addr4 = { .s_addr = 
htonl(mi->context.c2.push_ifconfig_local) };
+    if (mi->context.c2.push_ifconfig_defined)
+    {
+        o.ifconfig_local = inet_ntop(AF_INET, &addr4, ifconfig_local, 
sizeof(ifconfig_local));
+        if (!o.ifconfig_local)
+        {
+            return false;
+        }
+    }
+
+    char ifconfig_ipv6_local[INET6_ADDRSTRLEN];
+    if (mi->context.c2.push_ifconfig_ipv6_defined)
+    {
+        o.ifconfig_ipv6_local = inet_ntop(AF_INET6, 
&mi->context.c2.push_ifconfig_ipv6_local,
+                                          ifconfig_ipv6_local, 
sizeof(ifconfig_ipv6_local));
+        if (!o.ifconfig_ipv6_local)
+        {
+            return false;
+        }
+    }

     struct buffer_entry *e = msgs->head;
     while (e)
@@ -195,28 +216,8 @@

     if (option_types_found & OPT_P_UP)
     {
-        /* -ifconfig */
-        if (!o.ifconfig_local && mi->context.c2.push_ifconfig_defined)
-        {
-            unlearn_ifconfig(m, mi);
-        }
-        /* -ifconfig-ipv6 */
-        if (!o.ifconfig_ipv6_local && 
mi->context.c2.push_ifconfig_ipv6_defined)
-        {
-            unlearn_ifconfig_ipv6(m, mi);
-        }
-
-        if (o.ifconfig_local && !strcmp(o.ifconfig_local, canary))
-        {
-            o.ifconfig_local = NULL;
-        }
-        if (o.ifconfig_ipv6_local && !strcmp(o.ifconfig_ipv6_local, canary))
-        {
-            o.ifconfig_ipv6_local = NULL;
-        }
-
-        /* new ifconfig or new ifconfig-ipv6 */
-        update_vhash(m, mi, o.ifconfig_local, o.ifconfig_ipv6_local);
+        /* ifconfig or ifconfig-ipv6 */
+        update_vaddr(m, mi, o.ifconfig_local, o.ifconfig_ipv6_local);
     }

     return true;
@@ -253,12 +254,14 @@
 static int
 send_push_update(struct multi_context *m, const void *target, const char *msg, 
const push_update_type type, const size_t push_bundle_size)
 {
+#if !defined(TARGET_LINUX)
     if (dco_enabled(&m->top.options))
     {
         msg(M_WARN, "WARN: PUSH_UPDATE messages cannot currently be sent while 
DCO is enabled."
                     " To send a PUSH_UPDATE message, be sure to use the 
--disable-dco option.");
         return 0;
     }
+#endif

     if (!msg || !*msg || !m || (!target && type != UPT_BROADCAST))
     {
diff --git a/tests/unit_tests/openvpn/test_push_update_msg.c 
b/tests/unit_tests/openvpn/test_push_update_msg.c
index 9b7978e..2d2fc93 100644
--- a/tests/unit_tests/openvpn/test_push_update_msg.c
+++ b/tests/unit_tests/openvpn/test_push_update_msg.c
@@ -31,19 +31,7 @@
 }

 void
-unlearn_ifconfig(struct multi_context *m, struct multi_instance *mi)
-{
-    return;
-}
-
-void
-unlearn_ifconfig_ipv6(struct multi_context *m, struct multi_instance *mi)
-{
-    return;
-}
-
-void
-update_vhash(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6)
+update_vaddr(struct multi_context *m, struct multi_instance *mi, const char 
*new_ip, const char *new_ipv6)
 {
     return;
 }

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1450?usp=email
To unsubscribe, or for help writing mail filters, visit 
http://gerrit.openvpn.net/settings?usp=email

Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I88f37e47026a7d7ea0f7c6bde1d79d32c14e5297
Gerrit-Change-Number: 1450
Gerrit-PatchSet: 3
Gerrit-Owner: mrbff <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to