]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
multipeer: introduce asymmetric peer-id
authorGianmarco De Gregori <gianmarco@mandelbit.com>
Tue, 28 Jul 2026 12:23:50 +0000 (14:23 +0200)
committerGert Doering <gert@greenie.muc.de>
Sat, 8 Aug 2026 13:55:15 +0000 (15:55 +0200)
In order to achieve a multipeer functionality, peers now
use separate IDs for sending (tx_peer_id) and receiving
(rx_peer_id).

Each peer announces its own ID through pushing peer-info
using 'ID=7f1' hex format so identification can still
happen even if IP/port changes.

In P2P mode, peer switch to using the announced IDs after
mutual exchange.
In P2MP mode, clients always announce their ID, and servers
can optionally respond with their own to enable the same
behavior.

Change-Id: I0a13ee90b6706acf20eabcee3bab3f2dff639bf9
Signed-off-by: Gianmarco De Gregori <gianmarco@mandelbit.com>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1089
Message-Id: <20260728122355.2566-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg37944.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
12 files changed:
src/openvpn/dco.c
src/openvpn/init.c
src/openvpn/misc.c
src/openvpn/multi.c
src/openvpn/options.h
src/openvpn/push.c
src/openvpn/push_util.c
src/openvpn/ssl.c
src/openvpn/ssl_common.h
src/openvpn/ssl_ncp.c
src/openvpn/ssl_util.c
src/openvpn/ssl_util.h

index 25843688dea358e2061b9634fd815223ec2f6444..ce73701e2467d889e4c90a7f27606b429ba1047a 100644 (file)
@@ -544,14 +544,15 @@ dco_p2p_add_new_peer(struct context *c)
         c->c2.tls_multi->dco_peer_id = -1;
     }
 #endif
-    int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id, sock->sd, NULL,
-                           proto_is_dgram(sock->info.proto) ? remoteaddr : NULL, NULL, NULL);
+    int ret = dco_new_peer(&c->c1.tuntap->dco, multi->rx_peer_id, sock->sd, NULL,
+                           proto_is_dgram(sock->info.proto) ? remoteaddr : NULL,
+                           NULL, NULL);
     if (ret < 0)
     {
         return ret;
     }
 
-    c->c2.tls_multi->dco_peer_id = multi->peer_id;
+    c->c2.tls_multi->dco_peer_id = multi->rx_peer_id;
 
     return 0;
 }
@@ -626,7 +627,7 @@ dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
 {
     struct context *c = &mi->context;
 
-    int peer_id = c->c2.tls_multi->peer_id;
+    int peer_id = c->c2.tls_multi->rx_peer_id;
     struct sockaddr *remoteaddr, *localaddr = NULL;
     struct sockaddr_storage local = { 0 };
     const socket_descriptor_t sd = c->c2.link_sockets[0]->sd;
@@ -706,7 +707,7 @@ dco_install_iroute(struct multi_context *m, struct multi_instance *mi, struct mr
     {
 #if defined(_WIN32)
         dco_win_add_iroute_ipv6(&c->c1.tuntap->dco, addr->v6.addr, addr->netbits,
-                                c->c2.tls_multi->peer_id);
+                                c->c2.tls_multi->rx_peer_id);
 #else
         const struct in6_addr *gateway = &mi->context.c2.push_ifconfig_ipv6_local;
         if (addr->type & MR_ONLINK_DCO_ADDR)
@@ -723,7 +724,7 @@ dco_install_iroute(struct multi_context *m, struct multi_instance *mi, struct mr
     {
 #if defined(_WIN32)
         dco_win_add_iroute_ipv4(&c->c1.tuntap->dco, addr->v4.addr, addr->netbits,
-                                c->c2.tls_multi->peer_id);
+                                c->c2.tls_multi->rx_peer_id);
 #else
         in_addr_t dest = htonl(addr->v4.addr);
         const in_addr_t *gateway = &mi->context.c2.push_ifconfig_local;
index 0236886d3d466d04e9561005c14b463327c35f4a..906a83ca7eaf1b0a33c049fd862bc76b87b0d24e 100644 (file)
@@ -2203,9 +2203,10 @@ tls_print_deferred_options_results(struct context *c)
                    md_kt_name(o->authname));
     }
 
-    if (o->use_peer_id)
+    if (c->c2.tls_multi && c->c2.tls_multi->use_peer_id)
     {
-        buf_printf(&out, ", peer-id: %d", o->peer_id);
+        buf_printf(&out, ", rx-peer-id: %u, tx-peer-id: %u", c->c2.tls_multi->rx_peer_id,
+                   c->c2.tls_multi->tx_peer_id);
     }
 
 #ifdef USE_COMP
@@ -2322,6 +2323,11 @@ do_deferred_options_part2(struct context *c)
     }
 #endif
 
