udp: fix csum computation when offload disabled
[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/ip/ip4_inlines.h>
20 #include <vnet/ip/ip6_inlines.h>
21 #include <vppinfra/sparse_vec.h>
22
23 udp_main_t udp_main;
24
25 static void
26 udp_connection_register_port (u16 lcl_port, u8 is_ip4)
27 {
28   udp_main_t *um = &udp_main;
29   u16 *n;
30
31   /* Setup udp protocol -> next index sparse vector mapping. Do not setup
32    * udp_dst_port_info_t as that is used to distinguish between external
33    * and transport consumed ports */
34
35   if (is_ip4)
36     n = sparse_vec_validate (um->next_by_dst_port4, lcl_port);
37   else
38     n = sparse_vec_validate (um->next_by_dst_port6, lcl_port);
39
40   n[0] = um->local_to_input_edge[is_ip4];
41
42   __atomic_add_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
43                       __ATOMIC_RELAXED);
44 }
45
46 void
47 udp_connection_share_port (u16 lcl_port, u8 is_ip4)
48 {
49   udp_main_t *um = &udp_main;
50   __atomic_add_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
51                       __ATOMIC_RELAXED);
52 }
53
54 static void
55 udp_connection_unregister_port (u16 lcl_port, u8 is_ip4)
56 {
57   udp_main_t *um = &udp_main;
58   u16 *n;
59
60   /* Needed because listeners are not tracked as local endpoints */
61   if (__atomic_sub_fetch (&um->transport_ports_refcnt[is_ip4][lcl_port], 1,
62                           __ATOMIC_RELAXED))
63     return;
64
65   if (is_ip4)
66     n = sparse_vec_validate (um->next_by_dst_port4, lcl_port);
67   else
68     n = sparse_vec_validate (um->next_by_dst_port6, lcl_port);
69
70   n[0] = UDP_NO_NODE_SET;
71 }
72
73 udp_connection_t *
74 udp_connection_alloc (u32 thread_index)
75 {
76   udp_worker_t *wrk = udp_worker_get (thread_index);
77   udp_connection_t *uc;
78
79   pool_get_aligned_safe (wrk->connections, uc, CLIB_CACHE_LINE_BYTES);
80
81   clib_memset (uc, 0, sizeof (*uc));
82   uc->c_c_index = uc - wrk->connections;
83   uc->c_thread_index = thread_index;
84   uc->c_proto = TRANSPORT_PROTO_UDP;
85   return uc;
86 }
87
88 void
89 udp_connection_free (udp_connection_t * uc)
90 {
91   udp_worker_t *wrk = udp_worker_get (uc->c_thread_index);
92
93   clib_spinlock_free (&uc->rx_lock);
94   if (CLIB_DEBUG)
95     clib_memset (uc, 0xFA, sizeof (*uc));
96   pool_put (wrk->connections, uc);
97 }
98
99 static void
100 udp_connection_cleanup (udp_connection_t * uc)
101 {
102   transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &uc->c_lcl_ip,
103                                     uc->c_lcl_port);
104   udp_connection_unregister_port (uc->c_lcl_port, uc->c_is_ip4);
105   udp_connection_free (uc);
106 }
107
108 void
109 udp_connection_delete (udp_connection_t * uc)
110 {
111   session_transport_delete_notify (&uc->connection);
112   udp_connection_cleanup (uc);
113 }
114
115 static void
116 udp_handle_cleanups (void *args)
117 {
118   u32 thread_index = (u32) pointer_to_uword (args);
119   udp_connection_t *uc;
120   udp_worker_t *wrk;
121   u32 *uc_index;
122
123   wrk = udp_worker_get (thread_index);
124   vec_foreach (uc_index, wrk->pending_cleanups)
125     {
126       uc = udp_connection_get (*uc_index, thread_index);
127       udp_connection_delete (uc);
128     }
129   vec_reset_length (wrk->pending_cleanups);
130 }
131
132 static void
133 udp_connection_program_cleanup (udp_connection_t *uc)
134 {
135   uword thread_index = uc->c_thread_index;
136   udp_worker_t *wrk;
137
138   wrk = udp_worker_get (uc->c_thread_index);
139   vec_add1 (wrk->pending_cleanups, uc->c_c_index);
140
141   if (vec_len (wrk->pending_cleanups) == 1)
142     session_send_rpc_evt_to_thread_force (
143       thread_index, udp_handle_cleanups,
144       uword_to_pointer (thread_index, void *));
145 }
146
147 static u8
148 udp_connection_port_used_extern (u16 lcl_port, u8 is_ip4)
149 {
150   udp_main_t *um = vnet_get_udp_main ();
151   udp_dst_port_info_t *pi;
152
153   pi = udp_get_dst_port_info (um, lcl_port, is_ip4);
154   return (pi && udp_is_valid_dst_port (lcl_port, is_ip4));
155 }
156
157 static u16
158 udp_default_mtu (udp_main_t * um, u8 is_ip4)
159 {
160   u16 ip_hlen = is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t);
161   return (um->default_mtu - sizeof (udp_header_t) - ip_hlen);
162 }
163
164 static u32
165 udp_session_bind (u32 session_index, transport_endpoint_cfg_t *lcl)
166 {
167   udp_main_t *um = vnet_get_udp_main ();
168   transport_endpoint_cfg_t *lcl_ext;
169   udp_connection_t *listener;
170   void *iface_ip;
171
172   if (udp_connection_port_used_extern (clib_net_to_host_u16 (lcl->port),
173                                        lcl->is_ip4))
174     {
175       clib_warning ("port already used");
176       return SESSION_E_PORTINUSE;
177     }
178
179   pool_get (um->listener_pool, listener);
180   clib_memset (listener, 0, sizeof (udp_connection_t));
181
182   listener->c_lcl_port = lcl->port;
183   listener->c_c_index = listener - um->listener_pool;
184
185   /* If we are provided a sw_if_index, bind using one of its ips */
186   if (ip_is_zero (&lcl->ip, 1) && lcl->sw_if_index != ENDPOINT_INVALID_INDEX)
187     {
188       if ((iface_ip = ip_interface_get_first_ip (lcl->sw_if_index,
189                                                  lcl->is_ip4)))
190         ip_set (&lcl->ip, iface_ip, lcl->is_ip4);
191     }
192   ip_copy (&listener->c_lcl_ip, &lcl->ip, lcl->is_ip4);
193   listener->c_is_ip4 = lcl->is_ip4;
194   listener->c_proto = TRANSPORT_PROTO_UDP;
195   listener->c_s_index = session_index;
196   listener->c_fib_index = lcl->fib_index;
197   listener->mss =
198     lcl->mss ? lcl->mss : udp_default_mtu (um, listener->c_is_ip4);
199   listener->flags |= UDP_CONN_F_OWNS_PORT | UDP_CONN_F_LISTEN;
200   lcl_ext = (transport_endpoint_cfg_t *) lcl;
201   if (lcl_ext->transport_flags & TRANSPORT_CFG_F_CONNECTED)
202     listener->flags |= UDP_CONN_F_CONNECTED;
203   else
204     listener->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
205   clib_spinlock_init (&listener->rx_lock);
206   if (!um->csum_offload)
207     listener->cfg_flags |= UDP_CFG_F_NO_CSUM_OFFLOAD;
208
209   udp_connection_register_port (listener->c_lcl_port, lcl->is_ip4);
210   return listener->c_c_index;
211 }
212
213 static u32
214 udp_session_unbind (u32 listener_index)
215 {
216   udp_main_t *um = &udp_main;
217   udp_connection_t *listener;
218
219   listener = udp_listener_get (listener_index);
220   udp_connection_unregister_port (listener->c_lcl_port, listener->c_is_ip4);
221   clib_spinlock_free (&listener->rx_lock);
222   pool_put (um->listener_pool, listener);
223   return 0;
224 }
225
226 static transport_connection_t *
227 udp_session_get_listener (u32 listener_index)
228 {
229   udp_connection_t *us;
230
231   us = udp_listener_get (listener_index);
232   return &us->connection;
233 }
234
235 always_inline u16
236 udp_compute_checksum (vlib_main_t *vm, vlib_buffer_t *b, u8 csum_offload,
237                       u8 is_ip4)
238 {
239   u16 csum = 0;
240
241   if (csum_offload)
242     vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
243   else
244     {
245       if (is_ip4)
246         csum =
247           ip4_tcp_udp_compute_checksum (vm, b, vlib_buffer_get_current (b));
248       else
249         {
250           int bogus = 0;
251           csum = ip6_tcp_udp_icmp_compute_checksum (
252             vm, b, vlib_buffer_get_current (b), &bogus);
253         }
254     }
255
256   return csum;
257 }
258
259 always_inline u32
260 udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b,
261                      u8 is_cless)
262 {
263   udp_header_t *uh;
264
265   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
266   /* reuse tcp medatada for now */
267   vnet_buffer (b)->tcp.connection_index = uc->c_c_index;
268
269   if (!is_cless)
270     {
271       uh = vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port);
272
273       if (uc->c_is_ip4)
274         vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4,
275                                      IP_PROTOCOL_UDP, udp_csum_offload (uc),
276                                      0 /* is_df */, uc->c_dscp);
277       else
278         vlib_buffer_push_ip6 (vm, b, &uc->c_lcl_ip6, &uc->c_rmt_ip6,
279                               IP_PROTOCOL_UDP);
280
281       vnet_buffer (b)->tcp.flags = 0;
282     }
283   else
284     {
285       u8 *data = vlib_buffer_get_current (b);
286       session_dgram_hdr_t hdr;
287
288       hdr = *(session_dgram_hdr_t *) (data - sizeof (hdr));
289
290       /* Local port assumed to be bound, not overwriting it */
291       uh = vlib_buffer_push_udp (b, uc->c_lcl_port, hdr.rmt_port);
292
293       if (uc->c_is_ip4)
294         vlib_buffer_push_ip4_custom (vm, b, &hdr.lcl_ip.ip4, &hdr.rmt_ip.ip4,
295                                      IP_PROTOCOL_UDP, udp_csum_offload (uc),
296                                      0 /* is_df */, uc->c_dscp);
297       else
298         vlib_buffer_push_ip6 (vm, b, &hdr.lcl_ip.ip6, &hdr.rmt_ip.ip6,
299                               IP_PROTOCOL_UDP);
300
301       /* Not connected udp session. Mark buffer for custom handling in
302        * udp_output */
303       vnet_buffer (b)->tcp.flags |= UDP_CONN_F_LISTEN;
304     }
305
306   uh->checksum =
307     udp_compute_checksum (vm, b, udp_csum_offload (uc), uc->c_is_ip4);
308
309   return 0;
310 }
311
312 always_inline void
313 udp_push_header_batch (udp_connection_t *uc, vlib_buffer_t **bs, u32 n_bufs,
314                        u8 is_cless)
315 {
316   vlib_main_t *vm = vlib_get_main ();
317
318   while (n_bufs >= 4)
319     {
320       vlib_prefetch_buffer_header (bs[2], STORE);
321       vlib_prefetch_buffer_header (bs[3], STORE);
322
323       udp_push_one_header (vm, uc, bs[0], is_cless);
324       udp_push_one_header (vm, uc, bs[1], is_cless);
325
326       n_bufs -= 2;
327       bs += 2;
328     }
329   while (n_bufs)
330     {
331       if (n_bufs > 1)
332         vlib_prefetch_buffer_header (bs[1], STORE);
333
334       udp_push_one_header (vm, uc, bs[0], is_cless);
335
336       n_bufs -= 1;
337       bs += 1;
338     }
339 }
340
341 static u32
342 udp_push_header (transport_connection_t *tc, vlib_buffer_t **bs, u32 n_bufs)
343 {
344   udp_connection_t *uc;
345
346   uc = udp_connection_from_transport (tc);
347   if (uc->flags & UDP_CONN_F_CONNECTED)
348     udp_push_header_batch (uc, bs, n_bufs, 0 /* is_cless */);
349   else
350     udp_push_header_batch (uc, bs, n_bufs, 1 /* is_cless */);
351
352   if (PREDICT_FALSE (uc->flags & UDP_CONN_F_CLOSING))
353     {
354       if (!transport_tx_fifo_has_dgram (&uc->connection))
355         udp_connection_program_cleanup (uc);
356     }
357
358   return 0;
359 }
360
361 static transport_connection_t *
362 udp_session_get (u32 connection_index, u32 thread_index)
363 {
364   udp_connection_t *uc;
365   uc = udp_connection_get (connection_index, thread_index);
366   if (uc)
367     return &uc->connection;
368   return 0;
369 }
370
371 static void
372 udp_session_close (u32 connection_index, u32 thread_index)
373 {
374   udp_connection_t *uc;
375
376   uc = udp_connection_get (connection_index, thread_index);
377   if (!uc || (uc->flags & UDP_CONN_F_MIGRATED))
378     return;
379
380   if (!transport_tx_fifo_has_dgram (&uc->connection))
381     udp_connection_program_cleanup (uc);
382   else
383     uc->flags |= UDP_CONN_F_CLOSING;
384 }
385
386 static void
387 udp_session_cleanup (u32 connection_index, u32 thread_index)
388 {
389   udp_connection_t *uc;
390   uc = udp_connection_get (connection_index, thread_index);
391   if (!uc)
392     return;
393   if (uc->flags & UDP_CONN_F_MIGRATED)
394     udp_connection_free (uc);
395   else
396     udp_connection_cleanup (uc);
397 }
398
399 static int
400 udp_session_send_params (transport_connection_t * tconn,
401                          transport_send_params_t * sp)
402 {
403   udp_connection_t *uc;
404
405   uc = udp_connection_from_transport (tconn);
406
407   /* No constraint on TX window */
408   sp->snd_space = ~0;
409   /* TODO figure out MTU of output interface */
410   sp->snd_mss = uc->mss;
411   sp->tx_offset = 0;
412   sp->flags = 0;
413   return 0;
414 }
415
416 static int
417 udp_open_connection (transport_endpoint_cfg_t * rmt)
418 {
419   udp_main_t *um = &udp_main;
420   ip46_address_t lcl_addr;
421   udp_connection_t *uc;
422   u32 thread_index;
423   u16 lcl_port;
424   int rv;
425
426   rv = transport_alloc_local_endpoint (TRANSPORT_PROTO_UDP, rmt, &lcl_addr,
427                                        &lcl_port);
428   if (rv)
429     return rv;
430
431   if (udp_connection_port_used_extern (clib_net_to_host_u16 (lcl_port),
432                                        rmt->is_ip4))
433     {
434       /* If specific source port was requested abort */
435       if (rmt->peer.port)
436         {
437           transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
438                                             lcl_port);
439           return SESSION_E_PORTINUSE;
440         }
441
442       /* Try to find a port that's not used */
443       while (udp_connection_port_used_extern (clib_net_to_host_u16 (lcl_port),
444                                               rmt->is_ip4))
445         {
446           transport_release_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
447                                             lcl_port);
448           lcl_port =
449             transport_alloc_local_port (TRANSPORT_PROTO_UDP, &lcl_addr, rmt);
450           if ((int) lcl_port < 1)
451             return SESSION_E_PORTINUSE;
452         }
453     }
454
455   /* We don't poll main thread if we have workers */
456   thread_index = transport_cl_thread ();
457
458   uc = udp_connection_alloc (thread_index);
459   ip_copy (&uc->c_rmt_ip, &rmt->ip, rmt->is_ip4);
460   ip_copy (&uc->c_lcl_ip, &lcl_addr, rmt->is_ip4);
461   uc->c_rmt_port = rmt->port;
462   uc->c_lcl_port = lcl_port;
463   uc->c_is_ip4 = rmt->is_ip4;
464   uc->c_proto = TRANSPORT_PROTO_UDP;
465   uc->c_fib_index = rmt->fib_index;
466   uc->c_dscp = rmt->dscp;
467   uc->mss = rmt->mss ? rmt->mss : udp_default_mtu (um, uc->c_is_ip4);
468   if (rmt->peer.sw_if_index != ENDPOINT_INVALID_INDEX)
469     uc->sw_if_index = rmt->peer.sw_if_index;
470   uc->flags |= UDP_CONN_F_OWNS_PORT;
471   if (rmt->transport_flags & TRANSPORT_CFG_F_CONNECTED)
472     {
473       uc->flags |= UDP_CONN_F_CONNECTED;
474     }
475   else
476     {
477       clib_spinlock_init (&uc->rx_lock);
478       uc->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
479     }
480   if (!um->csum_offload)
481     uc->cfg_flags |= UDP_CFG_F_NO_CSUM_OFFLOAD;
482   uc->next_node_index = rmt->next_node_index;
483   uc->next_node_opaque = rmt->next_node_opaque;
484
485   udp_connection_register_port (uc->c_lcl_port, rmt->is_ip4);
486
487   return uc->c_c_index;
488 }
489
490 static transport_connection_t *
491 udp_session_get_half_open (u32 conn_index)
492 {
493   udp_connection_t *uc;
494   u32 thread_index;
495
496   /* We don't poll main thread if we have workers */
497   thread_index = transport_cl_thread ();
498   uc = udp_connection_get (conn_index, thread_index);
499   if (!uc)
500     return 0;
501   return &uc->connection;
502 }
503
504 static u8 *
505 format_udp_session (u8 * s, va_list * args)
506 {
507   u32 uci = va_arg (*args, u32);
508   u32 thread_index = va_arg (*args, u32);
509   u32 verbose = va_arg (*args, u32);
510   udp_connection_t *uc;
511
512   uc = udp_connection_get (uci, thread_index);
513   return format (s, "%U", format_udp_connection, uc, verbose);
514 }
515
516 static u8 *
517 format_udp_half_open_session (u8 * s, va_list * args)
518 {
519   u32 __clib_unused tci = va_arg (*args, u32);
520   u32 __clib_unused thread_index = va_arg (*args, u32);
521   clib_warning ("BUG");
522   return 0;
523 }
524
525 static u8 *
526 format_udp_listener_session (u8 * s, va_list * args)
527 {
528   u32 tci = va_arg (*args, u32);
529   u32 __clib_unused thread_index = va_arg (*args, u32);
530   u32 verbose = va_arg (*args, u32);
531   udp_connection_t *uc = udp_listener_get (tci);
532   return format (s, "%U", format_udp_connection, uc, verbose);
533 }
534
535 static void
536 udp_realloc_ports_sv (u16 **ports_nh_svp)
537 {
538   u16 port, port_no, *ports_nh_sv, *mc;
539   u32 *ports = 0, *nh = 0, msum, i;
540   sparse_vec_header_t *h;
541   uword sv_index, *mb;
542
543   ports_nh_sv = *ports_nh_svp;
544
545   for (port = 1; port < 65535; port++)
546     {
547       port_no = clib_host_to_net_u16 (port);
548
549       sv_index = sparse_vec_index (ports_nh_sv, port_no);
550       if (sv_index != SPARSE_VEC_INVALID_INDEX)
551         {
552           vec_add1 (ports, port_no);
553           vec_add1 (nh, ports_nh_sv[sv_index]);
554         }
555     }
556
557   sparse_vec_free (ports_nh_sv);
558
559   ports_nh_sv =
560     sparse_vec_new (/* elt bytes */ sizeof (ports_nh_sv[0]),
561                     /* bits in index */ BITS (((udp_header_t *) 0)->dst_port));
562
563   vec_resize (ports_nh_sv, 65535);
564
565   for (port = 1; port < 65535; port++)
566     ports_nh_sv[port] = UDP_NO_NODE_SET;
567
568   for (i = 0; i < vec_len (ports); i++)
569     ports_nh_sv[ports[i]] = nh[i];
570
571   h = sparse_vec_header (ports_nh_sv);
572   vec_foreach (mb, h->is_member_bitmap)
573     *mb = (uword) ~0;
574
575   msum = 0;
576   vec_foreach (mc, h->member_counts)
577     {
578       *mc = msum;
579       msum += msum == 0 ? 63 : 64;
580     }
581
582   vec_free (ports);
583   vec_free (nh);
584
585   *ports_nh_svp = ports_nh_sv;
586 }
587
588 static clib_error_t *
589 udp_enable_disable (vlib_main_t *vm, u8 is_en)
590 {
591   udp_main_t *um = &udp_main;
592
593   /* Not ideal. The sparse vector used to map ports to next nodes assumes
594    * only a few ports are ever used. When udp transport is enabled this does
595    * not hold and, to make matters worse, ports are consumed in a random
596    * order.
597    *
598    * This can lead to a lot of slow updates to internal data structures
599    * which in turn can slow udp connection allocations until all ports are
600    * eventually consumed.
601    *
602    * Consequently, reallocate sparse vector, preallocate all ports and have
603    * them point to UDP_NO_NODE_SET. We could consider switching the sparse
604    * vector to a preallocated vector but that would increase memory
605    * consumption for vpp deployments that do not rely on host stack.
606    */
607
608   udp_realloc_ports_sv (&um->next_by_dst_port4);
609   udp_realloc_ports_sv (&um->next_by_dst_port6);
610
611   vec_validate (um->transport_ports_refcnt[0], 65535);
612   vec_validate (um->transport_ports_refcnt[1], 65535);
613
614   return 0;
615 }
616
617 static const transport_proto_vft_t udp_proto = {
618   .enable = udp_enable_disable,
619   .start_listen = udp_session_bind,
620   .connect = udp_open_connection,
621   .stop_listen = udp_session_unbind,
622   .push_header = udp_push_header,
623   .get_connection = udp_session_get,
624   .get_listener = udp_session_get_listener,
625   .get_half_open = udp_session_get_half_open,
626   .close = udp_session_close,
627   .cleanup = udp_session_cleanup,
628   .send_params = udp_session_send_params,
629   .format_connection = format_udp_session,
630   .format_half_open = format_udp_half_open_session,
631   .format_listener = format_udp_listener_session,
632   .transport_options = {
633     .name = "udp",
634     .short_name = "U",
635     .tx_type = TRANSPORT_TX_DGRAM,
636     .service_type = TRANSPORT_SERVICE_CL,
637   },
638 };
639
640 static clib_error_t *
641 udp_init (vlib_main_t * vm)
642 {
643   udp_main_t *um = vnet_get_udp_main ();
644   ip_main_t *im = &ip_main;
645   vlib_thread_main_t *tm = vlib_get_thread_main ();
646   u32 num_threads;
647   ip_protocol_info_t *pi;
648
649   /*
650    * Registrations
651    */
652
653   /* IP registration */
654   pi = ip_get_protocol_info (im, IP_PROTOCOL_UDP);
655   if (pi == 0)
656     return clib_error_return (0, "UDP protocol info AWOL");
657   pi->format_header = format_udp_header;
658   pi->unformat_pg_edit = unformat_pg_udp_header;
659
660   /* Register as transport with session layer */
661   transport_register_protocol (TRANSPORT_PROTO_UDP, &udp_proto,
662                                FIB_PROTOCOL_IP4, udp4_output_node.index);
663   transport_register_protocol (TRANSPORT_PROTO_UDP, &udp_proto,
664                                FIB_PROTOCOL_IP6, udp6_output_node.index);
665
666   /*
667    * Initialize data structures
668    */
669
670   num_threads = 1 /* main thread */  + tm->n_threads;
671   vec_validate (um->wrk, num_threads - 1);
672
673   um->local_to_input_edge[UDP_IP4] =
674     vlib_node_add_next (vm, udp4_local_node.index, udp4_input_node.index);
675   um->local_to_input_edge[UDP_IP6] =
676     vlib_node_add_next (vm, udp6_local_node.index, udp6_input_node.index);
677
678   um->default_mtu = 1500;
679   um->csum_offload = 1;
680   return 0;
681 }
682
683 VLIB_INIT_FUNCTION (udp_init) =
684 {
685   .runs_after = VLIB_INITS("ip_main_init", "ip4_lookup_init",
686                            "ip6_lookup_init"),
687 };
688
689 /*
690  * fd.io coding-style-patch-verification: ON
691  *
692  * Local Variables:
693  * eval: (c-set-style "gnu")
694  * End:
695  */