gbp: refactor policy nodes
[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, ge->ge_fwd.gef_itf, rewrite);
279
280   adj_unlock (old_ai);
281 }
282
283 static flow_hash_config_t
284 gbp_contract_mk_lb_hp (gbp_hash_mode_t gu_hash_mode)
285 {
286   switch (gu_hash_mode)
287     {
288     case GBP_HASH_MODE_SRC_IP:
289       return IP_FLOW_HASH_SRC_ADDR;
290     case GBP_HASH_MODE_DST_IP:
291       return IP_FLOW_HASH_DST_ADDR;
292     case GBP_HASH_MODE_SYMMETRIC:
293       return (IP_FLOW_HASH_SRC_ADDR | IP_FLOW_HASH_DST_ADDR |
294               IP_FLOW_HASH_PROTO | IP_FLOW_HASH_SYMMETRIC);
295     }
296
297   return 0;
298 }
299
300 static void
301 gbp_contract_mk_lb (index_t gui, fib_protocol_t fproto)
302 {
303   load_balance_path_t *paths = NULL;
304   gbp_policy_node_t pnode;
305   gbp_next_hop_t *gnh;
306   dpo_proto_t dproto;
307   gbp_rule_t *gu;
308   u32 ii;
309
310   u32 policy_nodes[] = {
311     [GBP_POLICY_NODE_L2] = gbp_policy_port_node.index,
312     [GBP_POLICY_NODE_IP4] = ip4_gbp_policy_dpo_node.index,
313     [GBP_POLICY_NODE_IP6] = ip6_gbp_policy_dpo_node.index,
314   };
315
316   GBP_CONTRACT_DBG ("..mk-lb: %U", format_gbp_rule, gui);
317
318   gu = gbp_rule_get (gui);
319   dproto = fib_proto_to_dpo (fproto);
320
321   if (GBP_RULE_REDIRECT != gu->gu_action)
322     return;
323
324   vec_foreach_index (ii, gu->gu_nhs)
325   {
326     gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
327
328     gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP4);
329     gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP6);
330   }
331
332   FOR_EACH_GBP_POLICY_NODE (pnode)
333   {
334     vec_validate (paths, vec_len (gu->gu_nhs) - 1);
335
336     vec_foreach_index (ii, gu->gu_nhs)
337     {
338       gnh = gbp_next_hop_get (gu->gu_nhs[ii]);
339
340       paths[ii].path_index = FIB_NODE_INDEX_INVALID;
341       paths[ii].path_weight = 1;
342       dpo_set (&paths[ii].path_dpo, DPO_ADJACENCY,
343                dproto, gnh->gnh_ai[fproto]);
344     }
345
346     if (!dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
347       {
348         dpo_id_t dpo = DPO_INVALID;
349
350         dpo_set (&dpo, DPO_LOAD_BALANCE, dproto,
351                  load_balance_create (vec_len (paths),
352                                       dproto,
353                                       gbp_contract_mk_lb_hp
354                                       (gu->gu_hash_mode)));
355         dpo_stack_from_node (policy_nodes[pnode], &gu->gu_dpo[pnode][fproto],
356                              &dpo);
357         dpo_reset (&dpo);
358       }
359
360     load_balance_multipath_update (&gu->gu_dpo[pnode][fproto],
361                                    paths, LOAD_BALANCE_FLAG_NONE);
362     vec_free (paths);
363   }
364 }
365
366 static void
367 gbp_contract_mk_one_lb (index_t gui)
368 {
369   gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP4);
370   gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP6);
371 }
372
373 static int
374 gbp_contract_next_hop_resolve (index_t gui, index_t gnhi)
375 {
376   gbp_bridge_domain_t *gbd;
377   gbp_next_hop_t *gnh;
378   ip46_address_t *ips;
379   int rv;
380
381   ips = NULL;
382   gnh = gbp_next_hop_get (gnhi);
383   gbd = gbp_bridge_domain_get (gnh->gnh_bd);
384
385   gnh->gnh_gu = gui;
386   vec_add1 (ips, gnh->gnh_ip);
387
388   /*
389    * source the endpoint this contract needs to forward via.
390    * give ofrwarding details via the spine proxy. if this EP is known
391    * to us, then since we source here with a low priority, the learned
392    * info will take precedenc.
393    */
394   rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_RR,
395                                      gbd->gb_uu_fwd_sw_if_index,
396                                      ips,
397                                      &gnh->gnh_mac,
398                                      gnh->gnh_bd, gnh->gnh_rd, SCLASS_INVALID,
399                                      GBP_ENDPOINT_FLAG_NONE, NULL, NULL,
400                                      &gnh->gnh_ge);
401
402   if (0 == rv)
403     {
404       gnh->gnh_sibling = gbp_endpoint_child_add (gnh->gnh_ge,
405                                                  gbp_next_hop_fib_type, gnhi);
406     }
407
408   GBP_CONTRACT_DBG ("..resolve: %d: %d: %U", gui, gnhi, format_gbp_next_hop,
409                     gnhi);
410
411   vec_free (ips);
412   return (rv);
413 }
414
415 static void
416 gbp_contract_rule_resolve (index_t gui)
417 {
418   gbp_rule_t *gu;
419   index_t *gnhi;
420
421   gu = gbp_rule_get (gui);
422
423   GBP_CONTRACT_DBG ("..resolve: %U", format_gbp_rule, gui);
424
425   vec_foreach (gnhi, gu->gu_nhs)
426   {
427     gbp_contract_next_hop_resolve (gui, *gnhi);
428   }
429 }
430
431 static void
432 gbp_contract_resolve (index_t * guis)
433 {
434   index_t *gui;
435
436   vec_foreach (gui, guis)
437   {
438     gbp_contract_rule_resolve (*gui);
439   }
440 }
441
442 static void
443 gbp_contract_mk_lbs (index_t * guis)
444 {
445   index_t *gui;
446
447   vec_foreach (gui, guis)
448   {
449     gbp_contract_mk_one_lb (*gui);
450   }
451 }
452
453 int
454 gbp_contract_update (gbp_scope_t scope,
455                      sclass_t sclass,
456                      sclass_t dclass,
457                      u32 acl_index,
458                      index_t * rules,
459                      u16 * allowed_ethertypes, u32 * stats_index)
460 {
461   gbp_main_t *gm = &gbp_main;
462   u32 *acl_vec = NULL;
463   gbp_contract_t *gc;
464   index_t gci;
465   uword *p;
466
467   gbp_contract_key_t key = {
468     .gck_scope = scope,
469     .gck_src = sclass,
470     .gck_dst = dclass,
471   };
472
473   if (~0 == gm->gbp_acl_user_id)
474     {
475       acl_plugin_exports_init (&gm->acl_plugin);
476       gm->gbp_acl_user_id =
477         gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg");
478     }
479
480   p = hash_get (gbp_contract_db.gc_hash, key.as_u64);
481   if (p != NULL)
482     {
483       gci = p[0];
484       gc = gbp_contract_get (gci);
485       gbp_contract_rules_free (gc->gc_rules);
486       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
487       gc->gc_rules = NULL;
488       vec_free (gc->gc_allowed_ethertypes);
489     }
490   else
491     {
492       pool_get_zero (gbp_contract_pool, gc);
493       gc->gc_key = key;
494       gci = gc - gbp_contract_pool;
495       hash_set (gbp_contract_db.gc_hash, key.as_u64, gci);
496
497       vlib_validate_combined_counter (&gbp_contract_drop_counters, gci);
498       vlib_zero_combined_counter (&gbp_contract_drop_counters, gci);
499       vlib_validate_combined_counter (&gbp_contract_permit_counters, gci);
500       vlib_zero_combined_counter (&gbp_contract_permit_counters, gci);
501     }
502
503   GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci);
504
505   gc->gc_rules = rules;
506   gc->gc_allowed_ethertypes = allowed_ethertypes;
507   gbp_contract_resolve (gc->gc_rules);
508   gbp_contract_mk_lbs (gc->gc_rules);
509
510   gc->gc_acl_index = acl_index;
511   gc->gc_lc_index =
512     gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id,
513                                              sclass, dclass);
514
515   vec_add1 (acl_vec, gc->gc_acl_index);
516   gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec);
517   vec_free (acl_vec);
518
519   *stats_index = gci;
520
521   return (0);
522 }
523
524 int
525 gbp_contract_delete (gbp_scope_t scope, sclass_t sclass, sclass_t dclass)
526 {
527   gbp_contract_key_t key = {
528     .gck_scope = scope,
529     .gck_src = sclass,
530     .gck_dst = dclass,
531   };
532   gbp_contract_t *gc;
533   uword *p;
534
535   p = hash_get (gbp_contract_db.gc_hash, key.as_u64);
536   if (p != NULL)
537     {
538       gc = gbp_contract_get (p[0]);
539
540       gbp_contract_rules_free (gc->gc_rules);
541       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
542       vec_free (gc->gc_allowed_ethertypes);
543
544       hash_unset (gbp_contract_db.gc_hash, key.as_u64);
545       pool_put (gbp_contract_pool, gc);
546
547       return (0);
548     }
549
550   return (VNET_API_ERROR_NO_SUCH_ENTRY);
551 }
552
553 void
554 gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
555 {
556   gbp_contract_t *gc;
557
558   /* *INDENT-OFF* */
559   pool_foreach(gc, gbp_contract_pool,
560   ({
561     if (!cb(gc, ctx))
562       break;
563   }));
564   /* *INDENT-ON* */
565 }
566
567 static clib_error_t *
568 gbp_contract_cli (vlib_main_t * vm,
569                   unformat_input_t * input, vlib_cli_command_t * cmd)
570 {
571   sclass_t sclass = SCLASS_INVALID, dclass = SCLASS_INVALID;
572   u32 acl_index = ~0, stats_index, scope;
573   u8 add = 1;
574
575   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
576     {
577       if (unformat (input, "add"))
578         add = 1;
579       else if (unformat (input, "del"))
580         add = 0;
581       else if (unformat (input, "scope %d", &scope))
582         ;
583       else if (unformat (input, "sclass %d", &sclass))
584         ;
585       else if (unformat (input, "dclass %d", &dclass))
586         ;
587       else if (unformat (input, "acl-index %d", &acl_index))
588         ;
589       else
590         break;
591     }
592
593   if (SCLASS_INVALID == sclass)
594     return clib_error_return (0, "Source EPG-ID must be specified");
595   if (SCLASS_INVALID == dclass)
596     return clib_error_return (0, "Destination EPG-ID must be specified");
597
598   if (add)
599     {
600       gbp_contract_update (scope, sclass, dclass, acl_index,
601                            NULL, NULL, &stats_index);
602     }
603   else
604     {
605       gbp_contract_delete (scope, sclass, dclass);
606     }
607
608   return (NULL);
609 }
610
611 /*?
612  * Configure a GBP Contract
613  *
614  * @cliexpar
615  * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
616  * @cliexend
617  ?*/
618 /* *INDENT-OFF* */
619 VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
620 {
621   .path = "gbp contract",
622   .short_help =
623     "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
624   .function = gbp_contract_cli,
625 };
626 /* *INDENT-ON* */
627
628 static u8 *
629 format_gbp_contract_key (u8 * s, va_list * args)
630 {
631   gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *);
632
633   s = format (s, "{%d,%d,%d}", gck->gck_scope, gck->gck_src, gck->gck_dst);
634
635   return (s);
636 }
637
638 u8 *
639 format_gbp_contract (u8 * s, va_list * args)
640 {
641   index_t gci = va_arg (*args, index_t);
642   vlib_counter_t counts;
643   gbp_contract_t *gc;
644   index_t *gui;
645   u16 *et;
646
647   gc = gbp_contract_get (gci);
648
649   s = format (s, "[%d] %U: acl-index:%d",
650               gci, format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index);
651
652   s = format (s, "\n    rules:");
653   vec_foreach (gui, gc->gc_rules)
654   {
655     s = format (s, "\n      %d: %U", *gui, format_gbp_rule, *gui);
656   }
657
658   s = format (s, "\n    allowed-ethertypes:");
659   s = format (s, "\n      [");
660   vec_foreach (et, gc->gc_allowed_ethertypes)
661   {
662     int host_et = clib_net_to_host_u16 (*et);
663     if (0 != host_et)
664       s = format (s, "0x%x, ", host_et);
665   }
666   s = format (s, "]");
667
668   s = format (s, "\n    stats:");
669   vlib_get_combined_counter (&gbp_contract_drop_counters, gci, &counts);
670   s = format (s, "\n      drop:[%Ld:%Ld]", counts.packets, counts.bytes);
671   vlib_get_combined_counter (&gbp_contract_permit_counters, gci, &counts);
672   s = format (s, "\n      permit:[%Ld:%Ld]", counts.packets, counts.bytes);
673
674   s = format (s, "]");
675
676   return (s);
677 }
678
679 static clib_error_t *
680 gbp_contract_show (vlib_main_t * vm,
681                    unformat_input_t * input, vlib_cli_command_t * cmd)
682 {
683   gbp_contract_t *gc;
684   u32 src, dst;
685   index_t gci;
686
687   src = dst = SCLASS_INVALID;
688
689   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
690     {
691       if (unformat (input, "src %d", &src))
692         ;
693       else if (unformat (input, "dst %d", &dst))
694         ;
695       else
696         break;
697     }
698
699   vlib_cli_output (vm, "Contracts:");
700
701   /* *INDENT-OFF* */
702   pool_foreach (gc, gbp_contract_pool,
703   ({
704     gci = gc - gbp_contract_pool;
705
706     if (SCLASS_INVALID != src && SCLASS_INVALID != dst)
707       {
708         if (gc->gc_key.gck_src == src &&
709             gc->gc_key.gck_dst == dst)
710           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
711       }
712     else if (SCLASS_INVALID != src)
713       {
714         if (gc->gc_key.gck_src == src)
715           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
716       }
717     else if (SCLASS_INVALID != dst)
718       {
719         if (gc->gc_key.gck_dst == dst)
720           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
721       }
722     else
723       vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
724   }));
725   /* *INDENT-ON* */
726
727   return (NULL);
728 }
729
730 /*?
731  * Show Group Based Policy Contracts
732  *
733  * @cliexpar
734  * @cliexstart{show gbp contract}
735  * @cliexend
736  ?*/
737 /* *INDENT-OFF* */
738 VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
739   .path = "show gbp contract",
740   .short_help = "show gbp contract [src <SRC>] [dst <DST>]\n",
741   .function = gbp_contract_show,
742 };
743 /* *INDENT-ON* */
744
745 static fib_node_t *
746 gbp_next_hop_get_node (fib_node_index_t index)
747 {
748   gbp_next_hop_t *gnh;
749
750   gnh = gbp_next_hop_get (index);
751
752   return (&gnh->gnh_node);
753 }
754
755 static void
756 gbp_next_hop_last_lock_gone (fib_node_t * node)
757 {
758   ASSERT (0);
759 }
760
761 static gbp_next_hop_t *
762 gbp_next_hop_from_fib_node (fib_node_t * node)
763 {
764   ASSERT (gbp_next_hop_fib_type == node->fn_type);
765   return ((gbp_next_hop_t *) node);
766 }
767
768 static fib_node_back_walk_rc_t
769 gbp_next_hop_back_walk_notify (fib_node_t * node,
770                                fib_node_back_walk_ctx_t * ctx)
771 {
772   gbp_next_hop_t *gnh;
773
774   gnh = gbp_next_hop_from_fib_node (node);
775
776   gbp_contract_mk_one_lb (gnh->gnh_gu);
777
778   return (FIB_NODE_BACK_WALK_CONTINUE);
779 }
780
781 /*
782  * The FIB path's graph node virtual function table
783  */
784 static const fib_node_vft_t gbp_next_hop_vft = {
785   .fnv_get = gbp_next_hop_get_node,
786   .fnv_last_lock = gbp_next_hop_last_lock_gone,
787   .fnv_back_walk = gbp_next_hop_back_walk_notify,
788   // .fnv_mem_show = fib_path_memory_show,
789 };
790
791 static clib_error_t *
792 gbp_contract_init (vlib_main_t * vm)
793 {
794   gc_logger = vlib_log_register_class ("gbp", "con");
795   gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft);
796
797   return (NULL);
798 }
799
800 VLIB_INIT_FUNCTION (gbp_contract_init);
801
802 /*
803  * fd.io coding-style-patch-verification: ON
804  *
805  * Local Variables:
806  * eval: (c-set-style "gnu")
807  * End:
808  */