vxlan-gbp: Add support for vxlan gbp
[vpp.git] / src / vnet / udp / udp.h
1 /*
2  * Copyright (c) 2017 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 #ifndef __included_udp_h__
16 #define __included_udp_h__
17
18 #include <vnet/vnet.h>
19 #include <vnet/udp/udp_packet.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/ip/ip4.h>
22 #include <vnet/ip/ip4_packet.h>
23 #include <vnet/pg/pg.h>
24 #include <vnet/ip/format.h>
25
26 #include <vnet/ip/ip.h>
27 #include <vnet/session/transport.h>
28
29 typedef enum
30 {
31 #define udp_error(n,s) UDP_ERROR_##n,
32 #include <vnet/udp/udp_error.def>
33 #undef udp_error
34   UDP_N_ERROR,
35 } udp_error_t;
36
37 typedef struct
38 {
39   /** Required for pool_get_aligned */
40   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
41   transport_connection_t connection;    /**< must be first */
42   clib_spinlock_t rx_lock;              /**< rx fifo lock */
43   u8 is_connected;                      /**< connected mode */
44 } udp_connection_t;
45
46 #define foreach_udp4_dst_port                   \
47 _ (53, dns)                                     \
48 _ (67, dhcp_to_server)                          \
49 _ (68, dhcp_to_client)                          \
50 _ (500, ikev2)                                  \
51 _ (2152, GTPU)                                  \
52 _ (3784, bfd4)                                  \
53 _ (3785, bfd_echo4)                             \
54 _ (4341, lisp_gpe)                              \
55 _ (4342, lisp_cp)                               \
56 _ (4500, ipsec)                                 \
57 _ (4739, ipfix)                                 \
58 _ (4789, vxlan)                                 \
59 _ (4789, vxlan6)                                \
60 _ (48879, vxlan_gbp)                            \
61 _ (4790, VXLAN_GPE)                             \
62 _ (6633, vpath_3)                               \
63 _ (6081, geneve)                                \
64 _ (53053, dns_reply)
65
66
67 #define foreach_udp6_dst_port                   \
68 _ (53, dns6)                                    \
69 _ (547, dhcpv6_to_server)                       \
70 _ (546, dhcpv6_to_client)                       \
71 _ (2152, GTPU6)                                 \
72 _ (3784, bfd6)                                  \
73 _ (3785, bfd_echo6)                             \
74 _ (4341, lisp_gpe6)                             \
75 _ (4342, lisp_cp6)                              \
76 _ (48879, vxlan6_gbp)                           \
77 _ (4790, VXLAN6_GPE)                            \
78 _ (6633, vpath6_3)                              \
79 _ (6081, geneve6)                               \
80 _ (8138, BIER)                                  \
81 _ (53053, dns_reply6)
82
83 typedef enum
84 {
85 #define _(n,f) UDP_DST_PORT_##f = n,
86   foreach_udp4_dst_port foreach_udp6_dst_port
87 #undef _
88 } udp_dst_port_t;
89
90 typedef enum
91 {
92 #define _(n,f) UDP6_DST_PORT_##f = n,
93   foreach_udp6_dst_port
94 #undef _
95 } udp6_dst_port_t;
96
97 typedef struct
98 {
99   /* Name (a c string). */
100   char *name;
101
102   /* GRE protocol type in host byte order. */
103   udp_dst_port_t dst_port;
104
105   /* Node which handles this type. */
106   u32 node_index;
107
108   /* Next index for this type. */
109   u32 next_index;
110 } udp_dst_port_info_t;
111
112 typedef enum
113 {
114   UDP_IP6 = 0,
115   UDP_IP4,                      /* the code is full of is_ip4... */
116   N_UDP_AF,
117 } udp_af_t;
118
119 typedef struct
120 {
121   udp_dst_port_info_t *dst_port_infos[N_UDP_AF];
122
123   /* Hash tables mapping name/protocol to protocol info index. */
124   uword *dst_port_info_by_name[N_UDP_AF];
125   uword *dst_port_info_by_dst_port[N_UDP_AF];
126
127   /* Sparse vector mapping udp dst_port in network byte order
128      to next index. */
129   u16 *next_by_dst_port4;
130   u16 *next_by_dst_port6;
131   u8 punt_unknown4;
132   u8 punt_unknown6;
133
134   /*
135    * Per-worker thread udp connection pools used with session layer
136    */
137   udp_connection_t **connections;
138   u32 *connection_peekers;
139   clib_spinlock_t *peekers_readers_locks;
140   clib_spinlock_t *peekers_write_locks;
141   udp_connection_t *listener_pool;
142
143 } udp_main_t;
144
145 extern udp_main_t udp_main;
146 extern vlib_node_registration_t udp4_input_node;
147 extern vlib_node_registration_t udp6_input_node;
148
149 always_inline udp_connection_t *
150 udp_connection_get (u32 conn_index, u32 thread_index)
151 {
152   if (pool_is_free_index (udp_main.connections[thread_index], conn_index))
153     return 0;
154   return pool_elt_at_index (udp_main.connections[thread_index], conn_index);
155 }
156
157 always_inline udp_connection_t *
158 udp_listener_get (u32 conn_index)
159 {
160   return pool_elt_at_index (udp_main.listener_pool, conn_index);
161 }
162
163 always_inline udp_main_t *
164 vnet_get_udp_main ()
165 {
166   return &udp_main;
167 }
168
169 always_inline udp_connection_t *
170 udp_get_connection_from_transport (transport_connection_t * tc)
171 {
172   return ((udp_connection_t *) tc);
173 }
174
175 always_inline u32
176 udp_connection_index (udp_connection_t * uc)
177 {
178   return (uc - udp_main.connections[uc->c_thread_index]);
179 }
180
181 udp_connection_t *udp_connection_alloc (u32 thread_index);
182
183 /**
184  * Acquires a lock that blocks a connection pool from expanding.
185  */
186 always_inline void
187 udp_pool_add_peeker (u32 thread_index)
188 {
189   if (thread_index != vlib_get_thread_index ())
190     return;
191   clib_spinlock_lock_if_init (&udp_main.peekers_readers_locks[thread_index]);
192   udp_main.connection_peekers[thread_index] += 1;
193   if (udp_main.connection_peekers[thread_index] == 1)
194     clib_spinlock_lock_if_init (&udp_main.peekers_write_locks[thread_index]);
195   clib_spinlock_unlock_if_init (&udp_main.peekers_readers_locks
196                                 [thread_index]);
197 }
198
199 always_inline void
200 udp_pool_remove_peeker (u32 thread_index)
201 {
202   if (thread_index != vlib_get_thread_index ())
203     return;
204   ASSERT (udp_main.connection_peekers[thread_index] > 0);
205   clib_spinlock_lock_if_init (&udp_main.peekers_readers_locks[thread_index]);
206   udp_main.connection_peekers[thread_index] -= 1;
207   if (udp_main.connection_peekers[thread_index] == 0)
208     clib_spinlock_unlock_if_init (&udp_main.peekers_write_locks
209                                   [thread_index]);
210   clib_spinlock_unlock_if_init (&udp_main.peekers_readers_locks
211                                 [thread_index]);
212 }
213
214 always_inline udp_connection_t *
215 udp_connection_clone_safe (u32 connection_index, u32 thread_index)
216 {
217   udp_connection_t *old_c, *new_c;
218   u32 current_thread_index = vlib_get_thread_index ();
219   new_c = udp_connection_alloc (current_thread_index);
220
221   /* If during the memcpy pool is reallocated AND the memory allocator
222    * decides to give the old chunk of memory to somebody in a hurry to
223    * scribble something on it, we have a problem. So add this thread as
224    * a session pool peeker.
225    */
226   udp_pool_add_peeker (thread_index);
227   old_c = udp_main.connections[thread_index] + connection_index;
228   clib_memcpy (new_c, old_c, sizeof (*new_c));
229   udp_pool_remove_peeker (thread_index);
230   new_c->c_thread_index = current_thread_index;
231   new_c->c_c_index = udp_connection_index (new_c);
232   return new_c;
233 }
234
235
236 always_inline udp_dst_port_info_t *
237 udp_get_dst_port_info (udp_main_t * um, udp_dst_port_t dst_port, u8 is_ip4)
238 {
239   uword *p = hash_get (um->dst_port_info_by_dst_port[is_ip4], dst_port);
240   return p ? vec_elt_at_index (um->dst_port_infos[is_ip4], p[0]) : 0;
241 }
242
243 format_function_t format_udp_header;
244 format_function_t format_udp_rx_trace;
245 unformat_function_t unformat_udp_header;
246
247 void udp_register_dst_port (vlib_main_t * vm,
248                             udp_dst_port_t dst_port,
249                             u32 node_index, u8 is_ip4);
250 void udp_unregister_dst_port (vlib_main_t * vm,
251                               udp_dst_port_t dst_port, u8 is_ip4);
252
253 void udp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add);
254
255 always_inline void *
256 vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum)
257 {
258   udp_header_t *uh;
259   u16 udp_len = sizeof (udp_header_t) + b->current_length;
260   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID))
261     udp_len += b->total_length_not_including_first_buffer;
262
263   uh = vlib_buffer_push_uninit (b, sizeof (udp_header_t));
264   uh->src_port = sp;
265   uh->dst_port = dp;
266   uh->checksum = 0;
267   uh->length = clib_host_to_net_u16 (udp_len);
268   if (offload_csum)
269     {
270       b->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
271       vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data;
272     }
273   return uh;
274 }
275
276 always_inline void
277 ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4)
278 {
279   u16 new_l0;
280   udp_header_t *udp0;
281
282   if (is_ip4)
283     {
284       ip4_header_t *ip0;
285       ip_csum_t sum0;
286       u16 old_l0 = 0;
287
288       ip0 = vlib_buffer_get_current (b0);
289
290       /* fix the <bleep>ing outer-IP checksum */
291       sum0 = ip0->checksum;
292       /* old_l0 always 0, see the rewrite setup */
293       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
294
295       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
296                              length /* changed member */ );
297       ip0->checksum = ip_csum_fold (sum0);
298       ip0->length = new_l0;
299
300       /* Fix UDP length */
301       udp0 = (udp_header_t *) (ip0 + 1);
302       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
303                                      - sizeof (*ip0));
304       udp0->length = new_l0;
305     }
306   else
307     {
308       ip6_header_t *ip0;
309       int bogus0;
310
311       ip0 = vlib_buffer_get_current (b0);
312
313       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
314                                      - sizeof (*ip0));
315       ip0->payload_length = new_l0;
316
317       /* Fix UDP length */
318       udp0 = (udp_header_t *) (ip0 + 1);
319       udp0->length = new_l0;
320
321       udp0->checksum =
322         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
323       ASSERT (bogus0 == 0);
324
325       if (udp0->checksum == 0)
326         udp0->checksum = 0xffff;
327     }
328 }
329
330 always_inline void
331 ip_udp_encap_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 * ec0, word ec_len,
332                   u8 is_ip4)
333 {
334   vlib_buffer_advance (b0, -ec_len);
335
336   if (is_ip4)
337     {
338       ip4_header_t *ip0;
339
340       ip0 = vlib_buffer_get_current (b0);
341
342       /* Apply the encap string. */
343       clib_memcpy (ip0, ec0, ec_len);
344       ip_udp_fixup_one (vm, b0, 1);
345     }
346   else
347     {
348       ip6_header_t *ip0;
349
350       ip0 = vlib_buffer_get_current (b0);
351
352       /* Apply the encap string. */
353       clib_memcpy (ip0, ec0, ec_len);
354       ip_udp_fixup_one (vm, b0, 0);
355     }
356 }
357
358 always_inline void
359 ip_udp_encap_two (vlib_main_t * vm, vlib_buffer_t * b0, vlib_buffer_t * b1,
360                   u8 * ec0, u8 * ec1, word ec_len, u8 is_v4)
361 {
362   u16 new_l0, new_l1;
363   udp_header_t *udp0, *udp1;
364
365   ASSERT (_vec_len (ec0) == _vec_len (ec1));
366
367   vlib_buffer_advance (b0, -ec_len);
368   vlib_buffer_advance (b1, -ec_len);
369
370   if (is_v4)
371     {
372       ip4_header_t *ip0, *ip1;
373       ip_csum_t sum0, sum1;
374       u16 old_l0 = 0, old_l1 = 0;
375
376       ip0 = vlib_buffer_get_current (b0);
377       ip1 = vlib_buffer_get_current (b1);
378
379       /* Apply the encap string */
380       clib_memcpy (ip0, ec0, ec_len);
381       clib_memcpy (ip1, ec1, ec_len);
382
383       /* fix the <bleep>ing outer-IP checksum */
384       sum0 = ip0->checksum;
385       sum1 = ip1->checksum;
386
387       /* old_l0 always 0, see the rewrite setup */
388       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
389       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
390
391       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
392                              length /* changed member */ );
393       sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
394                              length /* changed member */ );
395
396       ip0->checksum = ip_csum_fold (sum0);
397       ip1->checksum = ip_csum_fold (sum1);
398
399       ip0->length = new_l0;
400       ip1->length = new_l1;
401
402       /* Fix UDP length */
403       udp0 = (udp_header_t *) (ip0 + 1);
404       udp1 = (udp_header_t *) (ip1 + 1);
405
406       new_l0 =
407         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
408                               sizeof (*ip0));
409       new_l1 =
410         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) -
411                               sizeof (*ip1));
412       udp0->length = new_l0;
413       udp1->length = new_l1;
414     }
415   else
416     {
417       ip6_header_t *ip0, *ip1;
418       int bogus0, bogus1;
419
420       ip0 = vlib_buffer_get_current (b0);
421       ip1 = vlib_buffer_get_current (b1);
422
423       /* Apply the encap string. */
424       clib_memcpy (ip0, ec0, ec_len);
425       clib_memcpy (ip1, ec1, ec_len);
426
427       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
428                                      - sizeof (*ip0));
429       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
430                                      - sizeof (*ip1));
431       ip0->payload_length = new_l0;
432       ip1->payload_length = new_l1;
433
434       /* Fix UDP length */
435       udp0 = (udp_header_t *) (ip0 + 1);
436       udp1 = (udp_header_t *) (ip1 + 1);
437
438       udp0->length = new_l0;
439       udp1->length = new_l1;
440
441       udp0->checksum =
442         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
443       udp1->checksum =
444         ip6_tcp_udp_icmp_compute_checksum (vm, b1, ip1, &bogus1);
445       ASSERT (bogus0 == 0);
446       ASSERT (bogus1 == 0);
447
448       if (udp0->checksum == 0)
449         udp0->checksum = 0xffff;
450       if (udp1->checksum == 0)
451         udp1->checksum = 0xffff;
452     }
453 }
454
455 /*
456  * fd.io coding-style-patch-verification: ON
457  *
458  * Local Variables:
459  * eval: (c-set-style "gnu")
460  * End:
461  */
462
463 #endif /* __included_udp_h__ */