bond: ping fails between l2 BD [VPP-1238]
[vpp.git] / src / plugins / gbp / gbp.c
1 /*
2  * gbp.h : Group Based Policy
3  *
4  * Copyright (c) 2013 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
20 /**
21  * IP4 destintion address to destination EPG mapping table
22  */
23 typedef struct gbp_ip4_to_epg_db_t_
24 {
25   /**
26    * use a simple hash table
27    */
28   uword *g4ie_hash;
29 } gbp_ip4_to_epg_db_t;
30
31 static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db;
32
33 /**
34  * IP6 destintion address to destination EPG mapping table
35  */
36 typedef struct gbp_ip6_to_epg_db_t_
37 {
38   /**
39    * use a memroy hash table
40    */
41   uword *g6ie_hash;
42 } gbp_ip6_to_epg_db_t;
43
44 static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
45
46 /**
47  * Result of a interface to EPG mapping.
48  * multiple Endpoints can occur on the same interface, so this
49  * mapping needs to be reference counted.
50  */
51 typedef struct gbp_itf_t_
52 {
53   epg_id_t gi_epg;
54   u32 gi_ref_count;
55 } gbp_itf_t;
56
57 const static gbp_itf_t ITF_INVALID = {
58   .gi_epg = EPG_INVALID,
59   .gi_ref_count = 0,
60 };
61
62 /**
63  * Interface to source EPG DB - a per-interface vector
64  */
65 typedef struct gbp_itf_to_epg_db_t_
66 {
67   gbp_itf_t *gte_vec;
68 } gbp_itf_to_epg_db_t;
69
70 static gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
71
72 /**
73  * Pool of GBP endpoints
74  */
75 static gbp_endpoint_t *gbp_endpoint_pool;
76
77 /**
78  * DB of endpoints
79  */
80 static uword *gbp_endpoint_db;
81
82 /**
83  * EPG src,dst pair to ACL mapping table, aka contract DB
84  */
85 typedef struct gbp_contract_db_t_
86 {
87   /**
88    * We can form a u64 key from the pair, so use a simple hash table
89    */
90   uword *gc_hash;
91 } gbp_contract_db_t;
92
93 /**
94  * Since contract DB instance
95  */
96 static gbp_contract_db_t gbp_contract_db;
97
98 static void
99 gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
100 {
101   /*
102    * we are dealing only with addresses here so this limited
103    * is_ip4 check is ok
104    */
105   if (ip46_address_is_ip4 (ip))
106     {
107       hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id);
108     }
109   else
110     {
111       hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id);
112     }
113 }
114
115 static void
116 gbp_ip_epg_delete (const ip46_address_t * ip)
117 {
118   if (ip46_address_is_ip4 (ip))
119     {
120       hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32);
121     }
122   else
123     {
124       hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6);
125     }
126 }
127
128 static void
129 gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg)
130 {
131   vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec,
132                            sw_if_index, ITF_INVALID);
133
134   if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
135     {
136       vnet_feature_enable_disable ("ip4-unicast", "gbp4",
137                                    sw_if_index, 1, NULL, 0);
138       vnet_feature_enable_disable ("ip6-unicast", "gbp6",
139                                    sw_if_index, 1, NULL, 0);
140     }
141   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg;
142   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++;
143 }
144
145 static void
146 gbp_itf_epg_delete (u32 sw_if_index)
147 {
148   if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index)
149     return;
150
151   if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
152     {
153       gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID;
154
155       vnet_feature_enable_disable ("ip4-unicast", "gbp4",
156                                    sw_if_index, 0, NULL, 0);
157       vnet_feature_enable_disable ("ip6-unicast", "gbp6",
158                                    sw_if_index, 0, NULL, 0);
159     }
160   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--;
161 }
162
163 void
164 gbp_endpoint_update (u32 sw_if_index,
165                      const ip46_address_t * ip, epg_id_t epg_id)
166 {
167   gbp_endpoint_key_t key = {
168     .gek_ip = *ip,
169     .gek_sw_if_index = sw_if_index,
170   };
171   gbp_endpoint_t *gbpe;
172   uword *p;
173
174   p = hash_get_mem (gbp_endpoint_db, &key);
175
176   if (p)
177     {
178       gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
179     }
180   else
181     {
182       pool_get (gbp_endpoint_pool, gbpe);
183
184       gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t));
185       clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t));
186
187       hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
188     }
189
190   gbpe->ge_epg_id = epg_id;
191
192   gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id);
193   gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id);
194 }
195
196 void
197 gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
198 {
199   gbp_endpoint_key_t key = {
200     .gek_ip = *ip,
201     .gek_sw_if_index = sw_if_index,
202   };
203   gbp_endpoint_t *gbpe;
204   uword *p;
205
206   p = hash_get_mem (gbp_endpoint_db, &key);
207
208   if (p)
209     {
210       gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
211
212       hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
213
214       gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
215       gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
216
217       clib_mem_free (gbpe->ge_key);
218
219       pool_put (gbp_endpoint_pool, gbpe);
220     }
221 }
222
223 void
224 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
225 {
226   gbp_endpoint_t *gbpe;
227
228   /* *INDENT-OFF* */
229   pool_foreach(gbpe, gbp_endpoint_pool,
230   {
231     if (!cb(gbpe, ctx))
232       break;
233   });
234   /* *INDENT-ON* */
235 }
236
237 void
238 gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index)
239 {
240   gbp_contract_key_t key = {
241     .gck_src = src_epg,
242     .gck_dst = dst_epg,
243   };
244
245   hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index);
246 }
247
248 void
249 gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg)
250 {
251   gbp_contract_key_t key = {
252     .gck_src = src_epg,
253     .gck_dst = dst_epg,
254   };
255
256   hash_unset (gbp_contract_db.gc_hash, key.as_u64);
257 }
258
259 void
260 gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
261 {
262   gbp_contract_key_t key;
263   u32 acl_index;
264
265   /* *INDENT-OFF* */
266   hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash,
267   ({
268     gbp_contract_t gbpc = {
269       .gc_key = key,
270       .gc_acl_index = acl_index,
271     };
272
273     if (!cb(&gbpc, ctx))
274       break;
275   }));
276   /* *INDENT-ON* */
277 }
278
279 static clib_error_t *
280 gbp_endpoint_cli (vlib_main_t * vm,
281                   unformat_input_t * input, vlib_cli_command_t * cmd)
282 {
283   vnet_main_t *vnm = vnet_get_main ();
284   epg_id_t epg_id = EPG_INVALID;
285   ip46_address_t ip = { };
286   u32 sw_if_index = ~0;
287   u8 add = 1;
288
289   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
290     {
291       if (unformat (input, "%U", unformat_vnet_sw_interface,
292                     vnm, &sw_if_index))
293         ;
294       else if (unformat (input, "add"))
295         add = 1;
296       else if (unformat (input, "del"))
297         add = 0;
298       else if (unformat (input, "epg %d", &epg_id))
299         ;
300       else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
301         ;
302       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
303         ;
304       else
305         break;
306     }
307
308   if (~0 == sw_if_index)
309     return clib_error_return (0, "interface must be specified");
310   if (EPG_INVALID == epg_id)
311     return clib_error_return (0, "EPG-ID must be specified");
312   if (ip46_address_is_zero (&ip))
313     return clib_error_return (0, "IP address must be specified");
314
315   if (add)
316     gbp_endpoint_update (sw_if_index, &ip, epg_id);
317   else
318     gbp_endpoint_delete (sw_if_index, &ip);
319
320   return (NULL);
321 }
322
323 static clib_error_t *
324 gbp_contract_cli (vlib_main_t * vm,
325                   unformat_input_t * input, vlib_cli_command_t * cmd)
326 {
327   epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID;
328   u32 acl_index = ~0;
329   u8 add = 1;
330
331   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
332     {
333       if (unformat (input, "add"))
334         add = 1;
335       else if (unformat (input, "del"))
336         add = 0;
337       else if (unformat (input, "src-epg %d", &src_epg_id))
338         ;
339       else if (unformat (input, "dst-epg %d", &dst_epg_id))
340         ;
341       else if (unformat (input, "acl-index %d", &acl_index))
342         ;
343       else
344         break;
345     }
346
347   if (EPG_INVALID == src_epg_id)
348     return clib_error_return (0, "Source EPG-ID must be specified");
349   if (EPG_INVALID == dst_epg_id)
350     return clib_error_return (0, "Destination EPG-ID must be specified");
351
352   if (add)
353     {
354       gbp_contract_update (src_epg_id, dst_epg_id, acl_index);
355     }
356   else
357     {
358       gbp_contract_delete (src_epg_id, dst_epg_id);
359     }
360
361   return (NULL);
362 }
363
364 /*?
365  * Configure a GBP Endpoint
366  *
367  * @cliexpar
368  * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
369  * @cliexend
370  ?*/
371 /* *INDENT-OFF* */
372 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
373   .path = "gbp endpoint",
374   .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
375   .function = gbp_endpoint_cli,
376 };
377
378 /*?
379  * Configure a GBP Contract
380  *
381  * @cliexpar
382  * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
383  * @cliexend
384  ?*/
385 VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = {
386   .path = "gbp contract",
387   .short_help = "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
388   .function = gbp_contract_cli,
389 };
390 /* *INDENT-ON* */
391
392 static int
393 gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
394 {
395   vnet_main_t *vnm = vnet_get_main ();
396   vlib_main_t *vm;
397
398   vm = ctx;
399   vlib_cli_output (vm, "  {%U, %U} -> %d",
400                    format_vnet_sw_if_index_name, vnm,
401                    gbpe->ge_key->gek_sw_if_index,
402                    format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY,
403                    gbpe->ge_epg_id);
404
405   return (1);
406 }
407
408 static clib_error_t *
409 gbp_endpoint_show (vlib_main_t * vm,
410                    unformat_input_t * input, vlib_cli_command_t * cmd)
411 {
412   vnet_main_t *vnm = vnet_get_main ();
413   ip46_address_t ip, *ipp;
414   epg_id_t epg_id;
415   u32 sw_if_index;
416
417   vlib_cli_output (vm, "Endpoints:");
418   gbp_endpoint_walk (gbp_endpoint_show_one, vm);
419
420   vlib_cli_output (vm, "\nSource interface to EPG:");
421
422   vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec)
423   {
424     if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg)
425       {
426         vlib_cli_output (vm, "  %U -> %d",
427                          format_vnet_sw_if_index_name, vnm, sw_if_index,
428                          gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
429       }
430   }
431
432   vlib_cli_output (vm, "\nDestination IP4 to EPG:");
433
434   /* *INDENT-OFF* */
435   hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash,
436   {
437     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, &ip,
438                      IP46_TYPE_IP4, epg_id);
439   });
440   /* *INDENT-ON* */
441
442   vlib_cli_output (vm, "\nDestination IP6 to EPG:");
443
444   /* *INDENT-OFF* */
445   hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash,
446   {
447     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, ipp,
448                      IP46_TYPE_IP6, epg_id);
449   });
450   /* *INDENT-ON* */
451
452   return (NULL);
453 }
454
455 static clib_error_t *
456 gbp_contract_show (vlib_main_t * vm,
457                    unformat_input_t * input, vlib_cli_command_t * cmd)
458 {
459   gbp_contract_key_t key;
460   epg_id_t epg_id;
461
462   vlib_cli_output (vm, "Contracts:");
463
464   /* *INDENT-OFF* */
465   hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash,
466   {
467     vlib_cli_output (vm, "  {%d,%d} -> %d", key.gck_src,
468                      key.gck_dst, epg_id);
469   });
470   /* *INDENT-ON* */
471
472   return (NULL);
473 }
474
475 /*?
476  * Show Group Based Policy Endpoints and derived information
477  *
478  * @cliexpar
479  * @cliexstart{show gbp endpoint}
480  * @cliexend
481  ?*/
482 /* *INDENT-OFF* */
483 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
484   .path = "show gbp endpoint",
485   .short_help = "show gbp endpoint\n",
486   .function = gbp_endpoint_show,
487 };
488 /* *INDENT-ON* */
489
490 /*?
491  * Show Group Based Policy Contracts
492  *
493  * @cliexpar
494  * @cliexstart{show gbp contract}
495  * @cliexend
496  ?*/
497 /* *INDENT-OFF* */
498 VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
499   .path = "show gbp contract",
500   .short_help = "show gbp contract\n",
501   .function = gbp_contract_show,
502 };
503 /* *INDENT-ON* */
504
505 #define foreach_gbp                    \
506   _(DENY,    "deny")
507
508 typedef enum
509 {
510 #define _(sym,str) GBP_ERROR_##sym,
511   foreach_gbp
512 #undef _
513     GBP_N_ERROR,
514 } gbp_error_t;
515
516 static char *gbp_error_strings[] = {
517 #define _(sym,string) string,
518   foreach_gbp
519 #undef _
520 };
521
522 typedef enum
523 {
524 #define _(sym,str) GBP_NEXT_##sym,
525   foreach_gbp
526 #undef _
527     GBP_N_NEXT,
528 } gbp_next_t;
529
530 /**
531  * per-packet trace data
532  */
533 typedef struct gbp_trace_t_
534 {
535   /* per-pkt trace data */
536   epg_id_t src_epg;
537   epg_id_t dst_epg;
538   u32 acl_index;
539 } gbp_trace_t;
540
541 static inline uword
542 gbp_inline (vlib_main_t * vm,
543             vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6)
544 {
545   u32 n_left_from, *from, *to_next;
546   gbp_next_t next_index;
547
548   next_index = 0;
549   n_left_from = frame->n_vectors;
550   from = vlib_frame_vector_args (frame);
551
552   while (n_left_from > 0)
553     {
554       u32 n_left_to_next;
555
556       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
557
558       while (n_left_from > 0 && n_left_to_next > 0)
559         {
560           vlib_buffer_t *b0;
561           u32 sw_if_index0;
562           gbp_next_t next0;
563           u32 bi0;
564           ip4_header_t *ip4_0;
565           ip6_header_t *ip6_0;
566           gbp_contract_key_t key0;
567           u32 acl_index0;
568           uword *p;
569
570           bi0 = from[0];
571           to_next[0] = bi0;
572           from += 1;
573           to_next += 1;
574           n_left_from -= 1;
575           n_left_to_next -= 1;
576
577           /* deny by default */
578           next0 = GBP_NEXT_DENY;
579
580           b0 = vlib_get_buffer (vm, bi0);
581           if (is_ip6)
582             ip6_0 = vlib_buffer_get_current (b0);
583           else
584             ip4_0 = vlib_buffer_get_current (b0);
585           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
586
587           /*
588            * determine the src and dst EPG
589            */
590           key0.gck_src = gbp_itf_to_epg_db.gte_vec[sw_if_index0].gi_epg;
591
592           if (is_ip6)
593             p = hash_get_mem (gbp_ip6_to_epg_db.g6ie_hash,
594                               &ip6_0->dst_address);
595           else
596             p = hash_get (gbp_ip4_to_epg_db.g4ie_hash,
597                           ip4_0->dst_address.as_u32);
598
599           if (NULL != p)
600             {
601               key0.gck_dst = p[0];
602
603               /*
604                * If the src and dst are the same, then let it through
605                */
606               if (key0.gck_dst == key0.gck_src)
607                 {
608                   vnet_feature_next (sw_if_index0, &next0, b0);
609                   acl_index0 = ~0;
610                 }
611               else
612                 {
613                   /*
614                    * find this src,dst pair in the egp->acl DB
615                    */
616                   p = hash_get (gbp_contract_db.gc_hash, key0.as_u64);
617
618                   if (NULL != p)
619                     {
620                       acl_index0 = p[0];
621
622                       /*
623                        * the ACL index stored is NULL, this means any-any so let it pass
624                        */
625                       if (~0 == acl_index0)
626                         {
627                           vnet_feature_next (sw_if_index0, &next0, b0);
628                         }
629                       else
630                         {
631                           /*
632                            * TODO tests against the ACL
633                            */
634                           /*
635                            * ACL tables are not available outside of ACL plugin
636                            * until then bypass the ACL to next node
637                            */
638                           vnet_feature_next (sw_if_index0, &next0, b0);
639                         }
640                     }
641                   else
642                     {
643                       /*
644                        * no ACL to apply for packets between these two EPGs.
645                        * GBP is a whitelist model, so no ACL implies deny, which
646                        * is the default result
647                        */
648                       acl_index0 = ~0;
649                     }
650                 }
651             }
652           else
653             {
654               /*
655                * cannot determine the destinaiotn EPG, so we cannot enforce policy
656                * on this node. permit.
657                */
658               vnet_feature_next (sw_if_index0, &next0, b0);
659
660               key0.gck_dst = ~0;
661               acl_index0 = ~0;
662             }
663
664           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
665                              (b0->flags & VLIB_BUFFER_IS_TRACED)))
666             {
667               gbp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
668               t->src_epg = key0.gck_src;
669               t->dst_epg = key0.gck_dst;
670               t->acl_index = acl_index0;
671             }
672
673           /* verify speculative enqueue, maybe switch current next frame */
674           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
675                                            to_next, n_left_to_next,
676                                            bi0, next0);
677         }
678
679       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
680     }
681
682   return frame->n_vectors;
683 }
684
685 /* packet trace format function */
686 static u8 *
687 format_gbp_trace (u8 * s, va_list * args)
688 {
689   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
690   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
691   gbp_trace_t *t = va_arg (*args, gbp_trace_t *);
692
693   s = format (s, "gbp: src:%d dst:%d acl:%d",
694               t->src_epg, t->dst_epg, t->acl_index);
695
696   return s;
697 }
698
699 static inline uword
700 gbp_4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
701 {
702   return (gbp_inline (vm, node, frame, 0));
703 }
704
705 static inline uword
706 gbp_6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
707 {
708   return (gbp_inline (vm, node, frame, 1));
709 }
710
711 /* *INDENT-OFF* */
712 VLIB_REGISTER_NODE (gbp_4_node) = {
713   .function = gbp_4,
714   .name = "gbp4",
715   .vector_size = sizeof (u32),
716   .format_trace = format_gbp_trace,
717   .type = VLIB_NODE_TYPE_INTERNAL,
718
719   .n_errors = ARRAY_LEN(gbp_error_strings),
720   .error_strings = gbp_error_strings,
721
722   .n_next_nodes = GBP_N_NEXT,
723
724   .next_nodes = {
725     [GBP_NEXT_DENY] = "ip4-drop",
726   },
727 };
728
729 VLIB_NODE_FUNCTION_MULTIARCH (gbp_4_node, gbp_4);
730
731 VNET_FEATURE_INIT (gbp_4_node, static) = {
732     .arc_name = "ip4-unicast",
733     .node_name = "gbp4",
734     .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
735 };
736
737 VLIB_REGISTER_NODE (gbp_6_node) = {
738   .function = gbp_6,
739   .name = "gbp6",
740   .vector_size = sizeof (u32),
741   .format_trace = format_gbp_trace,
742   .type = VLIB_NODE_TYPE_INTERNAL,
743
744   .n_errors = ARRAY_LEN(gbp_error_strings),
745   .error_strings = gbp_error_strings,
746
747   .n_next_nodes = GBP_N_NEXT,
748
749   .next_nodes = {
750     [GBP_NEXT_DENY] = "ip6-drop",
751   },
752 };
753
754 VLIB_NODE_FUNCTION_MULTIARCH (gbp_6_node, gbp_6);
755
756 VNET_FEATURE_INIT (gbp_6_node, static) = {
757     .arc_name = "ip6-unicast",
758     .node_name = "gbp6",
759     .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
760 };
761 /* *INDENT-ON* */
762
763 static clib_error_t *
764 gbp_init (vlib_main_t * vm)
765 {
766   gbp_endpoint_db = hash_create_mem (0,
767                                      sizeof (gbp_endpoint_key_t),
768                                      sizeof (u32));
769   gbp_ip6_to_epg_db.g6ie_hash =
770     hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32));
771   return 0;
772 }
773
774 VLIB_INIT_FUNCTION (gbp_init);
775
776 /*
777  * fd.io coding-style-patch-verification: ON
778  *
779  * Local Variables:
780  * eval: (c-set-style "gnu")
781  * End:
782  */