+    /* The peer-id can also be negotiated without being pushed, so sync the
+     * option before the frame is recalculated: it decides whether the
+     * DATA_V2 header is accounted for */
+    c->options.use_peer_id = c->c2.tls_multi->use_peer_id;
+
     struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
     if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options, &c->c2.frame,
                                           frame_fragment, get_link_socket_info(c),
@@ -2675,7 +2681,8 @@ do_deferred_options(struct context *c, const uint64_t found, const bool is_updat
     {
         msg(D_PUSH_DEBUG, "OPTIONS IMPORT: peer-id set");
         c->c2.tls_multi->use_peer_id = true;
-        c->c2.tls_multi->peer_id = c->options.peer_id;
+        c->c2.tls_multi->tx_peer_id = c->options.peer_id;
+        c->c2.tls_multi->rx_peer_id = c->options.peer_id;
     }
 
     /* process (potentially) pushed options */
@@ -2702,7 +2709,7 @@ do_deferred_options(struct context *c, const uint64_t found, const bool is_updat
     /* Ensure that for epoch data format is only enabled if also data v2
      * is enabled */
     bool epoch_data = c->options.imported_protocol_flags & CO_EPOCH_DATA_KEY_FORMAT;
-    bool datav2_enabled = c->options.use_peer_id && c->options.peer_id < MAX_PEER_ID;
+    bool datav2_enabled = c->c2.tls_multi->use_peer_id && c->c2.tls_multi->tx_peer_id < MAX_PEER_ID;
 
     if (epoch_data && !datav2_enabled)
     {
index 10bfc35111e76103f55f2805fe1bb9fcea6555fb..a8c88c9327fdd2b1bfe69464435a5075615c4a50 100644 (file)
@@ -765,14 +765,15 @@ output_peer_info_env(struct env_set *es, const char *peer_info)
     {
         chomp(line);
         if (validate_peer_info_line(line)
-            && (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0))
+            && (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0
+                || strncmp(line, "ID", 2) == 0))
         {
             msg(M_INFO, "peer info: %s", line);
             env_set_add(es, line);
         }
         else
         {
-            msg(M_WARN, "validation failed on peer_info line received from client");
+            msg(M_WARN, "validation failed on peer_info line received");
         }
     }
 }
index a4e9c1c8041b57e060ff105a3f4c1dd007d25106..cfa2ad889f1afe0c444d3d711198e5c8ecb2524b 100644 (file)
@@ -430,7 +430,7 @@ multi_instance_string(const struct multi_instance *mi, bool null, struct gc_aren
         if (mi->context.c2.tls_multi && check_debug_level(D_DCO_DEBUG)
             && dco_enabled(&mi->context.options))
         {
-            buf_printf(&out, " peer-id=%d", mi->context.c2.tls_multi->peer_id);
+            buf_printf(&out, " rx-peer-id=%d", mi->context.c2.tls_multi->rx_peer_id);
         }
         return BSTR(&out);
     }
@@ -598,9 +598,9 @@ multi_close_instance(struct multi_context *m, struct multi_instance *mi, bool sh
         }
 #endif
 
-        if (mi->context.c2.tls_multi->peer_id != MAX_PEER_ID)
+        if (mi->context.c2.tls_multi->rx_peer_id != MAX_PEER_ID)
         {
-            m->instances[mi->context.c2.tls_multi->peer_id] = NULL;
+            m->instances[mi->context.c2.tls_multi->rx_peer_id] = NULL;
 
             /* Adjust the max_peerid as this might have been the highest
              * peer id instance */
@@ -908,8 +908,7 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int
 #else
                         sep,
 #endif
-                        sep,
-                        mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX,
+                        sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->rx_peer_id : MAX_PEER_ID,
                         sep, translate_cipher_name_to_openvpn(mi->context.options.ciphername));
                 }
                 gc_free(&gc);
