udp: remove connected udp transport proto
[vpp.git] / src / vnet / udp / udp.c
1 /*
2  * Copyright (c) 2016-2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/udp/udp.h>
17 #include <vnet/session/session.h>
18 #include <vnet/dpo/load_balance.h>
19 #include <vnet/fib/ip4_fib.h>
20 #include <vppinfra/sparse_vec.h>
21
22 udp_main_t udp_main;
23
24 static void
25 udp_connection_register_port (vlib_main_t * vm, u16 lcl_port, u8 is_ip4)
26 {
27   udp_main_t *um = &udp_main;
28   udp_dst_port_info_t *pi;
29   u16 *n;
30
31   pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
32   if (!pi)
33     {
34       udp_add_dst_port (um, lcl_port, 0, is_ip4);
35       pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
36       pi->n_connections = 1;
37     }
38   else
39     {
40       pi->n_connections += 1;
41       /* Do not return. The fact that the pi is valid does not mean
42        * it's up to date */
43     }
44
45   pi->node_index = is_ip4 ? udp4_input_node.index : udp6_input_node.index;
46   pi->next_index = um->local_to_input_edge[is_ip4];
47
48   /* Setup udp protocol -> next index sparse vector mapping. */
49   if (is_ip4)
50     n = sparse_vec_validate (um->next_by_dst_port4,
51                              clib_host_to_net_u16 (lcl_port));
52   else
53     n = sparse_vec_validate (um->next_by_dst_port6,
54                              clib_host_to_net_u16 (lcl_port));
55
56   n[0] = pi->next_index;
57 }
58
59 static void
60 udp_connection_unregister_port (u16 lcl_port, u8 is_ip4)
61 {
62   udp_main_t *um = &udp_main;
63   udp_dst_port_info_t *pi;
64
65   pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
66   if (!pi)
67     return;
68
69   if (!pi->n_connections)
70     {
71       clib_warning ("no connections using port %u", lcl_port);
72       return;
73     }
74
75   if (!clib_atomic_sub_fetch (&pi->n_connections, 1))
76     udp_unregister_dst_port (0, lcl_port, is_ip4);
77 }
78
79 void
80 udp_connection_share_port (u16 lcl_port, u8 is_ip4)
81 {
82   udp_main_t *um = &udp_main;
83   udp_dst_port_info_t *pi;
84
85   /* Done without a lock but the operation is atomic. Writers to pi hash
86    * table and vector should be guarded by a barrier sync */
87   pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
88   clib_atomic_fetch_add_rel (&pi->n_connections, 1);
89 }
90
91 udp_connection_t *
92 udp_connection_alloc (u32 thread_index)
93 {
94   udp_main_t *um = &udp_main;
95   udp_connection_t *uc;
96   u32 will_expand = 0;
97   pool_get_aligned_will_expand (um->connections[thread_index], will_expand,
98                                 CLIB_CACHE_LINE_BYTES);
99
100   if (PREDICT_FALSE (will_expand))
101     {
102       clib_spinlock_lock_if_init (&udp_main.peekers_write_locks
103                                   [thread_index]);
104       pool_get_aligned (udp_main.connections[thread_index], uc,
105                         CLIB_CACHE_LINE_BYTES);
106       clib_spinlock_unlock_if_init (&udp_main.peekers_write_locks
107                                     [thread_index]);
108     }
109   else
110     {
111       pool_get_aligned (um->connections[thread_index], uc,
112                         CLIB_CACHE_LINE_BYTES);
113     }
114   clib_memset (uc, 0, sizeof (*uc));
115   uc->c_c_index = uc - um->connections[thread_index];
116   uc->c_thread_index = thread_index;
117   uc->c_proto = TRANSPORT_PROTO_UDP;
118   clib_spinlock_init (&uc->rx_lock);
119   return uc;
120 }
121
122 void
123 udp_connection_free (udp_connection_t * uc)
124 {
125   u32 thread_index = uc->c_thread_index;
126   if (CLIB_DEBUG)
127     clib_memset (uc, 0xFA, sizeof (*uc));
128   pool_put (udp_main.connections[thread_index], uc);
129 }
130
131 static void
132 udp_connection_cleanup (udp_connection_t * uc)
133 {
134   transport_endpoint_cleanup (TRANSPORT_PROTO_UDP, &uc->c_lcl_ip,
135                               uc->c_lcl_port);
136   udp_connection_unregister_port (clib_net_to_host_u16 (uc->c_lcl_port),
137                                   uc->c_is_ip4);
138   udp_connection_free (uc);
139 }
140
141 void
142 udp_connection_delete (udp_connection_t * uc)
143 {
144   session_transport_delete_notify (&uc->connection);
145   udp_connection_cleanup (uc);
146 }
147
148 static u8
149 udp_connection_port_used_extern (u16 lcl_port, u8 is_ip4)
150 {
151   udp_main_t *um = vnet_get_udp_main ();
152   udp_dst_port_info_t *pi;
153
154   pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
155   return (pi && !pi->n_connections
156           && udp_is_valid_dst_port (lcl_port, is_ip4));
157 }
158
159 static u16
160 udp_default_mtu (udp_main_t * um, u8 is_ip4)
161 {
162   u16 ip_hlen = is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t);
163   return (um->default_mtu - sizeof (udp_header_t) - ip_hlen);
164 }
165
166 static u32
167 udp_session_bind (u32 session_index, transport_endpoint_t * lcl)
168 {
169   udp_main_t *um = vnet_get_udp_main ();
170   vlib_main_t *vm = vlib_get_main ();
171   transport_endpoint_cfg_t *lcl_ext;
172   udp_connection_t *listener;
173   u16 lcl_port_ho;
174   void *iface_ip;
175
176   lcl_port_ho = clib_net_to_host_u16 (lcl->port);
177
178   if (udp_connection_port_used_extern (lcl_port_ho, lcl->is_ip4))
179     {
180       clib_warning ("port already used");
181       return SESSION_E_PORTINUSE;
182     }
183
184   pool_get (um->listener_pool, listener);
185   clib_memset (listener, 0, sizeof (udp_connection_t));
186
187   listener->c_lcl_port = lcl->port;
188   listener->c_c_index = listener - um->listener_pool;
189
190   /* If we are provided a sw_if_index, bind using one of its ips */
191   if (ip_is_zero (&lcl->ip, 1) && lcl->sw_if_index != ENDPOINT_INVALID_INDEX)
192     {
193       if ((iface_ip = ip_interface_get_first_ip (lcl->sw_if_index,
194                                                  lcl->is_ip4)))
195         ip_set (&lcl->ip, iface_ip, lcl->is_ip4);
196     }
197   ip_copy (&listener->c_lcl_ip, &lcl->ip, lcl->is_ip4);
198   listener->c_is_ip4 = lcl->is_ip4;
199   listener->c_proto = TRANSPORT_PROTO_UDP;
200   listener->c_s_index = session_index;
201   listener->c_fib_index = lcl->fib_index;
202   listener->mss = udp_default_mtu (um, listener->c_is_ip4);
203   listener->flags |= UDP_CONN_F_OWNS_PORT | UDP_CONN_F_LISTEN;
204   lcl_ext = (transport_endpoint_cfg_t *) lcl;
205   if (lcl_ext->transport_flags & TRANSPORT_CFG_F_CONNECTED)
206     listener->flags |= UDP_CONN_F_CONNECTED;
207   else
208     listener->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
209   clib_spinlock_init (&listener->rx_lock);
210
211   udp_connection_register_port (vm, lcl_port_ho, lcl->is_ip4);
212   return listener->c_c_index;
213 }
214
215 static u32
216 udp_session_unbind (u32 listener_index)
217 {
218   udp_main_t *um = &udp_main;
219   udp_connection_t *listener;
220
221   listener = udp_listener_get (listener_index);
222   udp_connection_unregister_port (clib_net_to_host_u16 (listener->c_lcl_port),
223                                   listener->c_is_ip4);
224   pool_put (um->listener_pool, listener);
225   return 0;
226 }
227
228 static transport_connection_t *
229 udp_session_get_listener (u32 listener_index)
230 {
231   udp_connection_t *us;
232
233   us = udp_listener_get (listener_index);
234   return &us->connection;
235 }
236
237 static u32
238 udp_push_header (transport_connection_t * tc, vlib_buffer_t * b)
239 {
240   udp_connection_t *uc;
241   vlib_main_t *vm = vlib_get_main ();
242
243   uc = udp_connection_from_transport (tc);
244
245   vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port, 1);
246   if (tc->is_ip4)
247     vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4,
248                                  IP_PROTOCOL_UDP, 1 /* csum offload */ ,
249                                  0 /* is_df */ );
250   else
251     vlib_buffer_push_ip6 (vm, b, &uc->c_lcl_ip6, &uc->c_rmt_ip6,
252                           IP_PROTOCOL_UDP);
253   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
254   vnet_buffer (b)->sw_if_index[VLIB_TX] = uc->c_fib_index;
255   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
256
257   if (PREDICT_FALSE (uc->flags & UDP_CONN_F_CLOSING))
258     {
259       if (!transport_max_tx_dequeue (&uc->connection))
260         udp_connection_delete (uc);
261     }
262
263   return 0;
264 }
265
266 static transport_connection_t *
267 udp_session_get (u32 connection_index, u32 thread_index)
268 {
269   udp_connection_t *uc;
270   uc = udp_connection_get (connection_index, thread_index);
271   if (uc)
272     return &uc->connection;
273   return 0;
274 }
275
276 static void
277 udp_session_close (u32 connection_index, u32 thread_index)
278 {
279   udp_connection_t *uc;
280
281   uc = udp_connection_get (connection_index, thread_index);
282   if (!uc)
283     return;
284
285   if (!transport_max_tx_dequeue (&uc->connection))
286     udp_connection_delete (uc);
287   else
288     uc->flags |= UDP_CONN_F_CLOSING;
289 }
290
291 static void
292 udp_session_cleanup (u32 connection_index, u32 thread_index)
293 {
294   udp_connection_t *uc;
295   uc = udp_connection_get (connection_index, thread_index);
296   if (!uc)
297     return;
298   if (uc->flags & UDP_CONN_F_MIGRATED)
299     udp_connection_free (uc);
300   else
301     udp_connection_cleanup (uc);
302 }
303
304 static int
305 udp_session_send_params (transport_connection_t * tconn,
306                          transport_send_params_t * sp)
307 {
308   udp_connection_t *uc;
309
310   uc = udp_connection_from_transport (tconn);
311
312   /* No constraint on TX window */
313   sp->snd_space = ~0;
314   /* TODO figure out MTU of output interface */
315   sp->snd_mss = uc->mss;
316   sp->tx_offset = 0;
317   sp->flags = 0;
318   return 0;
319 }
320
321 static int
322 udp_open_connection (transport_endpoint_cfg_t * rmt)
323 {
324   vlib_main_t *vm = vlib_get_main ();
325   u32 thread_index = vm->thread_index;
326   udp_main_t *um = &udp_main;
327   ip46_address_t lcl_addr;
328   udp_connection_t *uc;
329   u16 lcl_port;
330   int rv;
331
332   rv = transport_alloc_local_endpoint (TRANSPORT_PROTO_UDP, rmt, &lcl_addr,
333                                        &lcl_port);
334   if (rv)
335     {
336       if (rv != SESSION_E_PORTINUSE)
337         return rv;
338
339       if (udp_connection_port_used_extern (lcl_port, rmt->is_ip4))
340         return SESSION_E_PORTINUSE;
341
342       /* If port in use, check if 5-tuple is also in use */
343       if (session_lookup_connection (rmt->fib_index, &lcl_addr, &rmt->ip,
344                                      lcl_port, rmt->port, TRANSPORT_PROTO_UDP,
345                                      rmt->is_ip4))
346         return SESSION_E_PORTINUSE;
347
348       /* 5-tuple is available so increase lcl endpoint refcount and proceed
349        * with connection allocation */
350       transport_share_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
351                                       lcl_port);
352       goto conn_alloc;
353     }
354
355   if (udp_is_valid_dst_port (lcl_port, rmt->is_ip4))
356     {
357       /* If specific source port was requested abort */
358       if (rmt->peer.port)
359         return SESSION_E_PORTINUSE;
360
361       /* Try to find a port that's not used */
362       while (udp_is_valid_dst_port (lcl_port, rmt->is_ip4))
363         {
364           lcl_port = transport_alloc_local_port (TRANSPORT_PROTO_UDP,
365                                                  &lcl_addr);
366           if (lcl_port < 1)
367             return SESSION_E_PORTINUSE;
368         }
369     }
370
371 conn_alloc:
372
373   udp_connection_register_port (vm, lcl_port, rmt->is_ip4);
374
375   /* We don't poll main thread if we have workers */
376   if (vlib_num_workers ())
377     thread_index = 1;
378
379   uc = udp_connection_alloc (thread_index);
380   ip_copy (&uc->c_rmt_ip, &rmt->ip, rmt->is_ip4);
381   ip_copy (&uc->c_lcl_ip, &lcl_addr, rmt->is_ip4);
382   uc->c_rmt_port = rmt->port;
383   uc->c_lcl_port = clib_host_to_net_u16 (lcl_port);
384   uc->c_is_ip4 = rmt->is_ip4;
385   uc->c_proto = TRANSPORT_PROTO_UDP;
386   uc->c_fib_index = rmt->fib_index;
387   uc->mss = rmt->mss ? rmt->mss : udp_default_mtu (um, uc->c_is_ip4);
388   uc->flags |= UDP_CONN_F_OWNS_PORT;
389   if (rmt->transport_flags & TRANSPORT_CFG_F_CONNECTED)
390     uc->flags |= UDP_CONN_F_CONNECTED;
391   else
392     uc->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
393
394   return uc->c_c_index;
395 }
396
397 static transport_connection_t *
398 udp_session_get_half_open (u32 conn_index)
399 {
400   udp_connection_t *uc;
401   u32 thread_index;
402
403   /* We don't poll main thread if we have workers */
404   thread_index = vlib_num_workers ()? 1 : 0;
405   uc = udp_connection_get (conn_index, thread_index);
406   if (!uc)
407     return 0;
408   return &uc->connection;
409 }
410
411 static u8 *
412 format_udp_session (u8 * s, va_list * args)
413 {
414   u32 uci = va_arg (*args, u32);
415   u32 thread_index = va_arg (*args, u32);
416   u32 verbose = va_arg (*args, u32);
417   udp_connection_t *uc;
418
419   uc = udp_connection_get (uci, thread_index);
420   return format (s, "%U", format_udp_connection, uc, verbose);
421 }
422
423 static u8 *
424 format_udp_half_open_session (u8 * s, va_list * args)
425 {
426   u32 __clib_unused tci = va_arg (*args, u32);
427   u32 __clib_unused thread_index = va_arg (*args, u32);
428   clib_warning ("BUG");
429   return 0;
430 }
431
432 static u8 *
433 format_udp_listener_session (u8 * s, va_list * args)
434 {
435   u32 tci = va_arg (*args, u32);
436   u32 __clib_unused thread_index = va_arg (*args, u32);
437   u32 verbose = va_arg (*args, u32);
438   udp_connection_t *uc = udp_listener_get (tci);
439   return format (s, "%U", format_udp_connection, uc, verbose);
440 }
441
442 /* *INDENT-OFF* */
443 static const transport_proto_vft_t udp_proto = {
444   .start_listen = udp_session_bind,
445   .connect = udp_open_connection,
446   .stop_listen = udp_session_unbind,
447   .push_header = udp_push_header,
448   .get_connection = udp_session_get,
449   .get_listener = udp_session_get_listener,
450   .get_half_open = udp_session_get_half_open,
451   .close = udp_session_close,
452   .cleanup = udp_session_cleanup,
453   .send_params = udp_session_send_params,
454   .format_connection = format_udp_session,
455   .format_half_open = format_udp_half_open_session,
456   .format_listener = format_udp_listener_session,
457   .transport_options = {
458     .name = "udp",
459     .short_name = "U",
460     .tx_type = TRANSPORT_TX_DGRAM,
461     .service_type = TRANSPORT_SERVICE_CL,
462   },
463 };
464 /* *INDENT-ON* */
465
466 static clib_error_t *
467 udp_init (vlib_main_t * vm)
468 {
469   udp_main_t *um = vnet_get_udp_main ();
470   ip_main_t *im = &ip_main;
471   vlib_thread_main_t *tm = vlib_get_thread_main ();
472   u32 num_threads;
473   ip_protocol_info_t *pi;
474   int i;
475
476   /*
477    * Registrations
478    */
479
480   /* IP registration */
481   pi = ip_get_protocol_info (im, IP_PROTOCOL_UDP);
482   if (pi == 0)
483     return clib_error_return (0, "UDP protocol info AWOL");
484   pi->format_header = format_udp_header;
485   pi->unformat_pg_edit = unformat_pg_udp_header;
486
487   /* Register as transport with URI */
488   transport_register_protocol (TRANSPORT_PROTO_UDP, &udp_proto,
489                                FIB_PROTOCOL_IP4, ip4_lookup_node.index);
490   transport_register_protocol (TRANSPORT_PROTO_UDP, &udp_proto,
491                                FIB_PROTOCOL_IP6, ip6_lookup_node.index);
492
493   /*
494    * Initialize data structures
495    */
496
497   num_threads = 1 /* main thread */  + tm->n_threads;
498   vec_validate (um->connections, num_threads - 1);
499   vec_validate (um->connection_peekers, num_threads - 1);
500   vec_validate (um->peekers_readers_locks, num_threads - 1);
501   vec_validate (um->peekers_write_locks, num_threads - 1);
502
503   if (num_threads > 1)
504     for (i = 0; i < num_threads; i++)
505       {
506         clib_spinlock_init (&um->peekers_readers_locks[i]);
507         clib_spinlock_init (&um->peekers_write_locks[i]);
508       }
509
510   um->local_to_input_edge[UDP_IP4] =
511     vlib_node_add_next (vm, udp4_local_node.index, udp4_input_node.index);
512   um->local_to_input_edge[UDP_IP6] =
513     vlib_node_add_next (vm, udp6_local_node.index, udp6_input_node.index);
514
515   um->default_mtu = 1500;
516   return 0;
517 }
518
519 /* *INDENT-OFF* */
520 VLIB_INIT_FUNCTION (udp_init) =
521 {
522   .runs_after = VLIB_INITS("ip_main_init", "ip4_lookup_init",
523                            "ip6_lookup_init"),
524 };
525 /* *INDENT-ON* */
526
527 /*
528  * fd.io coding-style-patch-verification: ON
529  *
530  * Local Variables:
531  * eval: (c-set-style "gnu")
532  * End:
533  */