FIB Interpose Source
[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 static udp_encap_t *
37 udp_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
52 static void
53 udp_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
61 index_t
62 udp_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
157 void
158 udp_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
178 index_t
179 udp_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
191 void
192 udp_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
204 void
205 udp_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
222 void
223 udp_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
235 static void
236 udp_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
245 static void
246 udp_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
255 static u8 *
256 format_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
299 static u8 *
300 format_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
308 u8 *
309 format_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
325 static udp_encap_t *
326 udp_encap_from_fib_node (fib_node_t * node)
327 {
328   ASSERT (FIB_NODE_TYPE_UDP_ENCAP == node->fn_type);
329   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  */
336 static fib_node_back_walk_rc_t
337 udp_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  */
347 static fib_node_t *
348 udp_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  */
360 static void
361 udp_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
380 const static char *const udp4_encap_ip4_nodes[] = {
381   "udp4-encap",
382   NULL,
383 };
384
385 const static char *const udp4_encap_ip6_nodes[] = {
386   "udp4-encap",
387   NULL,
388 };
389
390 const static char *const udp4_encap_mpls_nodes[] = {
391   "udp4-encap",
392   NULL,
393 };
394
395 const static char *const udp4_encap_bier_nodes[] = {
396   "udp4-encap",
397   NULL,
398 };
399
400 const static char *const udp6_encap_ip4_nodes[] = {
401   "udp6-encap",
402   NULL,
403 };
404
405 const static char *const udp6_encap_ip6_nodes[] = {
406   "udp6-encap",
407   NULL,
408 };
409
410 const static char *const udp6_encap_mpls_nodes[] = {
411   "udp6-encap",
412   NULL,
413 };
414
415 const static char *const udp6_encap_bier_nodes[] = {
416   "udp6-encap",
417   NULL,
418 };
419
420 const 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,
424   [DPO_PROTO_BIER] = udp4_encap_bier_nodes,
425 };
426
427 const 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,
431   [DPO_PROTO_BIER] = udp6_encap_bier_nodes,
432 };
433
434 /*
435  * Virtual function table registered by UDP encaps
436  * for participation in the FIB object graph.
437  */
438 const 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
444 const 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,
448 };
449
450 clib_error_t *
451 udp_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
465 VLIB_INIT_FUNCTION (udp_encap_init);
466
467 clib_error_t *
468 udp_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
566 done:
567   unformat_free (line_input);
568   return error;
569 }
570
571 clib_error_t *
572 udp_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       else
585         return clib_error_return (0, "unknown input `%U'",
586                                   format_unformat_error, input);
587     }
588
589   if (~0 == ue_id)
590     {
591       udp_encap_t *ue;
592
593       /* *INDENT-OFF* */
594       pool_foreach(ue, udp_encap_pool,
595       ({
596         vlib_cli_output(vm, "%U", format_udp_encap, ue->ue_id, 0);
597       }));
598       /* *INDENT-ON* */
599     }
600   else
601     {
602       vlib_cli_output (vm, "%U", format_udp_encap, ue_id, 1);
603     }
604
605   return NULL;
606 }
607
608 /* *INDENT-OFF* */
609 VLIB_CLI_COMMAND (udp_encap_add_command, static) = {
610   .path = "udp encap",
611   .short_help = "udp encap [add|del] <id ID> <src-ip> <dst-ip> [<src-port>] <dst-port>  [src-port-is-entropy] [table-id <table>]",
612   .function = udp_encap_cli,
613   .is_mp_safe = 1,
614 };
615 VLIB_CLI_COMMAND (udp_encap_show_command, static) = {
616   .path = "show udp encap",
617   .short_help = "show udp encap [ID]",
618   .function = udp_encap_show,
619   .is_mp_safe = 1,
620 };
621 /* *INDENT-ON* */
622
623 /*
624  * fd.io coding-style-patch-verification: ON
625  *
626  * Local Variables:
627  * eval: (c-set-style "gnu")
628  * End:
629  */