@@ -3121,12 +3120,12 @@ multi_process_float(struct multi_context *m, struct multi_instance *mi, struct l
          * has, so we disallow it. This can happen if a DCO netlink notification
          * gets lost and we miss a floating step.
          */
-        if (m1->peer_id == m2->peer_id)
+        if (m1->rx_peer_id == m2->rx_peer_id)
         {
             msg(M_WARN,
                 "disallowing peer %" PRIu32 " (%s) from floating to "
                 "its own address (%s)",
-                m1->peer_id, tls_common_name(mi->context.c2.tls_multi, false),
+                m1->rx_peer_id, tls_common_name(mi->context.c2.tls_multi, false),
                 mroute_addr_print(&mi->real, &gc));
             goto done;
         }
@@ -3139,7 +3138,8 @@ multi_process_float(struct multi_context *m, struct multi_instance *mi, struct l
     }
 
     msg(D_MULTI_MEDIUM, "peer %" PRIu32 " (%s) floated from %s to %s",
-        mi->context.c2.tls_multi->peer_id, tls_common_name(mi->context.c2.tls_multi, false),
+        mi->context.c2.tls_multi->rx_peer_id,
+        tls_common_name(mi->context.c2.tls_multi, false),
         mroute_addr_print_ex(&mi->real, MAPF_SHOW_FAMILY, &gc),
         mroute_addr_print_ex(&real, MAPF_SHOW_FAMILY, &gc));
 
@@ -4099,7 +4099,7 @@ multi_assign_peer_id(struct multi_context *m, struct multi_instance *mi)
     {
         if (!m->instances[i])
         {
-            mi->context.c2.tls_multi->peer_id = i;
+            mi->context.c2.tls_multi->rx_peer_id = i;
             m->instances[i] = mi;
             break;
         }
@@ -4108,11 +4108,11 @@ multi_assign_peer_id(struct multi_context *m, struct multi_instance *mi)
     /* should not really end up here, since multi_create_instance returns null
      * if amount of clients exceeds max_clients and this method would then
      * also not have been called */
-    ASSERT(mi->context.c2.tls_multi->peer_id < m->max_clients);
+    ASSERT(mi->context.c2.tls_multi->rx_peer_id < m->max_clients);
 
-    if (mi->context.c2.tls_multi->peer_id > m->max_peerid)
+    if (mi->context.c2.tls_multi->rx_peer_id > m->max_peerid)
     {
-        m->max_peerid = mi->context.c2.tls_multi->peer_id;
+        m->max_peerid = mi->context.c2.tls_multi->rx_peer_id;
     }
 }
 
index 2f7fe300abeb77c03dd7ba32e2b8d37cb0c0223c..30a146fe0e2a09c787a7d8156abd4531990cee8d 100644 (file)
@@ -697,6 +697,9 @@ struct options
     enum tun_driver_type windows_driver;
 #endif
 
+    /** Whether the data channel uses the DATA_V2 header (peer-id).
+     *  Mirror of tls_multi->use_peer_id, needed by the MTU/frame calculation
+     *  which only has access to struct options. */
     bool use_peer_id;
     uint32_t peer_id;
 
index e391147c8c8124e135b398d52c120c3ee5bb6a18..633c007dc060bb83a8c0a7624b19c6ec0d4af76b 100644 (file)
@@ -661,9 +661,9 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, struct push_list *pus
                         print_in_addr_t(c->c2.push_ifconfig_remote_netmask, 0, gc));
     }
 
