udp: use transport port refcnt on accept
[vpp.git] / src / vnet / udp / udp.c
index 710d1ac..0a0247a 100644 (file)
@@ -38,6 +38,17 @@ udp_connection_register_port (u16 lcl_port, u8 is_ip4)
     n = sparse_vec_validate (um->next_by_dst_port6, lcl_port);
 
   n[0] = um->local_to_input_edge[is_ip4];
+
+  __atomic_add_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
+                     __ATOMIC_RELAXED);
+}
+
+void
+udp_connection_share_port (u16 lcl_port, u8 is_ip4)
+{
+  udp_main_t *um = &udp_main;
+  __atomic_add_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
+                     __ATOMIC_RELAXED);
 }
 
 static void
@@ -46,6 +57,11 @@ udp_connection_unregister_port (u16 lcl_port, u8 is_ip4)
   udp_main_t *um = &udp_main;
   u16 *n;
 
+  /* Needed because listeners are not tracked as local endpoints */
+  if (__atomic_sub_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
+                         __ATOMIC_RELAXED))
+    return;
+
   if (is_ip4)
     n = sparse_vec_validate (um->next_by_dst_port4, lcl_port);
   else
@@ -83,10 +99,9 @@ udp_connection_free (udp_connection_t * uc)
 static void
 udp_connection_cleanup (udp_connection_t * uc)
 {
-  /* Unregister port from udp_local only if refcount went to zero */
-  if (!transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &uc->c_lcl_ip,
-                                        uc->c_lcl_port))
-    udp_connection_unregister_port (uc->c_lcl_port, uc->c_is_ip4);
+  transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &uc->c_lcl_ip,
+                                   uc->c_lcl_port);
+  udp_connection_unregister_port (uc->c_lcl_port, uc->c_is_ip4);
   udp_connection_free (uc);
 }
 
@@ -218,32 +233,68 @@ udp_session_get_listener (u32 listener_index)
 }
 
 always_inline u32
-udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b)
+udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b,
+                    u8 is_cless)
 {
-  vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port,
-                       udp_csum_offload (uc));
   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
   /* reuse tcp medatada for now */
   vnet_buffer (b)->tcp.connection_index = uc->c_c_index;
 
+  if (!is_cless)
+    {
+      vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port,
+                           udp_csum_offload (uc));
+
+      if (uc->c_is_ip4)
+       vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4,
+                                    IP_PROTOCOL_UDP, udp_csum_offload (uc),
+                                    0 /* is_df */, uc->c_dscp);
+      else
+       vlib_buffer_push_ip6 (vm, b, &uc->c_lcl_ip6, &uc->c_rmt_ip6,
+                             IP_PROTOCOL_UDP);
+
+      vnet_buffer (b)->tcp.flags = 0;
+    }
+  else
+    {
+      u8 *data = vlib_buffer_get_current (b);
+      session_dgram_hdr_t hdr;
+
+      hdr = *(session_dgram_hdr_t *) (data - sizeof (hdr));
+
+      /* Local port assumed to be bound, not overwriting it */
+      vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port,
+                           udp_csum_offload (uc));
+
+      if (uc->c_is_ip4)
+       vlib_buffer_push_ip4_custom (vm, b, &hdr.lcl_ip.ip4, &hdr.rmt_ip.ip4,
+                                    IP_PROTOCOL_UDP, udp_csum_offload (uc),
+                                    0 /* is_df */, uc->c_dscp);
+      else
+       vlib_buffer_push_ip6 (vm, b, &hdr.lcl_ip.ip6, &hdr.rmt_ip.ip6,
+                             IP_PROTOCOL_UDP);
+
+      /* Not connected udp session. Mark buffer for custom handling in
+       * udp_output */
+      vnet_buffer (b)->tcp.flags |= UDP_CONN_F_LISTEN;
+    }
+
   return 0;
 }
 
