vxlan: multiarch optimization of vxlan
[vpp.git] / src / plugins / gbp / gbp_subnet.c
1 /*
2  * Copyright (c) 2018 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 <plugins/gbp/gbp.h>
17 #include <plugins/gbp/gbp_fwd_dpo.h>
18 #include <plugins/gbp/gbp_policy_dpo.h>
19 #include <plugins/gbp/gbp_route_domain.h>
20
21 #include <vnet/fib/fib_table.h>
22 #include <vnet/dpo/load_balance.h>
23
24 /**
25  * a key for the DB
26  */
27 typedef struct gbp_subnet_key_t_
28 {
29   fib_prefix_t gsk_pfx;
30   u32 gsk_fib_index;
31 } gbp_subnet_key_t;
32
33 /**
34  * Subnet
35  */
36 typedef struct gbp_subnet_t_
37 {
38   gbp_subnet_key_t *gs_key;
39   gbp_subnet_type_t gs_type;
40   index_t gs_rd;
41
42   union
43   {
44     struct
45     {
46       sclass_t gs_sclass;
47       u32 gs_sw_if_index;
48     } gs_stitched_external;
49     struct
50     {
51       sclass_t gs_sclass;
52     } gs_l3_out;
53   };
54
55   fib_node_index_t gs_fei;
56 } gbp_subnet_t;
57
58 /**
59  * A DB of the subnets; key={pfx,fib-index}
60  */
61 uword *gbp_subnet_db;
62
63 /**
64  * pool of subnets
65  */
66 gbp_subnet_t *gbp_subnet_pool;
67
68 static fib_source_t gbp_fib_source;
69
70 static index_t
71 gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx)
72 {
73   gbp_subnet_key_t key = {
74     .gsk_pfx = *pfx,
75     .gsk_fib_index = fib_index,
76   };
77   uword *p;
78
79   p = hash_get_mem (gbp_subnet_db, &key);
80
81   if (NULL != p)
82     return p[0];
83
84   return (INDEX_INVALID);
85 }
86
87 static void
88 gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs)
89 {
90   gbp_subnet_key_t *key;
91
92   key = clib_mem_alloc (sizeof (*key));
93
94   clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx));
95   key->gsk_fib_index = fib_index;
96
97   hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool));
98
99   gs->gs_key = key;
100 }
101
102 static void
103 gbp_subnet_db_del (gbp_subnet_t * gs)
104 {
105   hash_unset_mem (gbp_subnet_db, gs->gs_key);
106
107   clib_mem_free (gs->gs_key);
108   gs->gs_key = NULL;
109 }
110
111
112 static int
113 gbp_subnet_transport_add (gbp_subnet_t * gs)
114 {
115   dpo_id_t gfd = DPO_INVALID;
116   gbp_route_domain_t *grd;
117   fib_protocol_t fproto;
118
119   fproto = gs->gs_key->gsk_pfx.fp_proto;
120   grd = gbp_route_domain_get (gs->gs_rd);
121
122   if (~0 == grd->grd_uu_sw_if_index[fproto])
123     return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
124
125   gs->gs_fei = fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index,
126                                                 &gs->gs_key->gsk_pfx,
127                                                 gbp_fib_source,
128                                                 FIB_ENTRY_FLAG_NONE,
129                                                 fib_proto_to_dpo (fproto),
130                                                 &ADJ_BCAST_ADDR,
131                                                 grd->grd_uu_sw_if_index
132                                                 [fproto], ~0, 1, NULL,
133                                                 FIB_ROUTE_PATH_FLAG_NONE);
134
135   dpo_reset (&gfd);
136
137   return (0);
138 }
139
140 static int
141 gbp_subnet_internal_add (gbp_subnet_t * gs)
142 {
143   dpo_id_t gfd = DPO_INVALID;
144
145   gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
146                            &gfd);
147
148   gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
149                                                    &gs->gs_key->gsk_pfx,
150                                                    gbp_fib_source,
151                                                    FIB_ENTRY_FLAG_EXCLUSIVE,
152                                                    &gfd);
153
154   dpo_reset (&gfd);
155
156   return (0);
157 }
158
159 static int
160 gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, sclass_t sclass)
161 {
162   dpo_id_t gpd = DPO_INVALID;
163
164   gs->gs_stitched_external.gs_sclass = sclass;
165   gs->gs_stitched_external.gs_sw_if_index = sw_if_index;
166
167   gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
168                               gbp_route_domain_get_scope (gs->gs_rd),
169                               gs->gs_stitched_external.gs_sclass,
170                               gs->gs_stitched_external.gs_sw_if_index, &gpd);
171
172   gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
173                                                    &gs->gs_key->gsk_pfx,
174                                                    gbp_fib_source,
175                                                    (FIB_ENTRY_FLAG_EXCLUSIVE |
176                                                     FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT),
177                                                    &gpd);
178
179   dpo_reset (&gpd);
180
181   return (0);
182 }
183
184 static int
185 gbp_subnet_l3_out_add (gbp_subnet_t * gs, sclass_t sclass, int is_anon)
186 {
187   fib_entry_flag_t flags;
188   dpo_id_t gpd = DPO_INVALID;
189
190   gs->gs_l3_out.gs_sclass = sclass;
191
192   gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
193                               gbp_route_domain_get_scope (gs->gs_rd),
194                               gs->gs_l3_out.gs_sclass, ~0, &gpd);
195
196   flags = FIB_ENTRY_FLAG_INTERPOSE;
197   if (is_anon)
198     flags |= FIB_ENTRY_FLAG_COVERED_INHERIT;
199
200   gs->gs_fei = fib_table_entry_special_dpo_add (gs->gs_key->gsk_fib_index,
201                                                 &gs->gs_key->gsk_pfx,
202                                                 FIB_SOURCE_SPECIAL,
203                                                 flags, &gpd);
204
205   dpo_reset (&gpd);
206
207   return (0);
208 }
209
210 static void
211 gbp_subnet_del_i (index_t gsi)
212 {
213   gbp_subnet_t *gs;
214
215   gs = pool_elt_at_index (gbp_subnet_pool, gsi);
216
217   fib_table_entry_delete_index (gs->gs_fei,
218                                 (GBP_SUBNET_L3_OUT == gs->gs_type
219                                  || GBP_SUBNET_ANON_L3_OUT ==
220                                  gs->gs_type) ? FIB_SOURCE_SPECIAL :
221                                 gbp_fib_source);
222
223   gbp_subnet_db_del (gs);
224   gbp_route_domain_unlock (gs->gs_rd);
225
226   pool_put (gbp_subnet_pool, gs);
227 }
228
229 int
230 gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx)
231 {
232   gbp_route_domain_t *grd;
233   index_t gsi, grdi;
234   u32 fib_index;
235
236   grdi = gbp_route_domain_find (rd_id);
237
238   if (~0 == grdi)
239     return (VNET_API_ERROR_NO_SUCH_FIB);
240
241   grd = gbp_route_domain_get (grdi);
242   fib_index = grd->grd_fib_index[pfx->fp_proto];
243
244   gsi = gbp_subnet_db_find (fib_index, pfx);
245
246   if (INDEX_INVALID == gsi)
247     return (VNET_API_ERROR_NO_SUCH_ENTRY);
248
249   gbp_subnet_del_i (gsi);
250
251   return (0);
252 }
253
254 int
255 gbp_subnet_add (u32 rd_id,
256                 const fib_prefix_t * pfx,
257                 gbp_subnet_type_t type, u32 sw_if_index, sclass_t sclass)
258 {
259   gbp_route_domain_t *grd;
260   index_t grdi, gsi;
261   gbp_subnet_t *gs;
262   u32 fib_index;
263   int rv;
264
265   switch (type)
266     {
267     case GBP_SUBNET_TRANSPORT:
268     case GBP_SUBNET_STITCHED_INTERNAL:
269     case GBP_SUBNET_STITCHED_EXTERNAL:
270     case GBP_SUBNET_L3_OUT:
271     case GBP_SUBNET_ANON_L3_OUT:
272       break;
273     default:
274       return (VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE);
275     }
276
277   grdi = gbp_route_domain_find_and_lock (rd_id);
278
279   if (~0 == grdi)
280     return (VNET_API_ERROR_NO_SUCH_FIB);
281
282   grd = gbp_route_domain_get (grdi);
283   fib_index = grd->grd_fib_index[pfx->fp_proto];
284
285   gsi = gbp_subnet_db_find (fib_index, pfx);
286
287   /*
288    * this is an update if the subnet already exists, so remove the old
289    */
290   if (INDEX_INVALID != gsi)
291     gbp_subnet_del_i (gsi);
292
293   rv = -2;
294
295   pool_get (gbp_subnet_pool, gs);
296
297   gs->gs_type = type;
298   gs->gs_rd = grdi;
299   gbp_subnet_db_add (fib_index, pfx, gs);
300
301   switch (type)
302     {
303     case GBP_SUBNET_STITCHED_INTERNAL:
304       rv = gbp_subnet_internal_add (gs);
305       break;
306     case GBP_SUBNET_STITCHED_EXTERNAL:
307       rv = gbp_subnet_external_add (gs, sw_if_index, sclass);
308       break;
309     case GBP_SUBNET_TRANSPORT:
310       rv = gbp_subnet_transport_add (gs);
311       break;
312     case GBP_SUBNET_L3_OUT:
313       rv = gbp_subnet_l3_out_add (gs, sclass, 0 /* is_anon */ );
314       break;
315     case GBP_SUBNET_ANON_L3_OUT:
316       rv = gbp_subnet_l3_out_add (gs, sclass, 1 /* is_anon */ );
317       break;
318     }
319
320   return (rv);
321 }
322
323 static clib_error_t *
324 gbp_subnet_add_del_cli (vlib_main_t * vm,
325                         unformat_input_t * input, vlib_cli_command_t * cmd)
326 {
327   unformat_input_t _line_input, *line_input = &_line_input;
328   vnet_main_t *vnm = vnet_get_main ();
329   fib_prefix_t pfx = {.fp_addr = ip46_address_initializer };
330   int length;
331   u32 rd_id = ~0;
332   u32 sw_if_index = ~0;
333   gbp_subnet_type_t type = ~0;
334   u32 sclass = ~0;
335   int is_add = 1;
336   int rv;
337
338   /* Get a line of input. */
339   if (!unformat_user (input, unformat_line_input, line_input))
340     return 0;
341
342   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
343     {
344       if (unformat (line_input, "del"))
345         is_add = 0;
346       else if (unformat (line_input, "rd %d", &rd_id))
347         ;
348       else
349         if (unformat
350             (line_input, "prefix %U/%d", unformat_ip4_address,
351              &pfx.fp_addr.ip4, &length))
352         pfx.fp_proto = FIB_PROTOCOL_IP4;
353       else
354         if (unformat
355             (line_input, "prefix %U/%d", unformat_ip6_address,
356              &pfx.fp_addr.ip6, &length))
357         pfx.fp_proto = FIB_PROTOCOL_IP6;
358       else if (unformat (line_input, "type transport"))
359         type = GBP_SUBNET_TRANSPORT;
360       else if (unformat (line_input, "type stitched-internal"))
361         type = GBP_SUBNET_STITCHED_INTERNAL;
362       else if (unformat (line_input, "type stitched-external"))
363         type = GBP_SUBNET_STITCHED_EXTERNAL;
364       else if (unformat (line_input, "type anon-l3-out"))
365         type = GBP_SUBNET_ANON_L3_OUT;
366       else if (unformat (line_input, "type l3-out"))
367         type = GBP_SUBNET_L3_OUT;
368       else
369         if (unformat_user
370             (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
371         ;
372       else if (unformat (line_input, "sclass %u", &sclass))
373         ;
374       else
375         return clib_error_return (0, "unknown input `%U'",
376                                   format_unformat_error, line_input);
377     }
378   unformat_free (line_input);
379
380   pfx.fp_len = length;
381
382   if (is_add)
383     rv = gbp_subnet_add (rd_id, &pfx, type, sw_if_index, sclass);
384   else
385     rv = gbp_subnet_del (rd_id, &pfx);
386
387   switch (rv)
388     {
389     case 0:
390       return 0;
391     case VNET_API_ERROR_NO_SUCH_FIB:
392       return clib_error_return (0, "no such FIB");
393     }
394
395   return clib_error_return (0, "unknown error %d", rv);
396 }
397
398 /*?
399  * Add Group Based Policy Subnets
400  *
401  * @cliexpar
402  * @cliexstart{gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]}
403  * @cliexend
404  ?*/
405 /* *INDENT-OFF* */
406 VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = {
407   .path = "gbp subnet",
408   .short_help = "gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]\n",
409   .function = gbp_subnet_add_del_cli,
410 };
411 /* *INDENT-ON* */
412
413
414
415 void
416 gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx)
417 {
418   gbp_route_domain_t *grd;
419   gbp_subnet_t *gs;
420   u32 sw_if_index;
421   sclass_t sclass;
422
423   sclass = SCLASS_INVALID;
424   sw_if_index = ~0;
425
426   /* *INDENT-OFF* */
427   pool_foreach (gs, gbp_subnet_pool)
428    {
429     grd = gbp_route_domain_get(gs->gs_rd);
430
431     switch (gs->gs_type)
432       {
433       case GBP_SUBNET_STITCHED_INTERNAL:
434       case GBP_SUBNET_TRANSPORT:
435         /* use defaults above */
436         break;
437       case GBP_SUBNET_STITCHED_EXTERNAL:
438         sw_if_index = gs->gs_stitched_external.gs_sw_if_index;
439         sclass = gs->gs_stitched_external.gs_sclass;
440         break;
441       case GBP_SUBNET_L3_OUT:
442       case GBP_SUBNET_ANON_L3_OUT:
443         sclass = gs->gs_l3_out.gs_sclass;
444         break;
445       }
446
447     if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx,
448                          gs->gs_type, sw_if_index, sclass, ctx))
449       break;
450   }
451   /* *INDENT-ON* */
452 }
453
454 typedef enum gsb_subnet_show_flags_t_
455 {
456   GBP_SUBNET_SHOW_BRIEF,
457   GBP_SUBNET_SHOW_DETAILS,
458 } gsb_subnet_show_flags_t;
459
460 static u8 *
461 format_gbp_subnet_type (u8 * s, va_list * args)
462 {
463   gbp_subnet_type_t type = va_arg (*args, gbp_subnet_type_t);
464
465   switch (type)
466     {
467     case GBP_SUBNET_STITCHED_INTERNAL:
468       return (format (s, "stitched-internal"));
469     case GBP_SUBNET_STITCHED_EXTERNAL:
470       return (format (s, "stitched-external"));
471     case GBP_SUBNET_TRANSPORT:
472       return (format (s, "transport"));
473     case GBP_SUBNET_L3_OUT:
474       return (format (s, "l3-out"));
475     case GBP_SUBNET_ANON_L3_OUT:
476       return (format (s, "anon-l3-out"));
477     }
478
479   return (format (s, "unknown"));
480 }
481
482 u8 *
483 format_gbp_subnet (u8 * s, va_list * args)
484 {
485   index_t gsi = va_arg (*args, index_t);
486   gsb_subnet_show_flags_t flags = va_arg (*args, gsb_subnet_show_flags_t);
487   gbp_subnet_t *gs;
488   u32 table_id;
489
490   gs = pool_elt_at_index (gbp_subnet_pool, gsi);
491
492   table_id = fib_table_get_table_id (gs->gs_key->gsk_fib_index,
493                                      gs->gs_key->gsk_pfx.fp_proto);
494
495   s = format (s, "[%d] tbl:%d %U %U", gsi, table_id,
496               format_fib_prefix, &gs->gs_key->gsk_pfx,
497               format_gbp_subnet_type, gs->gs_type);
498
499   switch (gs->gs_type)
500     {
501     case GBP_SUBNET_STITCHED_INTERNAL:
502     case GBP_SUBNET_TRANSPORT:
503       break;
504     case GBP_SUBNET_STITCHED_EXTERNAL:
505       s = format (s, " {sclass:%d %U}", gs->gs_stitched_external.gs_sclass,
506                   format_vnet_sw_if_index_name,
507                   vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index);
508       break;
509     case GBP_SUBNET_L3_OUT:
510     case GBP_SUBNET_ANON_L3_OUT:
511       s = format (s, " {sclass:%d}", gs->gs_l3_out.gs_sclass);
512       break;
513     }
514
515   switch (flags)
516     {
517     case GBP_SUBNET_SHOW_DETAILS:
518       {
519         s = format (s, "\n  %U", format_fib_entry, gs->gs_fei,
520                     FIB_ENTRY_FORMAT_DETAIL);
521       }
522     case GBP_SUBNET_SHOW_BRIEF:
523       break;
524     }
525   return (s);
526 }
527
528 static clib_error_t *
529 gbp_subnet_show (vlib_main_t * vm,
530                  unformat_input_t * input, vlib_cli_command_t * cmd)
531 {
532   u32 gsi;
533
534   gsi = INDEX_INVALID;
535
536   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
537     {
538       if (unformat (input, "%d", &gsi))
539         ;
540       else
541         break;
542     }
543
544   if (INDEX_INVALID != gsi)
545     {
546       vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
547                        GBP_SUBNET_SHOW_DETAILS);
548     }
549   else
550     {
551       /* *INDENT-OFF* */
552       pool_foreach_index (gsi, gbp_subnet_pool)
553        {
554         vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
555                          GBP_SUBNET_SHOW_BRIEF);
556       }
557       /* *INDENT-ON* */
558     }
559
560   return (NULL);
561 }
562
563 /*?
564  * Show Group Based Policy Subnets
565  *
566  * @cliexpar
567  * @cliexstart{show gbp subnet}
568  * @cliexend
569  ?*/
570 /* *INDENT-OFF* */
571 VLIB_CLI_COMMAND (gbp_subnet_show_node, static) = {
572   .path = "show gbp subnet",
573   .short_help = "show gbp subnet\n",
574   .function = gbp_subnet_show,
575 };
576 /* *INDENT-ON* */
577
578 static clib_error_t *
579 gbp_subnet_init (vlib_main_t * vm)
580 {
581   gbp_subnet_db = hash_create_mem (0,
582                                    sizeof (gbp_subnet_key_t), sizeof (u32));
583   gbp_fib_source = fib_source_allocate ("gbp-subnet",
584                                         FIB_SOURCE_PRIORITY_HI,
585                                         FIB_SOURCE_BH_SIMPLE);
586
587   return (NULL);
588 }
589
590 VLIB_INIT_FUNCTION (gbp_subnet_init);
591
592 /*
593  * fd.io coding-style-patch-verification: ON
594  *
595  * Local Variables:
596  * eval: (c-set-style "gnu")
597  * End:
598  */