hs-test: fixed timed out tests passing in the CI
[vpp.git] / src / plugins / srv6-ad-flow / ad-flow.c
1 /*
2  * Copyright (c) 2015 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 /*
17  *------------------------------------------------------------------
18  * ad-flow.c - SRv6 Flow-based Dynamic Proxy (AD.Flow) behavior
19  *------------------------------------------------------------------
20  */
21
22 #include <vnet/vnet.h>
23 #include <vnet/adj/adj.h>
24 #include <vnet/plugin/plugin.h>
25 #include <vpp/app/version.h>
26 #include <srv6-ad-flow/ad-flow.h>
27
28 #include <vppinfra/bihash_template.c>
29
30 #define SID_CREATE_IFACE_FEATURE_ERROR -1
31 #define SID_CREATE_INVALID_IFACE_TYPE  -3
32 #define SID_CREATE_INVALID_IFACE_INDEX -4
33 #define SID_CREATE_INVALID_ADJ_INDEX   -5
34
35 unsigned char function_name[] = "SRv6-AD-Flow-plugin";
36 unsigned char keyword_str[] = "End.AD.Flow";
37 unsigned char def_str[] =
38   "Endpoint with flow-based dynamic proxy to SR-unaware appliance";
39 unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
40
41 srv6_ad_flow_main_t srv6_ad_flow_main;
42
43 static u32
44 ad_flow_calc_bihash_buckets (u32 n_elts)
45 {
46   return 1 << (max_log2 (n_elts >> 1) + 1);
47 }
48
49 static u32
50 ad_flow_calc_bihash_memory (u32 n_buckets, uword kv_size)
51 {
52   return n_buckets * (8 + kv_size * 4);
53 }
54
55 /*****************************************/
56 /* SRv6 LocalSID instantiation and removal functions */
57 static int
58 srv6_ad_flow_localsid_creation_fn (ip6_sr_localsid_t *localsid)
59 {
60   ip6_sr_main_t *srm = &sr_main;
61   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
62   srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
63   u32 localsid_index = localsid - srm->localsids;
64
65   /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
66
67   /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
68   adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
69   if (ls_mem->inner_type == AD_TYPE_IP4)
70     nh_adj_index =
71       adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, &ls_mem->nh_addr,
72                            ls_mem->sw_if_index_out);
73   else if (ls_mem->inner_type == AD_TYPE_IP6)
74     nh_adj_index =
75       adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &ls_mem->nh_addr,
76                            ls_mem->sw_if_index_out);
77
78   if (nh_adj_index == ADJ_INDEX_INVALID)
79     {
80       clib_mem_free (ls_mem);
81       return SID_CREATE_INVALID_ADJ_INDEX;
82     }
83
84   ls_mem->nh_adj = nh_adj_index;
85
86   /* Step 2: Prepare inbound policy for packets returning from the VNF */
87
88   /* Sanitise the SW_IF_INDEX */
89   if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
90                           ls_mem->sw_if_index_in))
91     {
92       adj_unlock (ls_mem->nh_adj);
93       clib_mem_free (ls_mem);
94       return SID_CREATE_INVALID_IFACE_INDEX;
95     }
96
97
98   if (ls_mem->inner_type == AD_TYPE_IP4)
99     {
100       /* Enable End.AD4 rewrite node for this interface */
101       int ret =
102         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
103                                      ls_mem->sw_if_index_in, 1, 0, 0);
104       if (ret != 0)
105         {
106           adj_unlock (ls_mem->nh_adj);
107           clib_mem_free (ls_mem);
108           return SID_CREATE_IFACE_FEATURE_ERROR;
109         }
110
111       /* Associate local SID index to this interface (resize vector if needed)
112        */
113       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
114         {
115           vec_resize (sm->sw_iface_localsid4,
116                       (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
117                        vec_len (sm->sw_iface_localsid4)));
118         }
119       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
120     }
121   else if (ls_mem->inner_type == AD_TYPE_IP6)
122     {
123       /* Enable End.AD6 rewrite node for this interface */
124       int ret =
125         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
126                                      ls_mem->sw_if_index_in, 1, 0, 0);
127       if (ret != 0)
128         {
129           adj_unlock (ls_mem->nh_adj);
130           clib_mem_free (ls_mem);
131           return SID_CREATE_IFACE_FEATURE_ERROR;
132         }
133
134       /* Associate local SID index to this interface (resize vector if needed)
135        */
136       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
137         {
138           vec_resize (sm->sw_iface_localsid6,
139                       (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
140                        vec_len (sm->sw_iface_localsid6)));
141         }
142       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
143     }
144
145   /* Initialize flow and cache tables */
146   ls_mem->cache_size = SRV6_AD_FLOW_DEFAULT_CACHE_SIZE;
147   ls_mem->cache_buckets = ad_flow_calc_bihash_buckets (ls_mem->cache_size);
148   ls_mem->cache_memory_size = ad_flow_calc_bihash_memory (
149     ls_mem->cache_buckets, sizeof (clib_bihash_40_8_t));
150
151   pool_alloc (ls_mem->cache, ls_mem->cache_size);
152   pool_alloc (ls_mem->lru_pool, ls_mem->cache_size);
153
154   dlist_elt_t *head;
155   pool_get (ls_mem->lru_pool, head);
156   ls_mem->lru_head_index = head - ls_mem->lru_pool;
157   clib_dlist_init (ls_mem->lru_pool, ls_mem->lru_head_index);
158
159   clib_bihash_init_40_8 (&ls_mem->ftable, "ad-flow", ls_mem->cache_buckets,
160                          ls_mem->cache_memory_size);
161
162   /* Step 3: Initialize rewrite counters */
163   srv6_ad_flow_localsid_t **ls_p;
164   pool_get (sm->sids, ls_p);
165   *ls_p = ls_mem;
166   ls_mem->index = ls_p - sm->sids;
167
168   vlib_validate_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
169   vlib_validate_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
170   vlib_validate_combined_counter (&(sm->sid_cache_full_counters),
171                                   ls_mem->index);
172   vlib_validate_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
173   vlib_validate_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
174
175   vlib_zero_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
176   vlib_zero_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
177   vlib_zero_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index);
178   vlib_zero_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
179   vlib_zero_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
180
181   return 0;
182 }
183
184 static int
185 srv6_ad_flow_localsid_removal_fn (ip6_sr_localsid_t *localsid)
186 {
187   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
188   srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
189
190   if (ls_mem->inner_type == AD_TYPE_IP4)
191     {
192       /* Disable End.AD4 rewrite node for this interface */
193       int ret =
194         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
195                                      ls_mem->sw_if_index_in, 0, 0, 0);
196       if (ret != 0)
197         return -1;
198
199       /* Remove local SID pointer from interface table */
200       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
201     }
202   else if (ls_mem->inner_type == AD_TYPE_IP6)
203     {
204       /* Disable End.AD6 rewrite node for this interface */
205       int ret =
206         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
207                                      ls_mem->sw_if_index_in, 0, 0, 0);
208       if (ret != 0)
209         return -1;
210
211       /* Remove local SID pointer from interface table */
212       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
213     }
214
215   /* Unlock (OIF, NHOP) adjacency */
216   adj_unlock (ls_mem->nh_adj);
217
218   /* Delete SID entry */
219   pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
220
221   /* Clean up local SID memory */
222   srv6_ad_flow_entry_t *e;
223   pool_foreach (e, ls_mem->cache)
224     {
225       vec_free (e->rw_data);
226     }
227   pool_free (ls_mem->cache);
228   pool_free (ls_mem->lru_pool);
229   clib_bihash_free_40_8 (&ls_mem->ftable);
230   clib_mem_free (localsid->plugin_mem);
231
232   return 0;
233 }
234
235 /**********************************/
236 /* SRv6 LocalSID format functions */
237 /*
238  * Prints nicely the parameters of a localsid
239  * Example: print "Table 5"
240  */
241 u8 *
242 format_srv6_ad_flow_localsid (u8 *s, va_list *args)
243 {
244   srv6_ad_flow_localsid_t *ls_mem = va_arg (*args, void *);
245
246   vnet_main_t *vnm = vnet_get_main ();
247   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
248
249   if (ls_mem->inner_type == AD_TYPE_IP4)
250     {
251       s = format (s, "Next-hop:\t%U\n\t", format_ip4_address,
252                   &ls_mem->nh_addr.ip4);
253     }
254   else if (ls_mem->inner_type == AD_TYPE_IP6)
255     {
256       s = format (s, "Next-hop:\t%U\n\t", format_ip6_address,
257                   &ls_mem->nh_addr.ip6);
258     }
259
260   s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
261               ls_mem->sw_if_index_out);
262   s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
263               ls_mem->sw_if_index_in);
264
265   vlib_counter_t sid_bypass, sid_punt, sid_full, rw_valid, rw_invalid;
266   vlib_get_combined_counter (&(sm->sid_bypass_counters), ls_mem->index,
267                              &sid_bypass);
268   vlib_get_combined_counter (&(sm->sid_punt_counters), ls_mem->index,
269                              &sid_punt);
270   vlib_get_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index,
271                              &sid_full);
272   vlib_get_combined_counter (&(sm->rw_valid_counters), ls_mem->index,
273                              &rw_valid);
274   vlib_get_combined_counter (&(sm->rw_invalid_counters), ls_mem->index,
275                              &rw_invalid);
276
277   s =
278     format (s, "\tTraffic that bypassed the NF: \t[%Ld packets : %Ld bytes]\n",
279             sid_bypass.packets, sid_bypass.bytes);
280   s = format (s, "\tPunted traffic: \t[%Ld packets : %Ld bytes]\n",
281               sid_punt.packets, sid_punt.bytes);
282   s =
283     format (s, "\tDropped traffic (cache full): \t[%Ld packets : %Ld bytes]\n",
284             sid_full.packets, sid_full.bytes);
285   s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
286               rw_valid.packets, rw_valid.bytes);
287   s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
288               rw_invalid.packets, rw_invalid.bytes);
289
290   return s;
291 }
292
293 /*
294  * Process the parameters of a localsid
295  * Example: process from:
296  * sr localsid address cafe::1 behavior new_srv6_localsid 5
297  * everything from behavior on... so in this case 'new_srv6_localsid 5'
298  * Notice that it MUST match the keyword_str and params_str defined above.
299  */
300 uword
301 unformat_srv6_ad_flow_localsid (unformat_input_t *input, va_list *args)
302 {
303   void **plugin_mem_p = va_arg (*args, void **);
304   srv6_ad_flow_localsid_t *ls_mem;
305
306   vnet_main_t *vnm = vnet_get_main ();
307
308   u8 inner_type = AD_TYPE_IP4;
309   ip46_address_t nh_addr;
310   u32 sw_if_index_out;
311   u32 sw_if_index_in;
312
313   u8 params = 0;
314 #define PARAM_AD_NH  (1 << 0)
315 #define PARAM_AD_OIF (1 << 1)
316 #define PARAM_AD_IIF (1 << 2)
317
318   if (!unformat (input, "end.ad.flow"))
319     return 0;
320
321   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
322     {
323       if (!(params & PARAM_AD_NH) &&
324           unformat (input, "nh %U", unformat_ip4_address, &nh_addr.ip4))
325         {
326           inner_type = AD_TYPE_IP4;
327           params |= PARAM_AD_NH;
328         }
329       if (!(params & PARAM_AD_NH) &&
330           unformat (input, "nh %U", unformat_ip6_address, &nh_addr.ip6))
331         {
332           inner_type = AD_TYPE_IP6;
333           params |= PARAM_AD_NH;
334         }
335       else if (!(params & PARAM_AD_OIF) &&
336                unformat (input, "oif %U", unformat_vnet_sw_interface, vnm,
337                          &sw_if_index_out))
338         {
339           params |= PARAM_AD_OIF;
340         }
341       else if (!(params & PARAM_AD_IIF) &&
342                unformat (input, "iif %U", unformat_vnet_sw_interface, vnm,
343                          &sw_if_index_in))
344         {
345           params |= PARAM_AD_IIF;
346         }
347       else
348         {
349           break;
350         }
351     }
352
353   /* Make sure that all parameters are supplied */
354   u8 params_chk = (PARAM_AD_NH | PARAM_AD_OIF | PARAM_AD_IIF);
355   if ((params & params_chk) != params_chk)
356     {
357       return 0;
358     }
359
360   /* Allocate and initialize memory block for local SID parameters */
361   ls_mem = clib_mem_alloc (sizeof *ls_mem);
362   clib_memset (ls_mem, 0, sizeof *ls_mem);
363   *plugin_mem_p = ls_mem;
364
365   /* Set local SID parameters */
366   ls_mem->inner_type = inner_type;
367   if (inner_type == AD_TYPE_IP4)
368     ls_mem->nh_addr.ip4 = nh_addr.ip4;
369   else if (inner_type == AD_TYPE_IP6)
370     ls_mem->nh_addr.ip6 = nh_addr.ip6;
371   ls_mem->sw_if_index_out = sw_if_index_out;
372   ls_mem->sw_if_index_in = sw_if_index_in;
373
374   return 1;
375 }
376
377 /*************************/
378 /* SRv6 LocalSID FIB DPO */
379 static u8 *
380 format_srv6_ad_flow_dpo (u8 *s, va_list *args)
381 {
382   index_t index = va_arg (*args, index_t);
383   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
384
385   return (format (s, "SR: dynamic_proxy_index:[%u]", index));
386 }
387
388 void
389 srv6_ad_flow_dpo_lock (dpo_id_t *dpo)
390 {
391 }
392
393 void
394 srv6_ad_flow_dpo_unlock (dpo_id_t *dpo)
395 {
396 }
397
398 const static dpo_vft_t srv6_ad_flow_vft = {
399   .dv_lock = srv6_ad_flow_dpo_lock,
400   .dv_unlock = srv6_ad_flow_dpo_unlock,
401   .dv_format = format_srv6_ad_flow_dpo,
402 };
403
404 const static char *const srv6_ad_flow_ip6_nodes[] = {
405   "srv6-ad-flow-localsid",
406   NULL,
407 };
408
409 const static char *const *const srv6_ad_flow_nodes[DPO_PROTO_NUM] = {
410   [DPO_PROTO_IP6] = srv6_ad_flow_ip6_nodes,
411 };
412
413 /**********************/
414 static clib_error_t *
415 srv6_ad_flow_init (vlib_main_t *vm)
416 {
417   srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
418   int rv = 0;
419
420   sm->vlib_main = vm;
421   sm->vnet_main = vnet_get_main ();
422
423   /* Create DPO */
424   sm->srv6_ad_flow_dpo_type =
425     dpo_register_new_type (&srv6_ad_flow_vft, srv6_ad_flow_nodes);
426
427   /* Register SRv6 LocalSID */
428   rv = sr_localsid_register_function (
429     vm, function_name, keyword_str, def_str, params_str, 128,
430     &sm->srv6_ad_flow_dpo_type, format_srv6_ad_flow_localsid,
431     unformat_srv6_ad_flow_localsid, srv6_ad_flow_localsid_creation_fn,
432     srv6_ad_flow_localsid_removal_fn);
433   if (rv < 0)
434     clib_error_return (0, "SRv6 LocalSID function could not be registered.");
435   else
436     sm->srv6_localsid_behavior_id = rv;
437
438   return 0;
439 }
440
441 VNET_FEATURE_INIT (srv6_ad4_flow_rewrite, static) = {
442   .arc_name = "ip4-unicast",
443   .node_name = "srv6-ad4-flow-rewrite",
444   .runs_before = 0,
445 };
446
447 VNET_FEATURE_INIT (srv6_ad6_flow_rewrite, static) = {
448   .arc_name = "ip6-unicast",
449   .node_name = "srv6-ad6-flow-rewrite",
450   .runs_before = 0,
451 };
452
453 VLIB_INIT_FUNCTION (srv6_ad_flow_init);
454
455 VLIB_PLUGIN_REGISTER () = {
456   .version = VPP_BUILD_VER,
457   .description = "Dynamic Segment Routing for IPv6 (SRv6) Proxy",
458 };
459
460 /*
461  * fd.io coding-style-patch-verification: ON
462  *
463  * Local Variables:
464  * eval: (c-set-style "gnu")
465  * End:
466  */