GBP: Counters per-contract
[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 (sclass_t sclass,
448                      sclass_t dclass,
449                      u32 acl_index,
450                      index_t * rules,
451                      u16 * allowed_ethertypes, u32 * stats_index)
452 {
453   gbp_main_t *gm = &gbp_main;
454   u32 *acl_vec = NULL;
455   gbp_contract_t *gc;
456   index_t gci;
457   uword *p;
458
459   gbp_contract_key_t key = {
460     .gck_src = sclass,
461     .gck_dst = dclass,
462   };
463
464   if (~0 == gm->gbp_acl_user_id)
465     {
466       acl_plugin_exports_init (&gm->acl_plugin);
467       gm->gbp_acl_user_id =
468         gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg");
469     }
470
471   p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
472   if (p != NULL)
473     {
474       gci = p[0];
475       gc = gbp_contract_get (gci);
476       gbp_contract_rules_free (gc->gc_rules);
477       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
478       gc->gc_rules = NULL;
479       vec_free (gc->gc_allowed_ethertypes);
480     }
481   else
482     {
483       pool_get_zero (gbp_contract_pool, gc);
484       gc->gc_key = key;
485       gci = gc - gbp_contract_pool;
486       hash_set (gbp_contract_db.gc_hash, key.as_u32, gci);
487
488       vlib_validate_combined_counter (&gbp_contract_drop_counters, gci);
489       vlib_zero_combined_counter (&gbp_contract_drop_counters, gci);
490       vlib_validate_combined_counter (&gbp_contract_permit_counters, gci);
491       vlib_zero_combined_counter (&gbp_contract_permit_counters, gci);
492     }
493
494   GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci);
495
496   gc->gc_rules = rules;
497   gc->gc_allowed_ethertypes = allowed_ethertypes;
498   gbp_contract_resolve (gc->gc_rules);
499   gbp_contract_mk_lbs (gc->gc_rules);
500
501   gc->gc_acl_index = acl_index;
502   gc->gc_lc_index =
503     gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id,
504                                              sclass, dclass);
505
506   vec_add1 (acl_vec, gc->gc_acl_index);
507   gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec);
508   vec_free (acl_vec);
509
510   *stats_index = gci;
511
512   return (0);
513 }
514
515 int
516 gbp_contract_delete (sclass_t sclass, sclass_t dclass)
517 {
518   gbp_contract_key_t key = {
519     .gck_src = sclass,
520     .gck_dst = dclass,
521   };
522   gbp_contract_t *gc;
523   uword *p;
524
525   p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
526   if (p != NULL)
527     {
528       gc = gbp_contract_get (p[0]);
529
530       gbp_contract_rules_free (gc->gc_rules);
531       gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
532       vec_free (gc->gc_allowed_ethertypes);
533
534       hash_unset (gbp_contract_db.gc_hash, key.as_u32);
535       pool_put (gbp_contract_pool, gc);
536
537       return (0);
538     }
539
540   return (VNET_API_ERROR_NO_SUCH_ENTRY);
541 }
542
543 void
544 gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
545 {
546   gbp_contract_t *gc;
547
548   /* *INDENT-OFF* */
549   pool_foreach(gc, gbp_contract_pool,
550   ({
551     if (!cb(gc, ctx))
552       break;
553   }));
554   /* *INDENT-ON* */
555 }
556
557 static clib_error_t *
558 gbp_contract_cli (vlib_main_t * vm,
559                   unformat_input_t * input, vlib_cli_command_t * cmd)
560 {
561   sclass_t sclass = SCLASS_INVALID, dclass = SCLASS_INVALID;
562   u32 acl_index = ~0, stats_index;
563   u8 add = 1;
564
565   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
566     {
567       if (unformat (input, "add"))
568         add = 1;
569       else if (unformat (input, "del"))
570         add = 0;
571       else if (unformat (input, "src-epg %d", &sclass))
572         ;
573       else if (unformat (input, "dst-epg %d", &dclass))
574         ;
575       else if (unformat (input, "acl-index %d", &acl_index))
576         ;
577       else
578         break;
579     }
580
581   if (SCLASS_INVALID == sclass)
582     return clib_error_return (0, "Source EPG-ID must be specified");
583   if (SCLASS_INVALID == dclass)
584     return clib_error_return (0, "Destination EPG-ID must be specified");
585
586   if (add)
587     {
588       gbp_contract_update (sclass, dclass, acl_index,
589                            NULL, NULL, &stats_index);
590     }
591   else
592     {
593       gbp_contract_delete (sclass, dclass);
594     }
595
596   return (NULL);
597 }
598
599 /*?
600  * Configure a GBP Contract
601  *
602  * @cliexpar
603  * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
604  * @cliexend
605  ?*/
606 /* *INDENT-OFF* */
607 VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
608 {
609   .path = "gbp contract",
610   .short_help =
611     "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
612   .function = gbp_contract_cli,
613 };
614 /* *INDENT-ON* */
615
616 static u8 *
617 format_gbp_contract_key (u8 * s, va_list * args)
618 {
619   gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *);
620
621   s = format (s, "{%d,%d}", gck->gck_src, gck->gck_dst);
622
623   return (s);
624 }
625
626 u8 *
627 format_gbp_contract (u8 * s, va_list * args)
628 {
629   index_t gci = va_arg (*args, index_t);
630   vlib_counter_t counts;
631   gbp_contract_t *gc;
632   index_t *gui;
633   u16 *et;
634
635   gc = gbp_contract_get (gci);
636
637   s = format (s, "[%d] %U: acl-index:%d",
638               gci, format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index);
639
640   vec_foreach (gui, gc->gc_rules)
641   {
642     s = format (s, "\n    %d: %U", *gui, format_gbp_rule, *gui);
643   }
644
645   s = format (s, "\n    allowed-ethertypes:[");
646   vec_foreach (et, gc->gc_allowed_ethertypes)
647   {
648     int host_et = clib_net_to_host_u16 (*et);
649     if (0 != host_et)
650       s = format (s, "0x%x, ", host_et);
651   }
652
653   vlib_get_combined_counter (&gbp_contract_drop_counters, gci, &counts);
654   s = format (s, "\n   drop:[%Ld:%Ld]", counts.packets, counts.bytes);
655   vlib_get_combined_counter (&gbp_contract_permit_counters, gci, &counts);
656   s = format (s, "\n   permit:[%Ld:%Ld]", counts.packets, counts.bytes);
657
658   s = format (s, "]");
659
660   return (s);
661 }
662
663 static clib_error_t *
664 gbp_contract_show (vlib_main_t * vm,
665                    unformat_input_t * input, vlib_cli_command_t * cmd)
666 {
667   gbp_contract_t *gc;
668   u32 src, dst;
669   index_t gci;
670
671   src = dst = SCLASS_INVALID;
672
673   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
674     {
675       if (unformat (input, "src %d", &src))
676         ;
677       else if (unformat (input, "dst %d", &dst))
678         ;
679       else
680         break;
681     }
682
683   vlib_cli_output (vm, "Contracts:");
684
685   /* *INDENT-OFF* */
686   pool_foreach (gc, gbp_contract_pool,
687   ({
688     gci = gc - gbp_contract_pool;
689
690     if (SCLASS_INVALID != src && SCLASS_INVALID != dst)
691       {
692         if (gc->gc_key.gck_src == src &&
693             gc->gc_key.gck_dst == dst)
694           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
695       }
696     else if (SCLASS_INVALID != src)
697       {
698         if (gc->gc_key.gck_src == src)
699           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
700       }
701     else if (SCLASS_INVALID != dst)
702       {
703         if (gc->gc_key.gck_dst == dst)
704           vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
705       }
706     else
707       vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
708   }));
709   /* *INDENT-ON* */
710
711   return (NULL);
712 }
713
714 /*?
715  * Show Group Based Policy Contracts
716  *
717  * @cliexpar
718  * @cliexstart{show gbp contract}
719  * @cliexend
720  ?*/
721 /* *INDENT-OFF* */
722 VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
723   .path = "show gbp contract",
724   .short_help = "show gbp contract [src <SRC>] [dst <DST>]\n",
725   .function = gbp_contract_show,
726 };
727 /* *INDENT-ON* */
728
729 static fib_node_t *
730 gbp_next_hop_get_node (fib_node_index_t index)
731 {
732   gbp_next_hop_t *gnh;
733
734   gnh = gbp_next_hop_get (index);
735
736   return (&gnh->gnh_node);
737 }
738
739 static void
740 gbp_next_hop_last_lock_gone (fib_node_t * node)
741 {
742   ASSERT (0);
743 }
744
745 static gbp_next_hop_t *
746 gbp_next_hop_from_fib_node (fib_node_t * node)
747 {
748   ASSERT (gbp_next_hop_fib_type == node->fn_type);
749   return ((gbp_next_hop_t *) node);
750 }
751
752 static fib_node_back_walk_rc_t
753 gbp_next_hop_back_walk_notify (fib_node_t * node,
754                                fib_node_back_walk_ctx_t * ctx)
755 {
756   gbp_next_hop_t *gnh;
757
758   gnh = gbp_next_hop_from_fib_node (node);
759
760   gbp_contract_mk_one_lb (gnh->gnh_gu);
761
762   return (FIB_NODE_BACK_WALK_CONTINUE);
763 }
764
765 /*
766  * The FIB path's graph node virtual function table
767  */
768 static const fib_node_vft_t gbp_next_hop_vft = {
769   .fnv_get = gbp_next_hop_get_node,
770   .fnv_last_lock = gbp_next_hop_last_lock_gone,
771   .fnv_back_walk = gbp_next_hop_back_walk_notify,
772   // .fnv_mem_show = fib_path_memory_show,
773 };
774
775 static clib_error_t *
776 gbp_contract_init (vlib_main_t * vm)
777 {
778   gc_logger = vlib_log_register_class ("gbp", "con");
779   gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft);
780
781   return (NULL);
782 }
783
784 VLIB_INIT_FUNCTION (gbp_contract_init);
785
786 /*
787  * fd.io coding-style-patch-verification: ON
788  *
789  * Local Variables:
790  * eval: (c-set-style "gnu")
791  * End:
792  */