diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index d70245f..7535797 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -3302,6 +3302,155 @@
 }
 #endif /* if defined(ENABLE_DCO) */
 
+/**
+ * Process incoming data packet from clients.
+ *
+ * This tries to decrypt and then forward incoming
+ * data packets.
+ */
+static void
+multi_process_incoming_link_data(struct multi_context *m, 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;
+
+    orig_buf = c->c2.buf.data;
+    if (process_incoming_link_part1(c, lsi, floated))
+    {
+        /* nonzero length means that we have a valid, decrypted packed */
+        if (floated && c->c2.buf.len > 0)
+        {
+            multi_process_float(m, m->pending, sock);
+        }
+
+        process_incoming_link_part2(c, lsi, orig_buf);
+    }
+
+    if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN)
+    {
+        struct mroute_addr src, dest;
+        /* extract packet source and dest addresses */
+        unsigned int mroute_flags =
+            mroute_extract_addr_from_packet(&src, &dest, 0, &c->c2.to_tun, DEV_TYPE_TUN);
+
+        /* drop packet if extract failed */
+        if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
+        {
+            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)
+        {
+            /* IPv6 link-local address (fe80::xxx)? */
+            if ((src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
+                && IN6_IS_ADDR_LINKLOCAL(&src.v6.addr))
+            {
+                /* do nothing, for now.  TODO: add address learning */
+            }
+            else
+            {
+                struct gc_arena gc = gc_new();
+                msg(D_MULTI_DROPPED,
+                    "MULTI: bad source address from client [%s], packet dropped",
+                    mroute_addr_print(&src, &gc));
+                gc_free(&gc);
+            }
+            c->c2.to_tun.len = 0;
+        }
+        /* client-to-client communication enabled? */
+        else if (m->enable_c2c)
+        {
+            /* multicast? */
+            if (mroute_flags & MROUTE_EXTRACT_MCAST)
+            {
+                /* for now, treat multicast as broadcast */
+                multi_bcast(m, &c->c2.to_tun, m->pending, 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);
+
+                /* if dest addr is a known client, route to it */
+                if (mi)
+                {
+                    {
+                        multi_unicast(m, &c->c2.to_tun, mi);
+                        register_activity(c, BLEN(&c->c2.to_tun));
+                    }
+                    c->c2.to_tun.len = 0;
+                }
+            }
+        }
+    }
+    else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP)
+    {
+        uint16_t vid = 0;
+
+        if (m->top.options.vlan_tagging)
+        {
+            if (vlan_is_tagged(&c->c2.to_tun))
+            {
+                /* Drop VLAN-tagged frame. */
+                msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame");
+                c->c2.to_tun.len = 0;
+            }
+            else
+            {
+                vid = c->options.vlan_pvid;
+            }
+        }
+        /* extract packet source and dest addresses */
+        struct mroute_addr src, dest;
+        /* extract packet source and dest addresses */
+        unsigned int mroute_flags =
+            mroute_extract_addr_from_packet(&src, &dest, vid, &c->c2.to_tun, DEV_TYPE_TAP);
+
+        if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
+        {
+            if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
+            {
+                /* 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);
+                    }
+                    else /* try client-to-client routing */
+                    {
+                        struct multi_instance *mi = multi_get_instance_by_virtual_addr(m, &dest, false);
+
+                        /* if dest addr is a known client, route to it */
+                        if (mi)
+                        {
+                            multi_unicast(m, &c->c2.to_tun, mi);
+                            register_activity(c, BLEN(&c->c2.to_tun));
+                            c->c2.to_tun.len = 0;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                struct gc_arena gc = gc_new();
+                msg(D_MULTI_DROPPED,
+                    "MULTI: bad source address from client [%s], packet dropped",
+                    mroute_addr_print(&src, &gc));
+                c->c2.to_tun.len = 0;
+                gc_free(&gc);
+            }
+        }
+        else
+        {
+            c->c2.to_tun.len = 0;
+        }
+    }
+}
+
 /*
  * Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
  * i.e. client -> server direction.
@@ -3311,9 +3460,6 @@
                             const unsigned int mpp_flags, struct link_socket *sock)
 {
     struct context *c;
-    struct mroute_addr src, dest;
-    unsigned int mroute_flags;
-    struct multi_instance *mi;
     bool ret = true;
     bool floated = false;
 
@@ -3357,141 +3503,7 @@
 
     if (BLEN(&c->c2.buf) > 0)
     {
-        struct link_socket_info *lsi;
-        const uint8_t *orig_buf;
-
-        /* decrypt in instance context */
-
-        lsi = &sock->info;
-        orig_buf = c->c2.buf.data;
-        if (process_incoming_link_part1(c, lsi, floated))
-        {
-            /* nonzero length means that we have a valid, decrypted packed */
-            if (floated && c->c2.buf.len > 0)
-            {
-                multi_process_float(m, m->pending, sock);
-            }
-
-            process_incoming_link_part2(c, lsi, orig_buf);
-        }
-
-        if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN)
-        {
-            /* extract packet source and dest addresses */
-            mroute_flags =
-                mroute_extract_addr_from_packet(&src, &dest, 0, &c->c2.to_tun, DEV_TYPE_TUN);
-
-            /* drop packet if extract failed */
-            if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
-            {
-                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)
-            {
-                /* IPv6 link-local address (fe80::xxx)? */
-                if ((src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
-                    && IN6_IS_ADDR_LINKLOCAL(&src.v6.addr))
-                {
-                    /* do nothing, for now.  TODO: add address learning */
-                }
-                else
-                {
-                    struct gc_arena gc = gc_new();
-                    msg(D_MULTI_DROPPED,
-                        "MULTI: bad source address from client [%s], packet dropped",
-                        mroute_addr_print(&src, &gc));
-                    gc_free(&gc);
-                }
-                c->c2.to_tun.len = 0;
-            }
-            /* client-to-client communication enabled? */
-            else if (m->enable_c2c)
-            {
-                /* multicast? */
-                if (mroute_flags & MROUTE_EXTRACT_MCAST)
-                {
-                    /* for now, treat multicast as broadcast */
-                    multi_bcast(m, &c->c2.to_tun, m->pending, 0);
-                }
-                else /* possible client to client routing */
-                {
-                    ASSERT(!(mroute_flags & MROUTE_EXTRACT_BCAST));
-                    mi = multi_get_instance_by_virtual_addr(m, &dest, true);
-
-                    /* if dest addr is a known client, route to it */
-                    if (mi)
-                    {
-                        {
-                            multi_unicast(m, &c->c2.to_tun, mi);
-                            register_activity(c, BLEN(&c->c2.to_tun));
-                        }
-                        c->c2.to_tun.len = 0;
-                    }
-                }
-            }
-        }
-        else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP)
-        {
-            uint16_t vid = 0;
-
-            if (m->top.options.vlan_tagging)
-            {
-                if (vlan_is_tagged(&c->c2.to_tun))
-                {
-                    /* Drop VLAN-tagged frame. */
-                    msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame");
-                    c->c2.to_tun.len = 0;
-                }
-                else
-                {
-                    vid = c->options.vlan_pvid;
-                }
-            }
-            /* extract packet source and dest addresses */
-            mroute_flags =
-                mroute_extract_addr_from_packet(&src, &dest, vid, &c->c2.to_tun, DEV_TYPE_TAP);
-
-            if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
-            {
-                if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
-                {
-                    /* 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);
-                        }
-                        else /* try client-to-client routing */
-                        {
-                            mi = multi_get_instance_by_virtual_addr(m, &dest, false);
-
-                            /* if dest addr is a known client, route to it */
-                            if (mi)
-                            {
-                                multi_unicast(m, &c->c2.to_tun, mi);
-                                register_activity(c, BLEN(&c->c2.to_tun));
-                                c->c2.to_tun.len = 0;
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    struct gc_arena gc = gc_new();
-                    msg(D_MULTI_DROPPED,
-                        "MULTI: bad source address from client [%s], packet dropped",
-                        mroute_addr_print(&src, &gc));
-                    c->c2.to_tun.len = 0;
-                    gc_free(&gc);
-                }
-            }
-            else
-            {
-                c->c2.to_tun.len = 0;
-            }
-        }
+        multi_process_incoming_link_data(m, floated, sock);
     }
 
     /* postprocess and set wakeup */
