gbp: Add support for flow hash profile
[vpp.git] / src / plugins / gbp / gbp_contract.c
1 /*
2  * gbp.h : Group Based Policy
3  *
4  * Copyright (c) 2018 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <plugins/gbp/gbp.h>
19 #include <plugins/gbp/gbp_bridge_domain.h>
20 #include <plugins/gbp/gbp_route_domain.h>
21 #include <plugins/gbp/gbp_policy_dpo.h>
22
23 #include <vnet/dpo/load_balance.h>
24 #include <vnet/dpo/drop_dpo.h>
25
26 /**
27  * Single contract DB instance
28  */
29 gbp_contract_db_t gbp_contract_db;
30
31 gbp_contract_t *gbp_contract_pool;
32
33 vlib_log_class_t gc_logger;
34
35 fib_node_type_t gbp_next_hop_fib_type;
36
37 gbp_rule_t *gbp_rule_pool;
38 gbp_next_hop_t *gbp_next_hop_pool;
39
40 #define GBP_CONTRACT_DBG(...)                           \
41     vlib_log_notice (gc_logger, __VA_ARGS__);
42
43 index_t
44 gbp_rule_alloc (gbp_rule_action_t action,
45                 gbp_hash_mode_t hash_mode, index_t * nhs)
46 {
47   gbp_rule_t *gu;
48
49   pool_get_zero (gbp_rule_pool, gu);
50
51   gu->gu_hash_mode = hash_mode;
52   gu->gu_nhs = nhs;
53   gu->gu_action = action;
54
55   return (gu - gbp_rule_pool);
56 }
57
58 index_t
59 gbp_next_hop_alloc (const ip46_address_t * ip,
60                     index_t grd, const mac_address_t * mac, index_t gbd)
61 {
62   fib_protocol_t fproto;
63   gbp_next_hop_t *gnh;
64
65   pool_get_zero (gbp_next_hop_pool, gnh);
66
67   fib_node_init (&gnh->gnh_node, gbp_next_hop_fib_type);
68
69   ip46_address_copy (&gnh->gnh_ip, ip);
70   mac_address_copy (&gnh->gnh_mac, mac);
71
72   gnh->gnh_rd = grd;
73   gnh->gnh_bd = gbd;
74
75   FOR_EACH_FIB_IP_PROTOCOL (fproto) gnh->gnh_ai[fproto] = INDEX_INVALID;
76
77   return (gnh - gbp_next_hop_pool);
78 }
79
80 static inline gbp_next_hop_t *
81 gbp_next_hop_get (index_t gui)
82 {
83   return (pool_elt_at_index (gbp_next_hop_pool, gui));
84 }
85
86 static void
87 gbp_contract_rules_free (index_t * rules)
88 {
89   index_t *gui, *gnhi;
90
91   vec_foreach (gui, rules)
92   {
93     gbp_policy_node_t pnode;
94     fib_protocol_t fproto;
95     gbp_next_hop_t *gnh;
96     gbp_rule_t *gu;
97
98     gu = gbp_rule_get (*gui);
99
100     FOR_EACH_GBP_POLICY_NODE (pnode)
101     {
102       FOR_EACH_FIB_IP_PROTOCOL (fproto)
103       {
104         dpo_reset (&gu->gu_dpo[pnode][fproto]);
105         dpo_reset (&gu->gu_dpo[pnode][fproto]);
106       }
107     }
108
109     vec_foreach (gnhi, gu->gu_nhs)
110     {
111       fib_protocol_t fproto;
112
113       gnh = gbp_next_hop_get (*gnhi);
114       gbp_bridge_domain_unlock (gnh->gnh_bd);
115       gbp_route_domain_unlock (gnh->gnh_rd);
116       gbp_endpoint_child_remove (gnh->gnh_ge, gnh->gnh_sibling);
117       gbp_endpoint_unlock (GBP_ENDPOINT_SRC_RR, gnh->gnh_ge);
118
119       FOR_EACH_FIB_IP_PROTOCOL (fproto)
120       {
121         adj_unlock (gnh->gnh_ai[fproto]);
122       }
123     }
124   }
125   vec_free (rules);
126 }
127
128 static u8 *
129 format_gbp_next_hop (u8 * s, va_list * args)
130 {
131   index_t gnhi = va_arg (*args, index_t);
132   gbp_next_hop_t *gnh;
133
134   gnh = gbp_next_hop_get (gnhi);
135
136   s = format (s, "%U, %U, %U EP:%d",
137               format_mac_address_t, &gnh->gnh_mac,
138               format_gbp_bridge_domain, gnh->gnh_bd,
139               format_ip46_address, &gnh->gnh_ip, IP46_TYPE_ANY, gnh->gnh_ge);
140
141   return (s);
142 }
143
144 static u8 *
145 format_gbp_rule_action (u8 * s, va_list * args)
146 {
147   gbp_rule_action_t action = va_arg (*args, gbp_rule_action_t);
148
149   switch (action)
150     {
151 #define _(v,a) case GBP_RULE_##v: return (format (s, "%s", a));
152       foreach_gbp_rule_action
153 #undef _
154     }
155
156   return (format (s, "unknown"));
157 }
158
159 static u8 *
160 format_gbp_hash_mode (u8 * s, va_list * args)
161 {
162   gbp_hash_mode_t hash_mode = va_arg (*args, gbp_hash_mode_t);
163
164   switch (hash_mode)
165     {
166 #define _(v,a) case GBP_HASH_MODE_##v: return (format (s, "%s", a));
167       foreach_gbp_hash_mode
168 #undef _
169     }
170
171   return (format (s, "unknown"));
172 }
173
174 static u8 *
175 format_gbp_policy_node (u8 * s, va_list * args)
176 {
177   gbp_policy_node_t action = va_arg (*args, gbp_policy_node_t);
178
179   switch (action)
180     {
181 #define _(v,a) case GBP_POLICY_NODE_##v: return (format (s, "%s", a));
182       foreach_gbp_policy_node
183 #undef _
184     }
185
186   return (format (s, "unknown"));
187 }
188
189 static u8 *
190 format_gbp_rule (u8 * s, va_list * args)
191 {
192   index_t gui = va_arg (*args, index_t);
193   gbp_policy_node_t pnode;
194   fib_protocol_t fproto;
195   gbp_rule_t *gu;
196   index_t *gnhi;
197
198   gu = gbp_rule_get (gui);
199   s = format (s, "%U", format_gbp_rule_action, gu->gu_action);
200
201   switch (gu->gu_action)
202     {
203     case GBP_RULE_PERMIT:
204     case GBP_RULE_DENY:
205       break;
206     case GBP_RULE_REDIRECT:
207       s = format (s, ", %U", format_gbp_hash_mode, gu->gu_hash_mode);
208       break;
209     }
210
211   vec_foreach (gnhi, gu->gu_nhs)
212   {
213     s = format (s, "\n      [%U]", format_gbp_next_hop, *gnhi);
214   }
215
216   FOR_EACH_GBP_POLICY_NODE (pnode)
217   {
218     s = format (s, "\n    policy-%U", format_gbp_policy_node, pnode);
219
220     FOR_EACH_FIB_IP_PROTOCOL (fproto)
221     {
222       if (dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
223         {
224           s =
225             format (s, "\n      %U", format_dpo_id,
226                     &gu->gu_dpo[pnode][fproto], 8);
227         }
228     }
229   }
230
231   return (s);
232 }
233
234 static void
235 gbp_contract_mk_adj (gbp_next_hop_t * gnh, fib_protocol_t fproto)
236 {
237   ethernet_header_t *eth;
238   gbp_endpoint_t *ge;
239   index_t old_ai;
240   u8 *rewrite;
241
242   old_ai = gnh->gnh_ai[fproto];
243   rewrite = NULL;
244   vec_validate (rewrite, sizeof (*eth) - 1);
245   eth = (ethernet_header_t *) rewrite;
246
247   GBP_CONTRACT_DBG ("...mk-adj: %U", format_gbp_next_hop,
248                     gnh - gbp_next_hop_pool);
249
250   ge = gbp_endpoint_get (gnh->gnh_ge);
251
252   eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
253                                      ETHERNET_TYPE_IP4 : ETHERNET_TYPE_IP6));
254   mac_address_to_bytes (gbp_route_domain_get_local_mac (), eth->src_address);
255   mac_address_to_bytes (&gnh->gnh_mac, eth->dst_address);
256
257   gnh->gnh_ai[fproto] =
258     adj_nbr_add_or_lock_w_rewrite (fproto,
259                                    fib_proto_to_link (fproto),
260                                    &gnh->gnh_ip, ge->ge_fwd.gef_itf, rewrite);
261
262   adj_unlock (old_ai);
263 }
264
265 static flow_hash_config_t
266 gbp_contract_mk_lb_hp (gbp_hash_mode_t gu_hash_mode)
267 {
268   switch (gu_hash_mode)
269     {
270     case GBP_HASH_MODE_SRC_IP:
271       return IP_FLOW_HASH_SRC_ADDR;
272     case GBP_HASH_MODE_DST_IP:
273       return IP_FLOW_HASH_DST_ADDR;
274     case GBP_HASH_MODE_SYMMETRIC:
275       return (IP_FLOW_HASH_SRC_ADDR | IP_FLOW_HASH_DST_ADDR |
276               IP_FLOW_HASH_PROTO | IP_FLOW_HASH_SYMMETRIC);
277     }
278
279   return 0;
280 }
281
282 static void
283 gbp_contract_mk_lb (index_t gui, fib_protocol_t fproto)
284 {
285   load_balance_path_t *paths = NULL;
286   gbp_policy_node_t pnode;
287   gbp_next_hop_t *gnh;
288   dpo_proto_t dproto;
289   gbp_rule_t *gu;
290   u32 ii;
291
292   u32 policy_nodes[] = {
293     [GBP_POLICY_NODE_L2] = gbp_policy_port_node.index,
294     [GBP_POLICY_NODE_IP4] = ip4_gbp_policy_dpo_node.index,
295     [GBP_POLICY_NODE_IP6] = ip6_gbp_policy_dpo_node.index,
296   };
297
298   GBP_CONTRACT_DBG ("..mk-lb: %U", format_gbp_rule, gui);
299
300   gu = gbp_rule_get (gui);
301   dproto = fib_proto_to_dpo (fproto);
302
303   if (GBP_RULE_REDIRECT != gu->gu_action)
304     return;
305
306   vec_foreach_index (ii, gu->gu_nhs)
307   {
308     gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
309
310     gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP4);
311     gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP6);
312   }
313
314   FOR_EACH_GBP_POLICY_NODE (pnode)
315   {
316     vec_validate (paths, vec_len (gu->gu_nhs) - 1);
317
318     vec_foreach_index (ii, gu->gu_nhs)
319     {
320       gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
321
322       paths[ii].path_index = FIB_NODE_INDEX_INVALID;
323       paths[ii].path_weight = 1;
324       dpo_set (&paths[ii].path_dpo, DPO_ADJACENCY,
325                dproto, gnh->gnh_ai[fproto]);
326     }
327
328     if (!dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
329       {
330         dpo_id_t dpo = DPO_INVALID;
331
332         dpo_set (&dpo, DPO_LOAD_BALANCE, dproto,
333                  load_balance_create (vec_len (paths),
334                                       dproto,
335                                       gbp_contract_mk_lb_hp
336                                       (gu->gu_hash_mode)));
337         dpo_stack_from_node (policy_nodes[pnode], &gu->gu_dpo[pnode][fproto],
338                              &dpo);
339         dpo_reset (&dpo);
340       }
341
342     load_balance_multipath_update (&gu->gu_dpo[pnode][fproto],
343                                    paths, LOAD_BALANCE_FLAG_NONE);
344     vec_free (paths);
345   }
346 }
347
348 static void
349 gbp_contract_mk_one_lb (index_t gui)
350 {
351   gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP4);
352   gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP6);
353 }
354
355 static int
356 gbp_contract_next_hop_resolve (index_t gui, index_t gnhi)
357 {
358   gbp_bridge_domain_t *gbd;
359   gbp_next_hop_t *gnh;
360   ip46_address_t *ips;
361   int rv;
362
363   ips = NULL;
364   gnh = gbp_next_hop_get (gnhi);
365   gbd = gbp_bridge_domain_get (gnh->gnh_bd);
366
367   gnh->gnh_gu = gui;
368   vec_add1 (ips, gnh->gnh_ip);
369
370   /*
371    * source the endpoint this contract needs to forward via.
372    * give ofrwarding details via the spine proxy. if this EP is known
373    * to us, then since we source here with a low priority, the learned
374    * info will take precedenc.
375    */
376   rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_RR,
377                                      gbd->gb_uu_fwd_sw_if_index,
378                                      ips,
379                                      &gnh->gnh_mac,
380                                      gnh->gnh_bd, gnh->gnh_rd, EPG_INVALID,
381                                      GBP_ENDPOINT_FLAG_NONE, NULL, NULL,
382                                      &gnh->gnh_ge);
383
384   if (0 == rv)
385     {
386       gnh->gnh_sibling = gbp_endpoint_child_add (gnh->gnh_ge,
387                                                  gbp_next_hop_fib_type, gnhi);
388     }
389
390   GBP_CONTRACT_DBG ("..resolve: %d: %d: %U", gui, gnhi, format_gbp_next_hop,
391                     gnhi);
392
393   vec_free (ips);
394   return (rv);
395 }
396
397 static void
398 gbp_contract_rule_resolve (index_t gui)
399 {
400   gbp_rule_t *gu;
401   index_t *gnhi;
402
403   gu = gbp_rule_get (gui);
404
405   GBP_CONTRACT_DBG ("..resolve: %U", format_gbp_rule, gui);
406
407   vec_foreach (gnhi, gu->gu_nhs)
408   {
409     gbp_contract_next_hop_resolve (gui, *gnhi);
410   }
411 }
412
413 static void
414 gbp_contract_resolve (index_t * guis)
415 {
416   index_t *gui;
417
418   vec_foreach (gui, guis)
419   {
420     gbp_contract_rule_resolve (*gui);
421   }
422 }
423
424 static void
425 gbp_contract_mk_lbs (index_t * guis)
426 {
427   index_t *gui;
428
429   vec_foreach (gui, guis)
430   {
431     gbp_contract_mk_one_lb (*gui);
432   }
433 }
434
435 int
436 gbp_contract_update (epg_id_t src_epg,
437                      epg_id_t dst_epg, u32 acl_index, index_t * rules)
438 {
439   gbp_main_t *gm = &gbp_main;
440   u32 *acl_vec = NULL;
441   gbp_contract_t *gc;
442   index_t gci;
443   uword *p;
444
445   gbp_contract_key_t key = {
446     .gck_src = src_epg,
447     .gck_dst = dst_epg,
448   };
449
450   if (~0 == gm->gbp_acl_user_id)
451     {
452       acl_plugin_exports_init (&gm->acl_plugin);
453       gm->gbp_acl_user_id =
454         gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg");
455     }
456
457   p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
458   if (p != NULL)
459     {
460       gci = p[0];
461       gc = gbp_contract_get (gci);
462       gbp_contract_rules_free (gc->gc_rules);
463       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
464       gc->gc_rules = NULL;
465     }
466   else
467     {
468       pool_get_zero (gbp_contract_pool, gc);
469       gc->gc_key = key;
470       gci = gc - gbp_contract_pool;
471       hash_set (gbp_contract_db.gc_hash, key.as_u32, gci);
472     }
473
474   GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci);
475
476   gc->gc_rules = rules;
477   gbp_contract_resolve (gc->gc_rules);
478   gbp_contract_mk_lbs (gc->gc_rules);
479
480   gc->gc_acl_index = acl_index;
481   gc->gc_lc_index =
482     gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id,
483                                              src_epg, dst_epg);
484
485   vec_add1 (acl_vec, gc->gc_acl_index);
486   gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec);
487   vec_free (acl_vec);
488
489   return (0);
490 }
491
492 int
493 gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg)
494 {
495   gbp_contract_key_t key = {
496     .gck_src = src_epg,
497     .gck_dst = dst_epg,
498   };
499   gbp_contract_t *gc;
500   uword *p;
501
502   p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
503   if (p != NULL)
504     {
505       gc = gbp_contract_get (p[0]);
506
507       gbp_contract_rules_free (gc->gc_rules);
508       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
509
510       hash_unset (gbp_contract_db.gc_hash, key.as_u32);
511       pool_put (gbp_contract_pool, gc);
512
513       return (0);
514     }
515
516   return (VNET_API_ERROR_NO_SUCH_ENTRY);
517 }
518
519 void
520 gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
521 {
522   gbp_contract_t *gc;
523
524   /* *INDENT-OFF* */
525   pool_foreach(gc, gbp_contract_pool,
526   ({
527     if (!cb(gc, ctx))
528       break;
529   }));
530   /* *INDENT-ON* */
531 }
532
533 static clib_error_t *
534 gbp_contract_cli (vlib_main_t * vm,
535                   unformat_input_t * input, vlib_cli_command_t * cmd)
536 {
537   epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID;
538   u32 acl_index = ~0;
539   u8 add = 1;
540
541   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
542     {
543       if (unformat (input, "add"))
544         add = 1;
545       else if (unformat (input, "del"))
546         add = 0;
547       else if (unformat (input, "src-epg %d", &src_epg_id))
548         ;
549       else if (unformat (input, "dst-epg %d", &dst_epg_id))
550         ;
551       else if (unformat (input, "acl-index %d", &acl_index))
552         ;
553       else
554         break;
555     }
556
557   if (EPG_INVALID == src_epg_id)
558     return clib_error_return (0, "Source EPG-ID must be specified");
559   if (EPG_INVALID == dst_epg_id)
560     return clib_error_return (0, "Destination EPG-ID must be specified");
561
562   if (add)
563     {
564       gbp_contract_update (src_epg_id, dst_epg_id, acl_index, NULL);
565     }
566   else
567     {
568       gbp_contract_delete (src_epg_id, dst_epg_id);
569     }
570
571   return (NULL);
572 }
573
574 /*?
575  * Configure a GBP Contract
576  *
577  * @cliexpar
578  * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
579  * @cliexend
580  ?*/
581 /* *INDENT-OFF* */
582 VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
583 {
584   .path = "gbp contract",
585   .short_help =
586     "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
587   .function = gbp_contract_cli,
588 };
589 /* *INDENT-ON* */
590
591 static u8 *
592 format_gbp_contract_key (u8 * s, va_list * args)
593 {
594   gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *);
595
596   s = format (s, "{%d,%d}", gck->gck_src, gck->gck_dst);
597
598   return (s);
599 }
600
601 u8 *
602 format_gbp_contract (u8 * s, va_list * args)
603 {
604   index_t gci = va_arg (*args, index_t);
605   gbp_contract_t *gc;
606   index_t *gui;
607
608   gc = gbp_contract_get (gci);
609
610   s = format (s, "%U: acl-index:%d",
611               format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index);
612
613   vec_foreach (gui, gc->gc_rules)
614   {
615     s = format (s, "\n    %d: %U", *gui, format_gbp_rule, *gui);
616   }
617
618   return (s);
619 }
620
621 static clib_error_t *
622 gbp_contract_show (vlib_main_t * vm,
623                    unformat_input_t * input, vlib_cli_command_t * cmd)
624 {
625   index_t gci;
626
627   vlib_cli_output (vm, "Contracts:");
628
629   /* *INDENT-OFF* */
630   pool_foreach_index (gci, gbp_contract_pool,
631   ({
632     vlib_cli_output (vm, "  [%d] %U", gci, format_gbp_contract, gci);
633   }));
634   /* *INDENT-ON* */
635
636   return (NULL);
637 }
638
639 /*?
640  * Show Group Based Policy Contracts
641  *
642  * @cliexpar
643  * @cliexstart{show gbp contract}
644  * @cliexend
645  ?*/
646 /* *INDENT-OFF* */
647 VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
648   .path = "show gbp contract",
649   .short_help = "show gbp contract\n",
650   .function = gbp_contract_show,
651 };
652 /* *INDENT-ON* */
653
654 static fib_node_t *
655 gbp_next_hop_get_node (fib_node_index_t index)
656 {
657   gbp_next_hop_t *gnh;
658
659   gnh = gbp_next_hop_get (index);
660
661   return (&gnh->gnh_node);
662 }
663
664 static void
665 gbp_next_hop_last_lock_gone (fib_node_t * node)
666 {
667   ASSERT (0);
668 }
669
670 static gbp_next_hop_t *
671 gbp_next_hop_from_fib_node (fib_node_t * node)
672 {
673   ASSERT (gbp_next_hop_fib_type == node->fn_type);
674   return ((gbp_next_hop_t *) node);
675 }
676
677 static fib_node_back_walk_rc_t
678 gbp_next_hop_back_walk_notify (fib_node_t * node,
679                                fib_node_back_walk_ctx_t * ctx)
680 {
681   gbp_next_hop_t *gnh;
682
683   gnh = gbp_next_hop_from_fib_node (node);
684
685   gbp_contract_mk_one_lb (gnh->gnh_gu);
686
687   return (FIB_NODE_BACK_WALK_CONTINUE);
688 }
689
690 /*
691  * The FIB path's graph node virtual function table
692  */
693 static const fib_node_vft_t gbp_next_hop_vft = {
694   .fnv_get = gbp_next_hop_get_node,
695   .fnv_last_lock = gbp_next_hop_last_lock_gone,
696   .fnv_back_walk = gbp_next_hop_back_walk_notify,
697   // .fnv_mem_show = fib_path_memory_show,
698 };
699
700 static clib_error_t *
701 gbp_contract_init (vlib_main_t * vm)
702 {
703   gc_logger = vlib_log_register_class ("gbp", "con");
704   gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft);
705
706   return (NULL);
707 }
708
709 VLIB_INIT_FUNCTION (gbp_contract_init);
710
711 /*
712  * fd.io coding-style-patch-verification: ON
713  *
714  * Local Variables:
715  * eval: (c-set-style "gnu")
716  * End:
717  */