BIER neighbor stats
[vpp.git] / src / vnet / udp / udp_encap.c
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
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  */
24 dpo_type_t udp_encap_dpo_types[FIB_PROTOCOL_MAX];
25
26 /**
27  * Hash DB to map from client ID to VPP index.
28  */
29 uword *udp_encap_db;
30
31 /**
32  * Pool of encaps
33  */
34 udp_encap_t *udp_encap_pool;
35
36 /**
37  * Stats for each UDP encap object
38  */
39 vlib_combined_counter_main_t udp_encap_counters;
40
41 static udp_encap_t *
42 udp_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
57 static void
58 udp_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
66 index_t
67 udp_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
84       pool_get_aligned (udp_encap_pool, ue, CLIB_CACHE_LINE_BYTES);
85       uei = ue - udp_encap_pool;
86
87       vlib_validate_combined_counter (&(udp_encap_counters), uei);
88       vlib_zero_combined_counter (&(udp_encap_counters), uei);
89
90       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
165 void
166 udp_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
186 index_t
187 udp_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
199 void
200 udp_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
212 void
213 udp_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
230 void
231 udp_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
243 static void
244 udp_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
253 static void
254 udp_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
263 static u8 *
264 format_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);
269   vlib_counter_t to;
270   udp_encap_t *ue;
271
272   ue = udp_encap_get (uei);
273
274   // FIXME
275   s = format (s, "udp-ecap:[%d]: id:%d ip-fib-index:%d ",
276               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     }
297   vlib_get_combined_counter (&(udp_encap_counters), uei, &to);
298   s = format (s, " to:[%Ld:%Ld]]", to.packets, to.bytes);
299
300   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
311 void
312 udp_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
322 static u8 *
323 format_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
331 u8 *
332 format_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
348 static udp_encap_t *
349 udp_encap_from_fib_node (fib_node_t * node)
350 {
351   ASSERT (FIB_NODE_TYPE_UDP_ENCAP == node->fn_type);
352   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  */
359 static fib_node_back_walk_rc_t
360 udp_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  */
370 static fib_node_t *
371 udp_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  */
383 static void
384 udp_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
403 const static char *const udp4_encap_ip4_nodes[] = {
404   "udp4-encap",
405   NULL,
406 };
407
408 const static char *const udp4_encap_ip6_nodes[] = {
409   "udp4-encap",
410   NULL,
411 };
412
413 const static char *const udp4_encap_mpls_nodes[] = {
414   "udp4-encap",
415   NULL,
416 };
417
418 const static char *const udp4_encap_bier_nodes[] = {
419   "udp4-encap",
420   NULL,
421 };
422
423 const static char *const udp6_encap_ip4_nodes[] = {
424   "udp6-encap",
425   NULL,
426 };
427
428 const static char *const udp6_encap_ip6_nodes[] = {
429   "udp6-encap",
430   NULL,
431 };
432
433 const static char *const udp6_encap_mpls_nodes[] = {
434   "udp6-encap",
435   NULL,
436 };
437
438 const static char *const udp6_encap_bier_nodes[] = {
439   "udp6-encap",
440   NULL,
441 };
442
443 const 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,
447   [DPO_PROTO_BIER] = udp4_encap_bier_nodes,
448 };
449
450 const 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,
454   [DPO_PROTO_BIER] = udp6_encap_bier_nodes,
455 };
456
457 /*
458  * Virtual function table registered by UDP encaps
459  * for participation in the FIB object graph.
460  */
461 const 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
467 const 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,
471 };
472
473 clib_error_t *
474 udp_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
488 VLIB_INIT_FUNCTION (udp_encap_init);
489
490 clib_error_t *
491 udp_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
589 done:
590   unformat_free (line_input);
591   return error;
592 }
593
594 void
595 udp_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 (WALK_STOP == cb(uei, ctx))
603       break;
604   }));
605   /* *INDENT-ON* */
606 }
607
608 clib_error_t *
609 udp_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         ;
621       else
622         return clib_error_return (0, "unknown input `%U'",
623                                   format_unformat_error, input);
624     }
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* */
646 VLIB_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 };
652 VLIB_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  */