-    if (tls_multi->use_peer_id)
+    if (tls_multi->use_peer_id && !tls_multi->use_asymmetric_peer_id)
     {
-        push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);
+        push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->rx_peer_id);
     }
     /*
      * If server uses --auth-gen-token and we have an auth token
index f57f54d3aaf0e47a65af24fe09be1d4503c8f6e8..c927f26566959d074a464db8dc4f2e235f6a8f75 100644 (file)
@@ -188,7 +188,7 @@ send_single_push_update(struct multi_context *m, struct multi_instance *mi, stru
         unsigned int permission_mask = pull_permission_mask(c);
         if (process_push_update(c, &o, permission_mask, &option_types_found, &tmp_msg, true) == PUSH_MSG_ERROR)
         {
-            msg(M_WARN, "Failed to process push update message sent to client ID: %u", c->c2.tls_multi->peer_id);
+            msg(M_WARN, "Failed to process push update message sent to client ID: %u", c->c2.tls_multi->rx_peer_id);
         }
         e = e->next;
     }
@@ -294,7 +294,7 @@ send_push_update(struct multi_context *m, const void *target, const char *msg, c
 
         if (!support_push_update(mi))
         {
-            msg(M_CLIENT, "PUSH_UPDATE: not sending message to unsupported peer with ID: %u", mi->context.c2.tls_multi->peer_id);
+            msg(M_CLIENT, "PUSH_UPDATE: not sending message to unsupported peer with ID: %u", mi->context.c2.tls_multi->rx_peer_id);
             buffer_list_free(msgs);
             gc_free(&gc);
             return 0;
@@ -329,7 +329,7 @@ send_push_update(struct multi_context *m, const void *target, const char *msg, c
         /* Type is UPT_BROADCAST so we update every client */
         if (!send_single_push_update(m, curr_mi, msgs))
         {
-            msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", curr_mi->context.c2.tls_multi->peer_id);
+            msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", curr_mi->context.c2.tls_multi->rx_peer_id);
             continue;
         }
         count++;
index ccd8264ccc65c885175beaf3c3ec9cd6f5819d15..d5100af76b2e953c2f2845a0af55a8cd27f6dfc0 100644 (file)
@@ -1168,7 +1168,10 @@ tls_multi_init(struct tls_options *tls_options)
     /* get command line derived options */
     ret->opt = *tls_options;
     ret->dco_peer_id = -1;
-    ret->peer_id = MAX_PEER_ID;
+    ret->use_asymmetric_peer_id = false;
+    /* The rx_peer_id is also used to identify DCO clients */
+    ret->rx_peer_id = MAX_PEER_ID;
+    ret->tx_peer_id = MAX_PEER_ID;
 
     return ret;
 }
@@ -1181,6 +1184,15 @@ tls_multi_init_finalize(struct tls_multi *multi, int tls_mtu)
 
     tls_session_init(multi, &multi->session[TM_ACTIVE]);
     tls_session_init(multi, &multi->session[TM_INITIAL]);
+
+    if (!multi->opt.dco_enabled)
+    {
+        /* Calculate the asymmetric peer-id */
+        if (multi->rx_peer_id == MAX_PEER_ID && multi->session[TM_INITIAL].opt->mode != MODE_SERVER)
+        {
+            multi->rx_peer_id = (uint32_t)(get_random() % (MAX_PEER_ID - 1));
+        }
+    }
 }
 
 /*
@@ -1858,6 +1870,25 @@ read_string_alloc(struct buffer *buf)
     return str;
 }
 
+static bool
+push_peer_info_peerid(struct buffer *out, struct tls_multi *multi, struct tls_session *session)
+{
+    if (multi->rx_peer_id == MAX_PEER_ID || session->opt->dco_enabled)
+    {
+        /* No valid peer id or DCO is enabled. Cannot use this feature */
+        return true;
+    }
+
+    /* In server mode we only add this when the client has announced its
+     * support for the feature */
+    if (session->opt->mode != MODE_SERVER || multi->use_asymmetric_peer_id)
+    {
+        return buf_printf(out, "ID=%x\n", multi->rx_peer_id);
+    }
+
+    return true;
+}
+
 /**
  * Prepares the IV_ and UV_ variables that are part of the
  * exchange to signal the peer's capabilities. The amount
@@ -1871,15 +1902,22 @@ read_string_alloc(struct buffer *buf)
  *
  * @param buf       the buffer to write these variables to
  * @param session   the TLS session object
+ * @param multi     the TLS multi object
  * @return          true if no error was encountered
  */
 static bool
-push_peer_info(struct buffer *buf, struct tls_session *session)
+push_peer_info(struct buffer *buf, struct tls_multi *multi, struct tls_session *session)
 {
     struct gc_arena gc = gc_new();
     bool ret = false;
     struct buffer out = alloc_buf_gc(512 * 3, &gc);
 
+    /* The asymmetric peer-id is always written when enabled */
+    if (!push_peer_info_peerid(&out, multi, session))
+    {
+        goto error;
+    }
+
     if (session->opt->push_peer_info_detail > 1)
     {
         /* push version */
@@ -2019,7 +2057,11 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
                 }
             }
         }
+    }
 
