[Openvpn-devel,v1] multi: remove m->pending

Message ID 20261028152825.19716-1-gert@greenie.muc.de
State New
Headers
Series [Openvpn-devel,v1] multi: remove m->pending |

Commit Message

Gert Doering Oct. 28, 2026, 3:28 p.m. UTC
  From: Gianmarco De Gregori <gianmarco@mandelbit.com>

m->pending pointed to the instance with output still to be written. It
was set as a side effect of multi_process_post() and read back by the
multi_io_action() state machine, by p2mp_iow_flags() to arm tun and UDP
sockets for writing across event_wait(), and by the incoming functions,
which dropped the packet if it was still set.

Now that every event goes through multi_io_action() and its output is
written before returning to the main loop, nothing is ever left pending
across event_wait(). Take the instance with output from the one
dispatch touched instead, and pass it explicitly to the write functions.
Tun and UDP sockets are only armed for reading, so drop p2mp_iow_flags()
and the tun/UDP write event branches.

Change-Id: Ie3384ba46347801a6dea11b97cdf9edc17632c4d
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/+/2048
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/2048
This mail reflects revision 1 of this Change.

Acked-by according to Gerrit (reflected above):
Arne Schwabe <arne-openvpn@rfc2549.org>
  

Patch

diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 2841a81..b1d7713 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -575,10 +575,6 @@ 
     mi->n_clients_delta = 0;
 
     /* prevent dangling pointers */
-    if (m->pending == mi)
-    {
-        multi_set_pending(m, NULL);
-    }
     if (m->earliest_wakeup == mi)
     {
         m->earliest_wakeup = NULL;
@@ -3098,18 +3094,15 @@ 
             ret = false;
         }
     }
