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