+    /* write peer info string if there is anything in it, empty string otherwise */
+    if (BLEN(&out) > 0)
+    {
         if (!write_string(buf, BSTR(&out), -1))
         {
             goto error;
@@ -2027,7 +2069,7 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
     }
     else
     {
-        if (!write_empty_string(buf)) /* no peer info */
+        if (!write_empty_string(buf))
         {
             goto error;
         }
@@ -2156,7 +2198,7 @@ key_method_2_write(struct buffer *buf, struct tls_multi *multi, struct tls_sessi
         }
     }
 
-    if (!push_peer_info(buf, session))
+    if (!push_peer_info(buf, multi, session))
     {
         goto error;
     }
@@ -2269,6 +2311,19 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio
     if (multi->peer_info)
     {
         output_peer_info_env(session->opt->es, multi->peer_info);
+        uint32_t peer_id = extract_asymmetric_peer_id(multi->peer_info);
+        if (peer_id != MAX_PEER_ID && !session->opt->dco_enabled)
+        {
+            multi->tx_peer_id = peer_id;
+            multi->use_asymmetric_peer_id = true;
+            multi->use_peer_id = true;
+        }
+        else
+        {
+            /* Peer has no support for asymmetric peer-id, and DCO currently
+             * can only handle symmetric peer IDs */
+            multi->tx_peer_id = multi->rx_peer_id;
+        }
     }
 
     free(multi->remote_ciphername);
@@ -4030,8 +4085,8 @@ tls_prepend_opcode_v2(const struct tls_multi *multi, struct buffer *buf)
     msg(D_TLS_DEBUG, __func__);
 
     ASSERT(ks);
-
-    peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 | (multi->peer_id & 0xFFFFFF));
+    peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24
+                 | (multi->tx_peer_id & 0xFFFFFF));
     ASSERT(buf_write_prepend(buf, &peer, 4));
 }
 
index 9c90242fa381d10d96c42d83e76d385273b169e5..f6392f9ae8e711acce2e7250a34e9a4a337dac2f 100644 (file)
@@ -696,8 +696,10 @@ struct tls_multi
 #define AUTH_TOKEN_VALID_EMPTYUSER (1 << 2)
 
     /* For P_DATA_V2 */
-    uint32_t peer_id;
+    uint32_t rx_peer_id;
+    uint32_t tx_peer_id;
     bool use_peer_id;
+    bool use_asymmetric_peer_id;
 
     char *remote_ciphername; /**< cipher specified in peer's config file */
     bool remote_usescomp;    /**< remote announced comp-lzo in OCC string */
index baf5a951078e1eb8885254b34c658b0a8e038fc2..b450de82e3490461dfd611043cd2813baa43e03a 100644 (file)
@@ -408,6 +408,7 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session, const
 {
     /* will return 0 if peer_info is null */
     const unsigned int iv_proto_peer = extract_iv_proto(multi->peer_info);
+    const unsigned int tx_peer_id = extract_asymmetric_peer_id(multi->peer_info);
 
     /* The other peer does not support P2P NCP */
     if (!(iv_proto_peer & IV_PROTO_NCP_P2P))
@@ -418,7 +419,6 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session, const
     if (iv_proto_peer & IV_PROTO_DATA_V2)
     {
         multi->use_peer_id = true;
-        multi->peer_id = 0x76706e; /* 'v' 'p' 'n' */
     }
 
     if (iv_proto_peer & IV_PROTO_CC_EXIT_NOTIFY)
@@ -441,27 +441,42 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session, const
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
         session->opt->crypto_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
+    }
 