+#ifdef MULTI_DEBUG_EVENT_LOOP
     else
     {
-        /* continue to pend on output? */
-        multi_set_pending(m, ANY_OUT(&mi->context) ? mi : NULL);
-
-#ifdef MULTI_DEBUG_EVENT_LOOP
-        printf("POST %s[%d] to=%d lo=%d/%d w=%" PRIi64 "/%ld\n", id(mi), (int)(mi == m->pending),
+        printf("POST %s[%d] to=%d lo=%d/%d w=%" PRIi64 "/%ld\n", id(mi), (int)ANY_OUT(&mi->context),
                mi->context.c2.to_tun.len, mi->context.c2.to_link.len,
                mi->context.c2.fragment ? mi->context.c2.fragment->outgoing.len : -1,
                (int64_t)mi->context.c2.timeval.tv_sec, (long)mi->context.c2.timeval.tv_usec);
-#endif
     }
+#endif
 
     if ((flags & MPP_RECORD_TOUCH) && m->mpp_touched)
     {
@@ -3400,13 +3393,14 @@ 
  * data packets.
  */
 static void
-multi_process_incoming_link_data(struct multi_context *m, bool floated, struct link_socket *sock)
+multi_process_incoming_link_data(struct multi_context *m, struct multi_instance *mi, bool floated,
+                                 struct link_socket *sock)
 {
     struct link_socket_info *lsi = &sock->info;
     const uint8_t *orig_buf;
 
     /* decrypt in instance context */
-    struct context *c = &m->pending->context;
+    struct context *c = &mi->context;
 
     orig_buf = c->c2.buf.data;
     if (process_incoming_link_part1(c, lsi, floated))
@@ -3414,7 +3408,7 @@ 
         /* nonzero length means that we have a valid, decrypted packed */
         if (floated && c->c2.buf.len > 0)
         {
-            multi_process_float(m, m->pending, sock);
+            multi_process_float(m, mi, sock);
         }
 
         process_incoming_link_part2(c, lsi, orig_buf);
@@ -3433,7 +3427,7 @@ 
             c->c2.to_tun.len = 0;
         }
         /* make sure that source address is associated with this client */
-        else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending)
+        else if (multi_get_instance_by_virtual_addr(m, &src, true) != mi)
         {
             /* IPv6 link-local address (fe80::xxx)? */
             if ((src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
@@ -3458,18 +3452,18 @@ 
             if (mroute_flags & MROUTE_EXTRACT_MCAST)
             {
                 /* for now, treat multicast as broadcast */
-                multi_bcast(m, &c->c2.to_tun, m->pending, 0);
+                multi_bcast(m, &c->c2.to_tun, mi, 0);
             }
             else /* possible client to client routing */
             {
                 ASSERT(!(mroute_flags & MROUTE_EXTRACT_BCAST));
-                struct multi_instance *mi = multi_get_instance_by_virtual_addr(m, &dest, true);
+                struct multi_instance *dest_mi = multi_get_instance_by_virtual_addr(m, &dest, true);
 
                 /* if dest addr is a known client, route to it */
-                if (mi)
+                if (dest_mi)
                 {
                     {
-                        multi_unicast(m, &c->c2.to_tun, mi);
+                        multi_unicast(m, &c->c2.to_tun, dest_mi);
                         register_activity(c, BLEN(&c->c2.to_tun));
                     }
                     c->c2.to_tun.len = 0;
@@ -3502,23 +3496,24 @@ 
 
         if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
         {
-            if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
+            if (multi_learn_addr(m, mi, &src, 0) == mi)
             {
                 /* check for broadcast */
                 if (m->enable_c2c)
                 {
                     if (mroute_flags & (MROUTE_EXTRACT_BCAST | MROUTE_EXTRACT_MCAST))
                     {
-                        multi_bcast(m, &c->c2.to_tun, m->pending, vid);
+                        multi_bcast(m, &c->c2.to_tun, mi, vid);
                     }
                     else /* try client-to-client routing */
                     {
-                        struct multi_instance *mi = multi_get_instance_by_virtual_addr(m, &dest, false);
+                        struct multi_instance *dest_mi =
+                            multi_get_instance_by_virtual_addr(m, &dest, false);
 
                         /* if dest addr is a known client, route to it */
-                        if (mi)
+                        if (dest_mi)
                         {
-                            multi_unicast(m, &c->c2.to_tun, mi);
+                            multi_unicast(m, &c->c2.to_tun, dest_mi);
                             register_activity(c, BLEN(&c->c2.to_tun));
                             c->c2.to_tun.len = 0;
                         }
@@ -3550,35 +3545,27 @@ 
 multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance,
                             const unsigned int mpp_flags, struct link_socket *sock)
 {
+    struct multi_instance *mi = instance;
     struct context *c;
     bool ret = true;
     bool floated = false;
 
-    if (m->pending)
-    {
-        return true;
-    }
-
-    if (!instance)
+    if (!mi)
     {
 #ifdef MULTI_DEBUG_EVENT_LOOP
         printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf));
 #endif
-        multi_set_pending(m, multi_get_create_instance_udp(m, &floated, sock));
-    }
-    else
-    {
-        multi_set_pending(m, instance);
+        mi = multi_get_create_instance_udp(m, &floated, sock);
     }
 
-    if (!m->pending)
+    if (!mi)
     {
         return true;
     }
-    set_prefix(m->pending);
+    set_prefix(mi);
 
     /* get instance context */
-    c = &m->pending->context;
+    c = &mi->context;
 
     if (!instance)
     {
@@ -3594,11 +3581,11 @@ 
 
     if (BLEN(&c->c2.buf) > 0)
     {
-        multi_process_incoming_link_data(m, floated, sock);
+        multi_process_incoming_link_data(m, mi, floated, sock);
     }
 
     /* postprocess and set wakeup */
-    ret = multi_process_post(m, m->pending, mpp_flags);
+    ret = multi_process_post(m, mi, mpp_flags);
 
     clear_prefix();
 
@@ -3625,11 +3612,6 @@ 
         printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf));
 #endif
 
-        if (m->pending)
-        {
-            return true;
-        }
-
         if (dev_type == DEV_TYPE_TAP && m->top.options.vlan_tagging)
         {
             vid = vlan_decapsulate(&m->top, &m->top.c2.buf);
@@ -3657,18 +3639,18 @@ 
             }
             else
             {
-                multi_set_pending(
-                    m, multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN));
+                struct multi_instance *mi =
+                    multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN);
 
-                if (m->pending)
+                if (mi)
                 {
                     /* get instance context */
-                    c = &m->pending->context;
+                    c = &mi->context;
 
-                    set_prefix(m->pending);
+                    set_prefix(mi);
 
                     {
-                        if (multi_output_queue_ready(m, m->pending))
+                        if (multi_output_queue_ready(m, mi))
                         {
                             /* transfer packet pointer from top-level context buffer to instance */
                             c->c2.buf = m->top.c2.buf;
@@ -3686,7 +3668,7 @@ 
                     process_incoming_tun(c, c->c2.link_sockets[0]);
 
                     /* postprocess and set wakeup */
-                    ret = multi_process_post(m, m->pending, mpp_flags);
+                    ret = multi_process_post(m, mi, mpp_flags);
 
                     clear_prefix();
                 }
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 31635ba..02841af 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -206,8 +206,11 @@ 
     unsigned long cid_counter;
 #endif
 
-    struct multi_instance *pending;
     struct multi_instance *earliest_wakeup;
+    /* While multi_io_dispatch() runs an action, points to its local where
+     * multi_process_post() with MPP_RECORD_TOUCH stores the instance it
+     * processed.  An action post-processes at most one instance, which
+     * multi_io_action() then closes on signal or writes the output of. */
     struct multi_instance **mpp_touched;
     struct context_buffers *context_buffers;
     time_t per_second_trigger;
@@ -638,11 +641,6 @@ 
  */
 #define MULTI_CHECK_SIG(m) EVENT_LOOP_CHECK_SIGNAL(&(m)->top, multi_process_signal, (m))
 
-static inline void
-multi_set_pending(struct multi_context *m, struct multi_instance *mi)
-{
-    m->pending = mi;
-}
 /**
  * Assigns a peer-id to a a client and adds the instance to the
  * the instances array of the \c multi_context structure.
diff --git a/src/openvpn/multi_io.c b/src/openvpn/multi_io.c
index 24a6918..a14978b 100644
--- a/src/openvpn/multi_io.c
+++ b/src/openvpn/multi_io.c
@@ -135,36 +135,6 @@ 
     }
 }
 
-/*
- * Return the io flags appropriate for
- * a point-to-multipoint tunnel.
- */
-static unsigned int
-p2mp_iow_flags(const struct multi_context *m, const struct link_socket *sock)
-{
-    unsigned int flags = IOW_WAIT_SIGNAL;
-
-    if (m->pending)
-    {
-        if (TUN_OUT(&m->pending->context))
-        {
-            flags |= IOW_TO_TUN;
-        }
-
-        /* If the to be processed socket happens to be the same one on the pending
-         * UDP client instance, we set that socket ready for write */
-        if (LINK_OUT(&m->pending->context) && m->pending->context.c2.link_sockets[0] == sock)
-        {
-            flags |= IOW_TO_LINK;
-        }
-    }
-    else
-    {
-        flags |= IOW_READ;
-    }
-    return flags;
-}
-
 int
 multi_io_wait(struct multi_context *m)
 {
@@ -177,7 +147,7 @@ 
 
         if (proto_is_dgram(sock->info.proto))
         {
-            multi_io_process_flags(&m->top, m->multi_io->es, sock, p2mp_iow_flags(m, sock));
+            multi_io_process_flags(&m->top, m->multi_io->es, sock, IOW_READ | IOW_WAIT_SIGNAL);
         }
         else if (!tuntap_is_dco_win(m->top.c1.tuntap))
         {
@@ -281,6 +251,7 @@ 
  * perform server-mode postprocessing.
  *
  * @param m            - The single \c multi_context structure.
+ * @param mi           - The \c multi_instance with the packet to send.
  * @param mpp_flags    - Fast I/O optimization flags.
  *
  * @return
@@ -289,12 +260,10 @@ 
  *  - Falls, if the \c multi_instance was closed.
  */
 static bool
-multi_io_write_tun(struct multi_context *m, const unsigned int mpp_flags)
+multi_io_write_tun(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
 {
-    struct multi_instance *mi = m->pending;
     bool ret = true;
 
-    ASSERT(mi);
 #ifdef MULTI_DEBUG_EVENT_LOOP
     printf("%s -> TUN len=%d\n", id(mi), mi->context.c2.to_tun.len);
 #endif
@@ -310,12 +279,8 @@ 
  * Drop a TUN/TAP outgoing packet..
  */
 static void
-multi_io_drop_tun(struct multi_context *m, const unsigned int mpp_flags)
+multi_io_drop_tun(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
 {
-    struct multi_instance *mi = m->pending;
-
-    ASSERT(mi);
-
     set_prefix(mi);
 
     msg(D_MULTI_ERRORS, "MULTI: Outgoing TUN queue full, dropped packet len=%d",
@@ -328,24 +293,22 @@ 
 }
 
 /*
- * Determine which instance has pending output
- * and prepare the output for sending in
- * the to_link buffer.
+ * Determine which instance has output for the
+ * link: mi itself, or else the next queued
+ * bcast/c2c packet, prepared in its to_link buffer.
  */
 static struct multi_instance *
-multi_io_write_link_pre(struct multi_context *m)
+multi_io_write_link_pre(struct multi_context *m, struct multi_instance *mi)
 {
-    struct multi_instance *mi = NULL;
-
-    if (m->pending)
+    if (mi && LINK_OUT(&mi->context))
     {
-        mi = m->pending;
+        return mi;
     }
-    else if (mbuf_defined(m->mbuf))
+    if (mbuf_defined(m->mbuf))
     {
-        mi = multi_get_queue(m->mbuf);
+        return multi_get_queue(m->mbuf);
     }
-    return mi;
+    return NULL;
 }
 
 static bool
@@ -381,11 +344,12 @@ 
 }
 
 static bool
-multi_io_write_link(struct multi_context *m, bool defer, const unsigned int mpp_flags)
+multi_io_write_link(struct multi_context *m, struct multi_instance *mi, bool defer,
+                    const unsigned int mpp_flags)
 {
-    struct multi_instance *mi = multi_io_write_link_pre(m);
     bool ret = true;
 
+    mi = multi_io_write_link_pre(m, mi);
     if (mi)
     {
         if ((defer && !proto_is_dgram(mi->context.c2.link_sockets[0]->info.proto))
@@ -406,20 +370,12 @@ 
                 mbuf_free_buf(mb);
                 buf_reset(buf);
                 ret = multi_process_post(m, mi, mpp_flags);
-                if (!ret)
-                {
-                    mi = NULL;
-                }
                 clear_prefix();
             }
         }
         else
         {
             ret = multi_io_send_link(m, mi, mpp_flags);
-            if (!ret)
-            {
-                mi = NULL;
-            }
         }
     }
     return ret;
@@ -477,11 +433,13 @@ 
             break;
 
         case TA_TUN_WRITE:
-            multi_io_write_tun(m, mpp_flags);
+            ASSERT(mi);
+            multi_io_write_tun(m, mi, mpp_flags);
             break;
 
         case TA_TUN_WRITE_TIMEOUT:
-            multi_io_drop_tun(m, mpp_flags);
+            ASSERT(mi);
+            multi_io_drop_tun(m, mi, mpp_flags);
             break;
 
         case TA_SOCKET_WRITE_READY:
@@ -490,11 +448,11 @@ 
             break;
 
         case TA_SOCKET_WRITE:
-            multi_io_write_link(m, false, mpp_flags);
+            multi_io_write_link(m, mi, false, mpp_flags);
             break;
 
         case TA_SOCKET_WRITE_DEFERRED:
-            multi_io_write_link(m, true, mpp_flags);
+            multi_io_write_link(m, mi, true, mpp_flags);
             break;
 
         case TA_INITIAL:
@@ -629,10 +587,6 @@ 
                             multi_io_action(m, mi, NULL, TA_INITIAL, false);
                         }
                     }
-                    else if (e->rwflags & EVENT_WRITE)
-                    {
-                        multi_io_action(m, NULL, NULL, TA_SOCKET_WRITE, false);
-                    }
                     else if (e->rwflags & EVENT_READ)
                     {
                         multi_io_action(m, NULL, ev_arg->u.sock, TA_SOCKET_READ, false);
@@ -653,11 +607,7 @@ 
                 /* incoming data on TUN? */
                 if (e->arg == MULTI_IO_TUN)
                 {
-                    if (e->rwflags & EVENT_WRITE)
-                    {
-                        multi_io_action(m, NULL, NULL, TA_TUN_WRITE, false);
-                    }
-                    else if (e->rwflags & EVENT_READ)
+                    if (e->rwflags & EVENT_READ)
                     {
                         multi_io_action(m, NULL, NULL, TA_TUN_READ, false);
                     }
@@ -749,17 +699,17 @@ 
                 mi = NULL;
             }
             multi_close_instance_on_signal(m, touched);
+            touched = NULL;
         }
 
-
         /*
-         * If dispatch produced any pending output
-         * for a particular instance, point to
-         * that instance.
+         * touched is the only instance this action
+         * post-processed: having closed it above if
+         * it was signalled, write its output if any.
          */
-        if (m->pending)
+        if (touched && ANY_OUT(&touched->context))
         {
-            mi = m->pending;
+            mi = touched;
         }
 
         /*