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