-        if (multi->use_peer_id)
+    if (multi->use_peer_id)
+    {
+        /* The asymmetric peer-id trumps on the EKM generated ones */
+        if ((iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
+            && (tx_peer_id != MAX_PEER_ID) && !multi->opt.dco_enabled)
+        {
+            multi->tx_peer_id = tx_peer_id;
+            multi->use_asymmetric_peer_id = true;
+        }
+        else
         {
+            uint8_t peerid[3];
             /* Using a non hardcoded peer-id makes a tiny bit harder to
              * fingerprint packets and also gives each connection a unique
              * peer-id that can be useful for NAT tracking etc. */
-
-            uint8_t peerid[3];
-            if (!key_state_export_keying_material(session, EXPORT_P2P_PEERID_LABEL,
-                                                  strlen(EXPORT_P2P_PEERID_LABEL), &peerid, 3))
+            if ((iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
+                && key_state_export_keying_material(session, EXPORT_P2P_PEERID_LABEL,
+                                                    strlen(EXPORT_P2P_PEERID_LABEL), &peerid, 3))
             {
-                /* Non DCO setup might still work but also this should never
-                 * happen or very likely the TLS encryption key exporter will
-                 * also fail */
-                msg(M_NONFATAL, "TLS key export for P2P peer id failed. "
-                                "Continuing anyway, expect problems");
+                multi->rx_peer_id = (peerid[0] << 16) + (peerid[1] << 8) + peerid[2];
             }
             else
             {
-                multi->peer_id = (peerid[0] << 16) + (peerid[1] << 8) + peerid[2];
+                if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
+                {
+                    /* Non DCO setup might still work but also this should never
+                     * happen or very likely the TLS encryption key exporter will
+                     * also fail */
+                    msg(M_NONFATAL, "TLS key export for P2P peer id failed. "
+                                    "Continuing anyway, expect problems");
+                }
+                multi->rx_peer_id = 0x76706e; /* 'v' 'p' 'n' */
             }
+            multi->tx_peer_id = multi->rx_peer_id;
         }
     }
     if (iv_proto_peer & IV_PROTO_DYN_TLS_CRYPT)
@@ -502,11 +517,13 @@ p2p_mode_ncp(struct tls_multi *multi, struct tls_session *session)
         common_cipher = BSTR(&out);
     }
 
-    msg(D_TLS_DEBUG_LOW,
-        "P2P mode NCP negotiation result: "
-        "TLS_export=%d, DATA_v2=%d, peer-id %d, epoch=%d, cipher=%s",
-        (bool)(session->opt->crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT), multi->use_peer_id,
-        multi->peer_id, (bool)(session->opt->crypto_flags & CO_EPOCH_DATA_KEY_FORMAT),
+    msg(D_TLS_DEBUG_LOW, "P2P mode NCP negotiation result: "
+                         "TLS_export=%d, DATA_v2=%d, rx-peer-id %d, tx-peer-id %d, epoch=%d, cipher=%s",
+        (bool)(session->opt->crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT),
+        multi->use_peer_id,
+        multi->rx_peer_id,
+        multi->tx_peer_id,
+        (bool)(session->opt->crypto_flags & CO_EPOCH_DATA_KEY_FORMAT),
         common_cipher);
 
     gc_free(&gc);
index 71fbc9e5e41a33a0fc4f71c8f9a4bb3208f9235b..2f01f8a3e29f9d59e9524e126d47ba161e5022a4 100644 (file)
@@ -24,7 +24,7 @@
 #endif
 
 #include "syshead.h"
-
+#include "openvpn.h"
 #include "ssl_util.h"
 
 char *
@@ -72,6 +72,25 @@ extract_iv_proto(const char *peer_info)
     return 0;
 }
 
+uint32_t
+extract_asymmetric_peer_id(const char *peer_info)
+{
+    for (const char *p = peer_info; p && (p = strstr(p, "ID=")); p += 3)
+    {
+        /* only accept "ID=" at the start of a line, so it does not match
+         * substrings like "UV_ID=" or "GUID=" */
+        if (p == peer_info || p[-1] == '\n')
+        {
+            uint32_t peer_id = 0;
+            if (sscanf(p, "ID=%x", &peer_id) == 1 && peer_id < MAX_PEER_ID)
+            {
+                return peer_id;
+            }
+        }
+    }
+    return MAX_PEER_ID;
+}
+
 const char *
 options_string_compat_lzo(const char *options, struct gc_arena *gc)
 {
index 2177d949052dfe12b526fa9b83cd6e1aeb1b37cc..1a69e6fa962225083afe61264fb68f9d229fb7ce 100644 (file)
@@ -53,6 +53,15 @@ char *extract_var_peer_info(const char *peer_info, const char *var, struct gc_ar
  */
 unsigned int extract_iv_proto(const char *peer_info);
 
+
+/**
+ * Extracts the ID variable and returns its value or
+ * MAX_PEER_ID if it cannot be extracted.
+ *
+ * @param peer_info     peer info string to search for ID
+ */
+uint32_t extract_asymmetric_peer_id(const char *peer_info);
+
 /**
  * Takes a locally produced OCC string for TLS server mode and modifies as
  * if the option comp-lzo was enabled. This is to send a client in