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