blob: 4b4d5412ca70e95983cfe432b88358d422d7080a [file] [log] [blame]
Neale Ranns810086d2017-11-05 16:26:46 -08001/*
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
16#include <vnet/udp/udp_encap.h>
17#include <vnet/fib/fib_entry.h>
18#include <vnet/fib/fib_table.h>
19#include <vnet/dpo/drop_dpo.h>
20
21/**
22 * Registered DPO types for the IP header encapsulated, v4 or v6.
23 */
24dpo_type_t udp_encap_dpo_types[FIB_PROTOCOL_MAX];
25
26/**
27 * Hash DB to map from client ID to VPP index.
28 */
29uword *udp_encap_db;
30
31/**
32 * Pool of encaps
33 */
34udp_encap_t *udp_encap_pool;
35
Neale Ranns43b1f442018-03-20 01:47:35 -070036/**
37 * Stats for each UDP encap object
38 */
39vlib_combined_counter_main_t udp_encap_counters;
40
Neale Ranns810086d2017-11-05 16:26:46 -080041static udp_encap_t *
42udp_encap_get_w_id (u32 id)
43{
44 udp_encap_t *ue = NULL;
45 index_t uei;
46
47 uei = udp_encap_find (id);
48
49 if (INDEX_INVALID != uei)
50 {
51 ue = udp_encap_get (uei);
52 }
53
54 return (ue);
55}
56
57static void
58udp_encap_restack (udp_encap_t * ue)
59{
60 dpo_stack (udp_encap_dpo_types[ue->ue_ip_proto],
61 fib_proto_to_dpo (ue->ue_ip_proto),
62 &ue->ue_dpo,
63 fib_entry_contribute_ip_forwarding (ue->ue_fib_entry_index));
64}
65
66index_t
67udp_encap_add_and_lock (u32 id,
68 fib_protocol_t proto,
69 index_t fib_index,
70 const ip46_address_t * src_ip,
71 const ip46_address_t * dst_ip,
72 u16 src_port,
73 u16 dst_port, udp_encap_fixup_flags_t flags)
74{
75 udp_encap_t *ue;
76 index_t uei;
77
78 uei = udp_encap_find (id);
79
80 if (INDEX_INVALID == uei)
81 {
82 u8 pfx_len = 0;
83
Damjan Marion7b0b9612018-03-21 22:27:46 +010084 pool_get_aligned (udp_encap_pool, ue, CLIB_CACHE_LINE_BYTES);
Neale Ranns810086d2017-11-05 16:26:46 -080085 uei = ue - udp_encap_pool;
86
Neale Ranns43b1f442018-03-20 01:47:35 -070087 vlib_validate_combined_counter (&(udp_encap_counters), uei);
88 vlib_zero_combined_counter (&(udp_encap_counters), uei);
89
Neale Ranns810086d2017-11-05 16:26:46 -080090 hash_set (udp_encap_db, id, uei);
91
92 fib_node_init (&ue->ue_fib_node, FIB_NODE_TYPE_UDP_ENCAP);
93 fib_node_lock (&ue->ue_fib_node);
94 ue->ue_fib_index = fib_index;
95 ue->ue_flags = flags;
96 ue->ue_id = id;
97 ue->ue_ip_proto = proto;
98
99 switch (proto)
100 {
101 case FIB_PROTOCOL_IP4:
102 pfx_len = 32;
103 ue->ue_hdrs.ip4.ue_ip4.ip_version_and_header_length = 0x45;
104 ue->ue_hdrs.ip4.ue_ip4.ttl = 254;
105 ue->ue_hdrs.ip4.ue_ip4.protocol = IP_PROTOCOL_UDP;
106 ue->ue_hdrs.ip4.ue_ip4.src_address.as_u32 = src_ip->ip4.as_u32;
107 ue->ue_hdrs.ip4.ue_ip4.dst_address.as_u32 = dst_ip->ip4.as_u32;
108 ue->ue_hdrs.ip4.ue_ip4.checksum =
109 ip4_header_checksum (&ue->ue_hdrs.ip4.ue_ip4);
110 ue->ue_hdrs.ip4.ue_udp.src_port = clib_host_to_net_u16 (src_port);
111 ue->ue_hdrs.ip4.ue_udp.dst_port = clib_host_to_net_u16 (dst_port);
112
113 break;
114 case FIB_PROTOCOL_IP6:
115 pfx_len = 128;
116 ue->ue_hdrs.ip6.ue_ip6.ip_version_traffic_class_and_flow_label =
117 clib_host_to_net_u32 (6 << 28);
118 ue->ue_hdrs.ip6.ue_ip6.hop_limit = 255;
119 ue->ue_hdrs.ip6.ue_ip6.protocol = IP_PROTOCOL_UDP;
120 ue->ue_hdrs.ip6.ue_ip6.src_address.as_u64[0] =
121 src_ip->ip6.as_u64[0];
122 ue->ue_hdrs.ip6.ue_ip6.src_address.as_u64[1] =
123 src_ip->ip6.as_u64[1];
124 ue->ue_hdrs.ip6.ue_ip6.dst_address.as_u64[0] =
125 dst_ip->ip6.as_u64[0];
126 ue->ue_hdrs.ip6.ue_ip6.dst_address.as_u64[1] =
127 dst_ip->ip6.as_u64[1];
128 ue->ue_hdrs.ip6.ue_udp.src_port = clib_host_to_net_u16 (src_port);
129 ue->ue_hdrs.ip6.ue_udp.dst_port = clib_host_to_net_u16 (dst_port);
130
131 break;
132 default:
133 ASSERT (0);
134 }
135
136 /*
137 * track the destination address
138 */
139 fib_prefix_t dst_pfx = {
140 .fp_proto = proto,
141 .fp_len = pfx_len,
142 .fp_addr = *dst_ip,
143 };
144
145 ue->ue_fib_entry_index =
146 fib_table_entry_special_add (fib_index,
147 &dst_pfx,
148 FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE);
149 ue->ue_fib_sibling =
150 fib_entry_child_add (ue->ue_fib_entry_index,
151 FIB_NODE_TYPE_UDP_ENCAP, uei);
152
153 udp_encap_restack (ue);
154 }
155 else
156 {
157 /*
158 * existing entry. updates not supported yet
159 */
160 uei = INDEX_INVALID;
161 }
162 return (uei);
163}
164
165void
166udp_encap_contribute_forwarding (u32 id, dpo_proto_t proto, dpo_id_t * dpo)
167{
168 index_t uei;
169
170 uei = udp_encap_find (id);
171
172 if (INDEX_INVALID == uei)
173 {
174 dpo_copy (dpo, drop_dpo_get (proto));
175 }
176 else
177 {
178 udp_encap_t *ue;
179
180 ue = udp_encap_get (uei);
181
182 dpo_set (dpo, udp_encap_dpo_types[ue->ue_ip_proto], proto, uei);
183 }
184}
185
186index_t
187udp_encap_find (u32 id)
188{
189 uword *p;
190
191 p = hash_get (udp_encap_db, id);
192
193 if (NULL != p)
194 return p[0];
195
196 return INDEX_INVALID;
197}
198
199void
200udp_encap_lock (u32 id)
201{
202 udp_encap_t *ue;
203
204 ue = udp_encap_get_w_id (id);
205
206 if (NULL != ue)
207 {
208 fib_node_lock (&ue->ue_fib_node);
209 }
210}
211
212void
213udp_encap_unlock_w_index (index_t uei)
214{
215 udp_encap_t *ue;
216
217 if (INDEX_INVALID == uei)
218 {
219 return;
220 }
221
222 ue = udp_encap_get (uei);
223
224 if (NULL != ue)
225 {
226 fib_node_unlock (&ue->ue_fib_node);
227 }
228}
229
230void
231udp_encap_unlock (u32 id)
232{
233 udp_encap_t *ue;
234
235 ue = udp_encap_get_w_id (id);
236
237 if (NULL != ue)
238 {
239 fib_node_unlock (&ue->ue_fib_node);
240 }
241}
242
243static void
244udp_encap_dpo_lock (dpo_id_t * dpo)
245{
246 udp_encap_t *ue;
247
248 ue = udp_encap_get (dpo->dpoi_index);
249
250 fib_node_lock (&ue->ue_fib_node);
251}
252
253static void
254udp_encap_dpo_unlock (dpo_id_t * dpo)
255{
256 udp_encap_t *ue;
257
258 ue = udp_encap_get (dpo->dpoi_index);
259
260 fib_node_unlock (&ue->ue_fib_node);
261}
262
263static u8 *
264format_udp_encap_i (u8 * s, va_list * args)
265{
266 index_t uei = va_arg (*args, index_t);
267 u32 indent = va_arg (*args, u32);
268 u32 details = va_arg (*args, u32);
Neale Ranns43b1f442018-03-20 01:47:35 -0700269 vlib_counter_t to;
Neale Ranns810086d2017-11-05 16:26:46 -0800270 udp_encap_t *ue;
271
272 ue = udp_encap_get (uei);
273
274 // FIXME
Neale Ranns2303cb12018-02-21 04:57:17 -0800275 s = format (s, "udp-ecap:[%d]: id:%d ip-fib-index:%d ",
Neale Ranns810086d2017-11-05 16:26:46 -0800276 uei, ue->ue_id, ue->ue_fib_index);
277 if (FIB_PROTOCOL_IP4 == ue->ue_ip_proto)
278 {
279 s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d, dst:%d]",
280 format_ip4_address,
281 &ue->ue_hdrs.ip4.ue_ip4.src_address,
282 format_ip4_address,
283 &ue->ue_hdrs.ip4.ue_ip4.dst_address,
284 clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.src_port),
285 clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.dst_port));
286 }
287 else
288 {
289 s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d dst:%d]",
290 format_ip6_address,
291 &ue->ue_hdrs.ip6.ue_ip6.src_address,
292 format_ip6_address,
293 &ue->ue_hdrs.ip6.ue_ip6.dst_address,
294 clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.src_port),
295 clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.dst_port));
296 }
Neale Ranns43b1f442018-03-20 01:47:35 -0700297 vlib_get_combined_counter (&(udp_encap_counters), uei, &to);
298 s = format (s, " to:[%Ld:%Ld]]", to.packets, to.bytes);
299
Neale Ranns810086d2017-11-05 16:26:46 -0800300 if (details)
301 {
302 s = format (s, " locks:%d", ue->ue_fib_node.fn_locks);
303 s = format (s, "\n%UStacked on:", format_white_space, indent + 1);
304 s = format (s, "\n%U%U",
305 format_white_space, indent + 2,
306 format_dpo_id, &ue->ue_dpo, indent + 3);
307 }
308 return (s);
309}
310
Neale Ranns43b1f442018-03-20 01:47:35 -0700311void
312udp_encap_get_stats (index_t uei, u64 * packets, u64 * bytes)
313{
314 vlib_counter_t to;
315
316 vlib_get_combined_counter (&(udp_encap_counters), uei, &to);
317
318 *packets = to.packets;
319 *bytes = to.bytes;
320}
321
Neale Ranns810086d2017-11-05 16:26:46 -0800322static u8 *
323format_udp_encap_dpo (u8 * s, va_list * args)
324{
325 index_t uei = va_arg (*args, index_t);
326 u32 indent = va_arg (*args, u32);
327
328 return (format (s, "%U", format_udp_encap_i, uei, indent, 1));
329}
330
331u8 *
332format_udp_encap (u8 * s, va_list * args)
333{
334 u32 id = va_arg (*args, u32);
335 u32 details = va_arg (*args, u32);
336 index_t uei;
337
338 uei = udp_encap_find (id);
339
340 if (INDEX_INVALID == uei)
341 {
342 return (format (s, "Invalid udp-encap ID: %d", id));
343 }
344
345 return (format (s, "%U", format_udp_encap_i, uei, 0, details));
346}
347
348static udp_encap_t *
349udp_encap_from_fib_node (fib_node_t * node)
350{
Neale Ranns810086d2017-11-05 16:26:46 -0800351 ASSERT (FIB_NODE_TYPE_UDP_ENCAP == node->fn_type);
Neale Ranns810086d2017-11-05 16:26:46 -0800352 return ((udp_encap_t *) (((char *) node) -
353 STRUCT_OFFSET_OF (udp_encap_t, ue_fib_node)));
354}
355
356/**
357 * Function definition to backwalk a FIB node
358 */
359static fib_node_back_walk_rc_t
360udp_encap_fib_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
361{
362 udp_encap_restack (udp_encap_from_fib_node (node));
363
364 return (FIB_NODE_BACK_WALK_CONTINUE);
365}
366
367/**
368 * Function definition to get a FIB node from its index
369 */
370static fib_node_t *
371udp_encap_fib_node_get (fib_node_index_t index)
372{
373 udp_encap_t *ue;
374
375 ue = pool_elt_at_index (udp_encap_pool, index);
376
377 return (&ue->ue_fib_node);
378}
379
380/**
381 * Function definition to inform the FIB node that its last lock has gone.
382 */
383static void
384udp_encap_fib_last_lock_gone (fib_node_t * node)
385{
386 udp_encap_t *ue;
387
388 ue = udp_encap_from_fib_node (node);
389
390 /**
391 * reset the stacked DPO to unlock it
392 */
393 dpo_reset (&ue->ue_dpo);
394 hash_unset (udp_encap_db, ue->ue_id);
395
396 fib_entry_child_remove (ue->ue_fib_entry_index, ue->ue_fib_sibling);
397 fib_table_entry_delete_index (ue->ue_fib_entry_index, FIB_SOURCE_RR);
398
399
400 pool_put (udp_encap_pool, ue);
401}
402
403const static char *const udp4_encap_ip4_nodes[] = {
404 "udp4-encap",
405 NULL,
406};
407
408const static char *const udp4_encap_ip6_nodes[] = {
409 "udp4-encap",
410 NULL,
411};
412
413const static char *const udp4_encap_mpls_nodes[] = {
414 "udp4-encap",
415 NULL,
416};
417
Neale Ranns91286372017-12-05 13:24:04 -0800418const static char *const udp4_encap_bier_nodes[] = {
419 "udp4-encap",
420 NULL,
421};
422
Neale Ranns810086d2017-11-05 16:26:46 -0800423const static char *const udp6_encap_ip4_nodes[] = {
424 "udp6-encap",
425 NULL,
426};
427
428const static char *const udp6_encap_ip6_nodes[] = {
429 "udp6-encap",
430 NULL,
431};
432
433const static char *const udp6_encap_mpls_nodes[] = {
434 "udp6-encap",
435 NULL,
436};
437
Neale Ranns91286372017-12-05 13:24:04 -0800438const static char *const udp6_encap_bier_nodes[] = {
439 "udp6-encap",
440 NULL,
441};
442
Neale Ranns810086d2017-11-05 16:26:46 -0800443const static char *const *const udp4_encap_nodes[DPO_PROTO_NUM] = {
444 [DPO_PROTO_IP4] = udp4_encap_ip4_nodes,
445 [DPO_PROTO_IP6] = udp4_encap_ip6_nodes,
446 [DPO_PROTO_MPLS] = udp4_encap_mpls_nodes,
Neale Ranns91286372017-12-05 13:24:04 -0800447 [DPO_PROTO_BIER] = udp4_encap_bier_nodes,
Neale Ranns810086d2017-11-05 16:26:46 -0800448};
449
450const static char *const *const udp6_encap_nodes[DPO_PROTO_NUM] = {
451 [DPO_PROTO_IP4] = udp6_encap_ip4_nodes,
452 [DPO_PROTO_IP6] = udp6_encap_ip6_nodes,
453 [DPO_PROTO_MPLS] = udp6_encap_mpls_nodes,
Neale Ranns91286372017-12-05 13:24:04 -0800454 [DPO_PROTO_BIER] = udp6_encap_bier_nodes,
Neale Ranns810086d2017-11-05 16:26:46 -0800455};
456
457/*
458 * Virtual function table registered by UDP encaps
459 * for participation in the FIB object graph.
460 */
461const static fib_node_vft_t udp_encap_fib_vft = {
462 .fnv_get = udp_encap_fib_node_get,
463 .fnv_last_lock = udp_encap_fib_last_lock_gone,
464 .fnv_back_walk = udp_encap_fib_back_walk,
465};
466
467const static dpo_vft_t udp_encap_dpo_vft = {
468 .dv_lock = udp_encap_dpo_lock,
469 .dv_unlock = udp_encap_dpo_unlock,
470 .dv_format = format_udp_encap_dpo,
Neale Ranns810086d2017-11-05 16:26:46 -0800471};
472
473clib_error_t *
474udp_encap_init (vlib_main_t * vm)
475{
476 udp_encap_db = hash_create (0, sizeof (index_t));
477
478 fib_node_register_type (FIB_NODE_TYPE_UDP_ENCAP, &udp_encap_fib_vft);
479
480 udp_encap_dpo_types[FIB_PROTOCOL_IP4] =
481 dpo_register_new_type (&udp_encap_dpo_vft, udp4_encap_nodes);
482 udp_encap_dpo_types[FIB_PROTOCOL_IP6] =
483 dpo_register_new_type (&udp_encap_dpo_vft, udp6_encap_nodes);
484
485 return (NULL);
486}
487
488VLIB_INIT_FUNCTION (udp_encap_init);
489
490clib_error_t *
491udp_encap_cli (vlib_main_t * vm,
492 unformat_input_t * main_input, vlib_cli_command_t * cmd)
493{
494 unformat_input_t _line_input, *line_input = &_line_input;
495 clib_error_t *error = NULL;
496 ip46_address_t src_ip, dst_ip;
497 u32 table_id, ue_id;
498 u32 src_port, dst_port;
499 udp_encap_fixup_flags_t flags;
500 fib_protocol_t fproto;
501 u8 is_del;
502
503 is_del = 0;
504 table_id = 0;
505 flags = UDP_ENCAP_FIXUP_NONE;
506 fproto = FIB_PROTOCOL_MAX;
507 dst_port = 0;
508 ue_id = ~0;
509
510 /* Get a line of input. */
511 if (!unformat_user (main_input, unformat_line_input, line_input))
512 return 0;
513
514 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
515 {
516 if (unformat (line_input, "id %d", &ue_id))
517 ;
518 else if (unformat (line_input, "add"))
519 is_del = 0;
520 else if (unformat (line_input, "del"))
521 is_del = 1;
522 else if (unformat (line_input, "%U %U",
523 unformat_ip4_address,
524 &src_ip.ip4, unformat_ip4_address, &dst_ip.ip4))
525 fproto = FIB_PROTOCOL_IP4;
526 else if (unformat (line_input, "%U %U",
527 unformat_ip6_address,
528 &src_ip.ip6, unformat_ip6_address, &dst_ip.ip6))
529 fproto = FIB_PROTOCOL_IP6;
530 else if (unformat (line_input, "%d %d", &src_port, &dst_port))
531 ;
532 else if (unformat (line_input, "%d", &dst_port))
533 ;
534 else if (unformat (line_input, "table-id %d", &table_id))
535 ;
536 else if (unformat (line_input, "src-port-is-entropy"))
537 flags |= UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY;
538 else
539 {
540 error = unformat_parse_error (line_input);
541 goto done;
542 }
543 }
544
545 if (~0 == ue_id)
546 {
547 error =
548 clib_error_return (0, "An ID for the UDP encap instance is required");
549 goto done;
550 }
551
552 if (!is_del && fproto != FIB_PROTOCOL_MAX)
553 {
554 u32 fib_index;
555 index_t uei;
556
557 fib_index = fib_table_find (fproto, table_id);
558
559 if (~0 == fib_index)
560 {
561 error = clib_error_return (0, "Nonexistent table id %d", table_id);
562 goto done;
563 }
564
565 uei = udp_encap_add_and_lock (ue_id, fproto, fib_index,
566 &src_ip, &dst_ip,
567 src_port, dst_port, flags);
568
569 if (INDEX_INVALID == uei)
570 {
571 error =
572 clib_error_return (0, "update to existing encap not supported %d",
573 ue_id);
574 goto done;
575 }
576 }
577 else if (is_del)
578 {
579 udp_encap_unlock (ue_id);
580 }
581 else
582 {
583 error =
584 clib_error_return (0,
585 "Some IP addresses would be usefull, don't you think?",
586 ue_id);
587 }
588
589done:
590 unformat_free (line_input);
591 return error;
592}
593
Neale Ranns43b1f442018-03-20 01:47:35 -0700594void
595udp_encap_walk (udp_encap_walk_cb_t cb, void *ctx)
596{
597 index_t uei;
598
599 /* *INDENT-OFF* */
600 pool_foreach_index(uei, udp_encap_pool,
601 ({
602 if (!cb(uei, ctx))
603 break;
604 }));
605 /* *INDENT-ON* */
606}
607
Neale Ranns810086d2017-11-05 16:26:46 -0800608clib_error_t *
609udp_encap_show (vlib_main_t * vm,
610 unformat_input_t * input, vlib_cli_command_t * cmd)
611{
612 u32 ue_id;
613
614 ue_id = ~0;
615
616 /* Get a line of input. */
617 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
618 {
619 if (unformat (input, "%d", &ue_id))
620 ;
Swarup Nayak529a4252017-12-21 11:35:33 +0530621 else
622 return clib_error_return (0, "unknown input `%U'",
623 format_unformat_error, input);
Neale Ranns810086d2017-11-05 16:26:46 -0800624 }
625
626 if (~0 == ue_id)
627 {
628 udp_encap_t *ue;
629
630 /* *INDENT-OFF* */
631 pool_foreach(ue, udp_encap_pool,
632 ({
633 vlib_cli_output(vm, "%U", format_udp_encap, ue->ue_id, 0);
634 }));
635 /* *INDENT-ON* */
636 }
637 else
638 {
639 vlib_cli_output (vm, "%U", format_udp_encap, ue_id, 1);
640 }
641
642 return NULL;
643}
644
645/* *INDENT-OFF* */
646VLIB_CLI_COMMAND (udp_encap_add_command, static) = {
647 .path = "udp encap",
648 .short_help = "udp encap [add|del] <id ID> <src-ip> <dst-ip> [<src-port>] <dst-port> [src-port-is-entropy] [table-id <table>]",
649 .function = udp_encap_cli,
650 .is_mp_safe = 1,
651};
652VLIB_CLI_COMMAND (udp_encap_show_command, static) = {
653 .path = "show udp encap",
654 .short_help = "show udp encap [ID]",
655 .function = udp_encap_show,
656 .is_mp_safe = 1,
657};
658/* *INDENT-ON* */
659
660/*
661 * fd.io coding-style-patch-verification: ON
662 *
663 * Local Variables:
664 * eval: (c-set-style "gnu")
665 * End:
666 */