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