-static u32
-udp_push_header (transport_connection_t *tc, vlib_buffer_t **bs, u32 n_bufs)
+always_inline void
+udp_push_header_batch (udp_connection_t *uc, vlib_buffer_t **bs, u32 n_bufs,
+                      u8 is_cless)
 {
   vlib_main_t *vm = vlib_get_main ();
-  udp_connection_t *uc;
-
-  uc = udp_connection_from_transport (tc);
 
   while (n_bufs >= 4)
     {
       vlib_prefetch_buffer_header (bs[2], STORE);
       vlib_prefetch_buffer_header (bs[3], STORE);
 
-      udp_push_one_header (vm, uc, bs[0]);
-      udp_push_one_header (vm, uc, bs[1]);
+      udp_push_one_header (vm, uc, bs[0], is_cless);
+      udp_push_one_header (vm, uc, bs[1], is_cless);
 
       n_bufs -= 2;
       bs += 2;
@@ -253,15 +304,27 @@ udp_push_header (transport_connection_t *tc, vlib_buffer_t **bs, u32 n_bufs)
       if (n_bufs > 1)
        vlib_prefetch_buffer_header (bs[1], STORE);
 
-      udp_push_one_header (vm, uc, bs[0]);
+      udp_push_one_header (vm, uc, bs[0], is_cless);
 
       n_bufs -= 1;
       bs += 1;
     }
+}
+
+static u32
+udp_push_header (transport_connection_t *tc, vlib_buffer_t **bs, u32 n_bufs)
+{
+  udp_connection_t *uc;
+
+  uc = udp_connection_from_transport (tc);
+  if (uc->flags & UDP_CONN_F_CONNECTED)
+    udp_push_header_batch (uc, bs, n_bufs, 0 /* is_cless */);
+  else
+    udp_push_header_batch (uc, bs, n_bufs, 1 /* is_cless */);
 
   if (PREDICT_FALSE (uc->flags & UDP_CONN_F_CLOSING))
     {
-      if (!transport_max_tx_dequeue (&uc->connection))
+      if (!transport_tx_fifo_has_dgram (&uc->connection))
        udp_connection_program_cleanup (uc);
     }
 
@@ -287,7 +350,7 @@ udp_session_close (u32 connection_index, u32 thread_index)
   if (!uc || (uc->flags & UDP_CONN_F_MIGRATED))
     return;
 
-  if (!transport_max_tx_dequeue (&uc->connection))
+  if (!transport_tx_fifo_has_dgram (&uc->connection))
     udp_connection_program_cleanup (uc);
   else
     uc->flags |= UDP_CONN_F_CLOSING;
@@ -336,44 +399,32 @@ udp_open_connection (transport_endpoint_cfg_t * rmt)
   rv = transport_alloc_local_endpoint (TRANSPORT_PROTO_UDP, rmt, &lcl_addr,
                                       &lcl_port);
   if (rv)
-    {
-      if (rv != SESSION_E_PORTINUSE)
-       return rv;
-
-      if (udp_connection_port_used_extern (lcl_port, rmt->is_ip4))
-       return SESSION_E_PORTINUSE;
-
-      /* If port in use, check if 5-tuple is also in use */
-      if (session_lookup_connection (rmt->fib_index, &lcl_addr, &rmt->ip,
-                                    lcl_port, rmt->port, TRANSPORT_PROTO_UDP,
-                                    rmt->is_ip4))
-       return SESSION_E_PORTINUSE;
-
-      /* 5-tuple is available so increase lcl endpoint refcount and proceed
-       * with connection allocation */
-      transport_share_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
-                                     lcl_port);
-      goto conn_alloc;
-    }
+    return rv;
 
-  if (udp_is_valid_dst_port (lcl_port, rmt->is_ip4))
+  if (udp_connection_port_used_extern (clib_net_to_host_u16 (lcl_port),
+                                      rmt->is_ip4))
     {
       /* If specific source port was requested abort */
       if (rmt->peer.port)
-       return SESSION_E_PORTINUSE;
+       {
+         transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
+                                           lcl_port);
+         return SESSION_E_PORTINUSE;
+       }
 
       /* Try to find a port that's not used */
-      while (udp_is_valid_dst_port (lcl_port, rmt->is_ip4))
+      while (udp_connection_port_used_extern (clib_net_to_host_u16 (lcl_port),
+                                             rmt->is_ip4))
        {
-         lcl_port = transport_alloc_local_port (TRANSPORT_PROTO_UDP,
-                                                &lcl_addr);
+         transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
+                                           lcl_port);
+         lcl_port =
+           transport_alloc_local_port (TRANSPORT_PROTO_UDP, &lcl_addr, rmt);
          if (lcl_port < 1)
            return SESSION_E_PORTINUSE;
        }
     }
 
-conn_alloc:
-
   /* We don't poll main thread if we have workers */
   thread_index = transport_cl_thread ();
 
@@ -530,6 +581,9 @@ udp_enable_disable (vlib_main_t *vm, u8 is_en)
   udp_realloc_ports_sv (&um->next_by_dst_port4);
   udp_realloc_ports_sv (&um->next_by_dst_port6);
 
+  vec_validate (um->transport_ports_refcnt[0], 65535);
+  vec_validate (um->transport_ports_refcnt[1], 65535);
+
   return 0;
 }