Browse code

dco: backport OS-independent part of peer float support

This is a backport of commit cb8a0f6f5741d102b667d98370ab4d553503d0b5,
which introduces float support for DCO linux, Windows, and the
OS-independent parts.

DCO linux/windows in 2.6 has no float support kernel-side, so this
ignores all OS dependent parts, backporting just enough to add
FreeBSD support in the next patch.

One notable difference in the backport is that 2.6 has no multi-socket
support, so all the "link_sockets[0]" occurances need to be changed back
to "link_socket".

See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=289303 for all
the epic details...

Change-Id: Ib748e726eb84dcbe8a48b297d165dec80c0e578d
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Ralf Lici <ralf@mandelbit.com>
(cherry picked from commit cb8a0f6f5741d102b667d98370ab4d553503d0b5)
Message-Id: <20250908081124.17933-1-gert@greenie.muc.de>
URL: https://sourceforge.net/p/openvpn/mailman/message/59230454/
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Ralf Lici authored on 2025/09/08 17:11:18
Showing 3 changed files
... ...
@@ -1234,6 +1234,41 @@ process_incoming_link(struct context *c)
1234 1234
     perf_pop();
1235 1235
 }
1236 1236
 
1237
+void
1238
+extract_dco_float_peer_addr(const sa_family_t socket_family,
1239
+                            struct openvpn_sockaddr *out_osaddr,
1240
+                            const struct sockaddr *float_sa)
1241
+{
1242
+    if (float_sa->sa_family == AF_INET)
1243
+    {
1244
+        struct sockaddr_in *float4 = (struct sockaddr_in *)float_sa;
1245
+        /* DCO treats IPv4-mapped IPv6 addresses as pure IPv4. However, on a
1246
+         * dual-stack socket, we need to preserve the mapping otherwise openvpn
1247
+         * will not be able to find the peer by its transport address.
1248
+         */
1249
+        if (socket_family == AF_INET6)
1250
+        {
1251
+            out_osaddr->addr.in6.sin6_family = AF_INET6;
1252
+            out_osaddr->addr.in6.sin6_port = float4->sin_port;
1253
+
1254
+            memset(&out_osaddr->addr.in6.sin6_addr.s6_addr, 0, 10);
1255
+            out_osaddr->addr.in6.sin6_addr.s6_addr[10] = 0xff;
1256
+            out_osaddr->addr.in6.sin6_addr.s6_addr[11] = 0xff;
1257
+            memcpy(&out_osaddr->addr.in6.sin6_addr.s6_addr[12],
1258
+                   &float4->sin_addr.s_addr, sizeof(in_addr_t));
1259
+        }
1260
+        else
1261
+        {
1262
+            memcpy(&out_osaddr->addr.in4, float4, sizeof(struct sockaddr_in));
1263
+        }
1264
+    }
1265
+    else
1266
+    {
1267
+        struct sockaddr_in6 *float6 = (struct sockaddr_in6 *)float_sa;
1268
+        memcpy(&out_osaddr->addr.in6, float6, sizeof(struct sockaddr_in6));
1269
+    }
1270
+}
1271
+
1237 1272
 static void
1238 1273
 process_incoming_dco(struct context *c)
1239 1274
 {
... ...
@@ -189,6 +189,21 @@ bool process_incoming_link_part1(struct context *c, struct link_socket_info *lsi
189 189
 void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf);
190 190
 
191 191
 /**
192
+ * Transfers \c float_sa data extracted from an incoming DCO
193
+ * PEER_FLOAT_NTF to \c out_osaddr for later processing.
194
+ *
195
+ * @param socket_family - The address family of the socket
196
+ * @param out_osaddr - openvpn_sockaddr struct that will be filled the new
197
+ *      address data
198
+ * @param float_sa - The sockaddr struct containing the data received from the
199
+ *      DCO notification
200
+ */
201
+void
202
+extract_dco_float_peer_addr(sa_family_t socket_family,
203
+                            struct openvpn_sockaddr *out_osaddr,
204
+                            const struct sockaddr *float_sa);
205
+
206
+/**
192 207
  * Write a packet to the external network interface.
193 208
  * @ingroup external_multiplexer
194 209
  *
... ...
@@ -3169,6 +3169,18 @@ multi_process_float(struct multi_context *m, struct multi_instance *mi)
3169 3169
             goto done;
3170 3170
         }
3171 3171
 
3172
+        /* It doesn't make sense to let a peer float to the address it already
3173
+         * has, so we disallow it. This can happen if a DCO netlink notification
3174
+         * gets lost and we miss a floating step.
3175
+         */
3176
+        if (m1->peer_id == m2->peer_id)
3177
+        {
3178
+            msg(M_WARN, "disallowing peer %" PRIu32 " (%s) from floating to "
3179
+                "its own address (%s)",
3180
+                m1->peer_id, tls_common_name(mi->context.c2.tls_multi, false),
3181
+                mroute_addr_print(&mi->real, &gc));
3182
+            goto done;
3183
+        }
3172 3184
         msg(D_MULTI_MEDIUM, "closing instance %s", multi_instance_string(ex_mi, false, &gc));
3173 3185
         multi_close_instance(m, ex_mi, false);
3174 3186
     }
... ...
@@ -3301,6 +3313,17 @@ multi_process_incoming_dco(struct multi_context *m)
3301 3301
         {
3302 3302
             process_incoming_del_peer(m, mi, dco);
3303 3303
         }
3304
+#if 0
3305
+        else if (dco->dco_message_type == OVPN_CMD_FLOAT_PEER)
3306
+        {
3307
+            ASSERT(mi->context.c2.link_socket);
3308
+            extract_dco_float_peer_addr(mi->context.c2.link_socket->info.af,
3309
+                                        &m->top.c2.from.dest,
3310
+                                        (struct sockaddr *)&dco->dco_float_peer_ss);
3311
+            multi_process_float(m, mi);
3312
+            CLEAR(dco->dco_float_peer_ss);
3313
+        }
3314
+#endif /* if defined(TARGET_LINUX) || defined(TARGET_WIN32) */
3304 3315
         else if (dco->dco_message_type == OVPN_CMD_SWAP_KEYS)
3305 3316
         {
3306 3317
             tls_session_soft_reset(mi->context.c2.tls_multi);