misc: deprecate gbp and its dependents
[vpp.git] / extras / deprecated / plugins / gbp / gbp_learn_node.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <plugins/gbp/gbp.h>
17 #include <plugins/gbp/gbp_learn.h>
18 #include <plugins/gbp/gbp_bridge_domain.h>
19 #include <vlibmemory/api.h>
20
21 #include <vnet/util/throttle.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/fib/fib_table.h>
24 #include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
25 #include <vnet/ethernet/arp_packet.h>
26
27 #define GBP_LEARN_DBG(...)                                      \
28     vlib_log_debug (gbp_learn_main.gl_logger, __VA_ARGS__);
29
30 #define foreach_gbp_learn                      \
31   _(DROP,    "drop")
32
33 typedef enum
34 {
35 #define _(sym,str) GBP_LEARN_ERROR_##sym,
36   foreach_gbp_learn
37 #undef _
38     GBP_LEARN_N_ERROR,
39 } gbp_learn_error_t;
40
41 static char *gbp_learn_error_strings[] = {
42 #define _(sym,string) string,
43   foreach_gbp_learn
44 #undef _
45 };
46
47 typedef enum
48 {
49 #define _(sym,str) GBP_LEARN_NEXT_##sym,
50   foreach_gbp_learn
51 #undef _
52     GBP_LEARN_N_NEXT,
53 } gbp_learn_next_t;
54
55 typedef struct gbp_learn_l2_t_
56 {
57   ip46_address_t ip;
58   mac_address_t mac;
59   u32 sw_if_index;
60   u32 bd_index;
61   sclass_t sclass;
62   ip46_address_t outer_src;
63   ip46_address_t outer_dst;
64 } gbp_learn_l2_t;
65
66
67 static void
68 gbp_learn_l2_cp (const gbp_learn_l2_t * gl2)
69 {
70   ip46_address_t *ips = NULL;
71
72   GBP_LEARN_DBG ("L2 EP: %U %U, %d",
73                  format_mac_address_t, &gl2->mac,
74                  format_ip46_address, &gl2->ip, IP46_TYPE_ANY, gl2->sclass);
75
76   if (!ip46_address_is_zero (&gl2->ip))
77     vec_add1 (ips, gl2->ip);
78
79   /*
80    * flip the source and dst, since that's how it was received, this API
81    * takes how it's sent
82    */
83   gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP,
84                                 gl2->sw_if_index, ips,
85                                 &gl2->mac, INDEX_INVALID,
86                                 INDEX_INVALID, gl2->sclass,
87                                 (GBP_ENDPOINT_FLAG_LEARNT |
88                                  GBP_ENDPOINT_FLAG_REMOTE),
89                                 &gl2->outer_dst, &gl2->outer_src, NULL);
90   vec_free (ips);
91 }
92
93 static void
94 gbp_learn_l2_ip4_dp (const u8 * mac, const ip4_address_t * ip,
95                      u32 bd_index, u32 sw_if_index, sclass_t sclass,
96                      const ip4_address_t * outer_src,
97                      const ip4_address_t * outer_dst)
98 {
99   gbp_learn_l2_t gl2 = {
100     .sw_if_index = sw_if_index,
101     .bd_index = bd_index,
102     .sclass = sclass,
103     .ip.ip4 = *ip,
104     .outer_src.ip4 = *outer_src,
105     .outer_dst.ip4 = *outer_dst,
106   };
107   mac_address_from_bytes (&gl2.mac, mac);
108
109   vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
110 }
111
112 static void
113 gbp_learn_l2_ip6_dp (const u8 * mac, const ip6_address_t * ip,
114                      u32 bd_index, u32 sw_if_index, sclass_t sclass,
115                      const ip4_address_t * outer_src,
116                      const ip4_address_t * outer_dst)
117 {
118   gbp_learn_l2_t gl2 = {
119     .sw_if_index = sw_if_index,
120     .bd_index = bd_index,
121     .sclass = sclass,
122     .ip.ip6 = *ip,
123     .outer_src.ip4 = *outer_src,
124     .outer_dst.ip4 = *outer_dst,
125   };
126   mac_address_from_bytes (&gl2.mac, mac);
127
128   vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
129 }
130
131 static void
132 gbp_learn_l2_dp (const u8 * mac, u32 bd_index, u32 sw_if_index,
133                  sclass_t sclass,
134                  const ip4_address_t * outer_src,
135                  const ip4_address_t * outer_dst)
136 {
137   gbp_learn_l2_t gl2 = {
138     .sw_if_index = sw_if_index,
139     .bd_index = bd_index,
140     .sclass = sclass,
141     .outer_src.ip4 = *outer_src,
142     .outer_dst.ip4 = *outer_dst,
143   };
144   mac_address_from_bytes (&gl2.mac, mac);
145
146   vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
147 }
148
149 /**
150  * per-packet trace data
151  */
152 typedef struct gbp_learn_l2_trace_t_
153 {
154   /* per-pkt trace data */
155   mac_address_t mac;
156   u32 sw_if_index;
157   u32 new;
158   u32 throttled;
159   u32 sclass;
160   u32 d_bit;
161   gbp_bridge_domain_flags_t gb_flags;
162 } gbp_learn_l2_trace_t;
163
164 always_inline void
165 gbp_learn_get_outer (const ethernet_header_t * eh0,
166                      ip4_address_t * outer_src, ip4_address_t * outer_dst)
167 {
168   ip4_header_t *ip0;
169   u8 *buff;
170
171   /* rewind back to the ivxlan header */
172   buff = (u8 *) eh0;
173   buff -= (sizeof (vxlan_gbp_header_t) +
174            sizeof (udp_header_t) + sizeof (ip4_header_t));
175
176   ip0 = (ip4_header_t *) buff;
177
178   *outer_src = ip0->src_address;
179   *outer_dst = ip0->dst_address;
180 }
181
182 always_inline int
183 gbp_endpoint_update_required (const gbp_endpoint_t * ge0,
184                               u32 rx_sw_if_index, sclass_t sclass)
185 {
186   /* Conditions for [re]learning this EP */
187
188   /* 1. it doesn't have a dataplane source */
189   if (!gbp_endpoint_is_learnt (ge0))
190     return (!0);
191
192   /* 2. has the input interface changed */
193   if (gbp_itf_get_sw_if_index (ge0->ge_fwd.gef_itf) != rx_sw_if_index)
194     return (!0);
195
196   /* 3. has the sclass changed */
197   if (sclass != ge0->ge_fwd.gef_sclass)
198     return (!0);
199
200   /* otherwise it's unchanged */
201   return (0);
202 }
203
204 VLIB_NODE_FN (gbp_learn_l2_node) (vlib_main_t * vm,
205                                   vlib_node_runtime_t * node,
206                                   vlib_frame_t * frame)
207 {
208   u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
209   gbp_learn_main_t *glm;
210   f64 time_now;
211
212   glm = &gbp_learn_main;
213   next_index = 0;
214   n_left_from = frame->n_vectors;
215   from = vlib_frame_vector_args (frame);
216   time_now = vlib_time_now (vm);
217   thread_index = vm->thread_index;
218
219   seed = throttle_seed (&glm->gl_l2_throttle, thread_index, time_now);
220
221   while (n_left_from > 0)
222     {
223       u32 n_left_to_next;
224
225       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
226
227       while (n_left_from > 0 && n_left_to_next > 0)
228         {
229           ip4_address_t outer_src, outer_dst;
230           const ethernet_header_t *eh0;
231           u32 bi0, sw_if_index0, t0;
232           gbp_bridge_domain_t *gb0;
233           gbp_learn_next_t next0;
234           gbp_endpoint_t *ge0;
235           vlib_buffer_t *b0;
236           sclass_t sclass0;
237
238           next0 = GBP_LEARN_NEXT_DROP;
239           bi0 = from[0];
240           to_next[0] = bi0;
241           from += 1;
242           to_next += 1;
243           n_left_from -= 1;
244           n_left_to_next -= 1;
245
246           b0 = vlib_get_buffer (vm, bi0);
247           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
248
249           eh0 = vlib_buffer_get_current (b0);
250           sclass0 = vnet_buffer2 (b0)->gbp.sclass;
251
252           next0 = vnet_l2_feature_next (b0, glm->gl_l2_input_feat_next,
253                                         L2INPUT_FEAT_GBP_LEARN);
254
255           ge0 = gbp_endpoint_find_mac (eh0->src_address,
256                                        vnet_buffer (b0)->l2.bd_index);
257           gb0 =
258             gbp_bridge_domain_get_by_bd_index (vnet_buffer (b0)->l2.bd_index);
259
260           if ((vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D) ||
261               (gb0->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN))
262             {
263               t0 = 1;
264               goto trace;
265             }
266
267           /*
268            * check for new EP or a moved EP
269            */
270           if (NULL == ge0 ||
271               gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
272             {
273               /*
274                * use the last 4 bytes of the mac address as the hash for the EP
275                */
276               t0 = throttle_check (&glm->gl_l2_throttle, thread_index,
277                                    *((u32 *) (eh0->src_address + 2)), seed);
278               if (!t0)
279                 {
280                   gbp_learn_get_outer (eh0, &outer_src, &outer_dst);
281
282                   if (outer_src.as_u32 == 0 || outer_dst.as_u32 == 0)
283                     {
284                       t0 = 2;
285                       goto trace;
286                     }
287
288                   switch (clib_net_to_host_u16 (eh0->type))
289                     {
290                     case ETHERNET_TYPE_IP4:
291                       {
292                         const ip4_header_t *ip0;
293
294                         ip0 = (ip4_header_t *) (eh0 + 1);
295
296                         gbp_learn_l2_ip4_dp (eh0->src_address,
297                                              &ip0->src_address,
298                                              vnet_buffer (b0)->l2.bd_index,
299                                              sw_if_index0, sclass0,
300                                              &outer_src, &outer_dst);
301
302                         break;
303                       }
304                     case ETHERNET_TYPE_IP6:
305                       {
306                         const ip6_header_t *ip0;
307
308                         ip0 = (ip6_header_t *) (eh0 + 1);
309
310                         gbp_learn_l2_ip6_dp (eh0->src_address,
311                                              &ip0->src_address,
312                                              vnet_buffer (b0)->l2.bd_index,
313                                              sw_if_index0, sclass0,
314                                              &outer_src, &outer_dst);
315
316                         break;
317                       }
318                     case ETHERNET_TYPE_ARP:
319                       {
320                         const ethernet_arp_header_t *arp0;
321
322                         arp0 = (ethernet_arp_header_t *) (eh0 + 1);
323
324                         gbp_learn_l2_ip4_dp (eh0->src_address,
325                                              &arp0->ip4_over_ethernet[0].ip4,
326                                              vnet_buffer (b0)->l2.bd_index,
327                                              sw_if_index0, sclass0,
328                                              &outer_src, &outer_dst);
329                         break;
330                       }
331                     default:
332                       gbp_learn_l2_dp (eh0->src_address,
333                                        vnet_buffer (b0)->l2.bd_index,
334                                        sw_if_index0, sclass0,
335                                        &outer_src, &outer_dst);
336                       break;
337                     }
338                 }
339             }
340           else
341             {
342               /*
343                * this update could happen simultaneoulsy from multiple workers
344                * but that's ok we are not interested in being very accurate.
345                */
346               t0 = 0;
347               ge0->ge_last_time = time_now;
348             }
349         trace:
350           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
351             {
352               gbp_learn_l2_trace_t *t =
353                 vlib_add_trace (vm, node, b0, sizeof (*t));
354               clib_memcpy_fast (t->mac.bytes, eh0->src_address, 6);
355               t->new = (NULL == ge0);
356               t->throttled = t0;
357               t->sw_if_index = sw_if_index0;
358               t->sclass = sclass0;
359               t->gb_flags = gb0->gb_flags;
360               t->d_bit = ! !(vnet_buffer2 (b0)->gbp.flags &
361                              VXLAN_GBP_GPFLAGS_D);
362             }
363
364           /* verify speculative enqueue, maybe switch current next frame */
365           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
366                                            to_next, n_left_to_next,
367                                            bi0, next0);
368         }
369
370       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
371     }
372
373   return frame->n_vectors;
374 }
375
376 /* packet trace format function */
377 static u8 *
378 format_gbp_learn_l2_trace (u8 * s, va_list * args)
379 {
380   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
381   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
382   gbp_learn_l2_trace_t *t = va_arg (*args, gbp_learn_l2_trace_t *);
383
384   s = format (s, "new:%d throttled:%d d-bit:%d mac:%U itf:%d sclass:%d"
385               " gb-flags:%U",
386               t->new, t->throttled, t->d_bit,
387               format_mac_address_t, &t->mac, t->sw_if_index, t->sclass,
388               format_gbp_bridge_domain_flags, t->gb_flags);
389
390   return s;
391 }
392
393 /* *INDENT-OFF* */
394 VLIB_REGISTER_NODE (gbp_learn_l2_node) = {
395   .name = "gbp-learn-l2",
396   .vector_size = sizeof (u32),
397   .format_trace = format_gbp_learn_l2_trace,
398   .type = VLIB_NODE_TYPE_INTERNAL,
399
400   .n_errors = ARRAY_LEN(gbp_learn_error_strings),
401   .error_strings = gbp_learn_error_strings,
402
403   .n_next_nodes = GBP_LEARN_N_NEXT,
404
405   .next_nodes = {
406     [GBP_LEARN_NEXT_DROP] = "error-drop",
407   },
408 };
409 /* *INDENT-ON* */
410
411 typedef struct gbp_learn_l3_t_
412 {
413   ip46_address_t ip;
414   u32 fib_index;
415   u32 sw_if_index;
416   sclass_t sclass;
417   ip46_address_t outer_src;
418   ip46_address_t outer_dst;
419 } gbp_learn_l3_t;
420
421 static void
422 gbp_learn_l3_cp (const gbp_learn_l3_t * gl3)
423 {
424   ip46_address_t *ips = NULL;
425
426   GBP_LEARN_DBG ("L3 EP: %U, %d", format_ip46_address, &gl3->ip,
427                  IP46_TYPE_ANY, gl3->sclass);
428
429   vec_add1 (ips, gl3->ip);
430
431   gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP,
432                                 gl3->sw_if_index, ips, NULL,
433                                 INDEX_INVALID, INDEX_INVALID, gl3->sclass,
434                                 (GBP_ENDPOINT_FLAG_REMOTE |
435                                  GBP_ENDPOINT_FLAG_LEARNT),
436                                 &gl3->outer_dst, &gl3->outer_src, NULL);
437   vec_free (ips);
438 }
439
440 static void
441 gbp_learn_ip4_dp (const ip4_address_t * ip,
442                   u32 fib_index, u32 sw_if_index, sclass_t sclass,
443                   const ip4_address_t * outer_src,
444                   const ip4_address_t * outer_dst)
445 {
446   /* *INDENT-OFF* */
447   gbp_learn_l3_t gl3 = {
448     .ip = {
449       .ip4 = *ip,
450     },
451     .sw_if_index = sw_if_index,
452     .fib_index = fib_index,
453     .sclass = sclass,
454     .outer_src.ip4 = *outer_src,
455     .outer_dst.ip4 = *outer_dst,
456   };
457   /* *INDENT-ON* */
458
459   vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
460 }
461
462 static void
463 gbp_learn_ip6_dp (const ip6_address_t * ip,
464                   u32 fib_index, u32 sw_if_index, sclass_t sclass,
465                   const ip4_address_t * outer_src,
466                   const ip4_address_t * outer_dst)
467 {
468   /* *INDENT-OFF* */
469   gbp_learn_l3_t gl3 = {
470     .ip = {
471       .ip6 = *ip,
472     },
473     .sw_if_index = sw_if_index,
474     .fib_index = fib_index,
475     .sclass = sclass,
476     .outer_src.ip4 = *outer_src,
477     .outer_dst.ip4 = *outer_dst,
478   };
479   /* *INDENT-ON* */
480
481   vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
482 }
483
484 /**
485  * per-packet trace data
486  */
487 typedef struct gbp_learn_l3_trace_t_
488 {
489   /* per-pkt trace data */
490   ip46_address_t ip;
491   u32 sw_if_index;
492   u32 new;
493   u32 throttled;
494   u32 sclass;
495 } gbp_learn_l3_trace_t;
496
497 static uword
498 gbp_learn_l3 (vlib_main_t * vm,
499               vlib_node_runtime_t * node, vlib_frame_t * frame,
500               fib_protocol_t fproto)
501 {
502   u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
503   gbp_learn_main_t *glm;
504   f64 time_now;
505
506   glm = &gbp_learn_main;
507   next_index = 0;
508   n_left_from = frame->n_vectors;
509   from = vlib_frame_vector_args (frame);
510   time_now = vlib_time_now (vm);
511   thread_index = vm->thread_index;
512
513   seed = throttle_seed (&glm->gl_l3_throttle, thread_index, time_now);
514
515   while (n_left_from > 0)
516     {
517       u32 n_left_to_next;
518
519       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
520
521       while (n_left_from > 0 && n_left_to_next > 0)
522         {
523           CLIB_UNUSED (const ip4_header_t *) ip4_0;
524           CLIB_UNUSED (const ip6_header_t *) ip6_0;
525           u32 bi0, sw_if_index0, t0, fib_index0;
526           ip4_address_t outer_src, outer_dst;
527           ethernet_header_t *eth0;
528           gbp_learn_next_t next0;
529           gbp_endpoint_t *ge0;
530           vlib_buffer_t *b0;
531           sclass_t sclass0;
532
533           next0 = GBP_LEARN_NEXT_DROP;
534           bi0 = from[0];
535           to_next[0] = bi0;
536           from += 1;
537           to_next += 1;
538           n_left_from -= 1;
539           n_left_to_next -= 1;
540
541           b0 = vlib_get_buffer (vm, bi0);
542           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
543           sclass0 = vnet_buffer2 (b0)->gbp.sclass;
544           ip6_0 = NULL;
545           ip4_0 = NULL;
546
547           vnet_feature_next (&next0, b0);
548
549           if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D)
550             {
551               t0 = 1;
552               ge0 = NULL;
553               goto trace;
554             }
555
556           fib_index0 = fib_table_get_index_for_sw_if_index (fproto,
557                                                             sw_if_index0);
558
559           if (FIB_PROTOCOL_IP6 == fproto)
560             {
561               ip6_0 = vlib_buffer_get_current (b0);
562               eth0 = (ethernet_header_t *) (((u8 *) ip6_0) - sizeof (*eth0));
563
564               gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
565
566               ge0 = gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0);
567
568               if ((NULL == ge0) ||
569                   gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
570                 {
571                   t0 = throttle_check (&glm->gl_l3_throttle,
572                                        thread_index,
573                                        ip6_address_hash_to_u32
574                                        (&ip6_0->src_address), seed);
575
576                   if (!t0)
577                     {
578                       gbp_learn_ip6_dp (&ip6_0->src_address,
579                                         fib_index0, sw_if_index0, sclass0,
580                                         &outer_src, &outer_dst);
581                     }
582                 }
583               else
584                 {
585                   /*
586                    * this update could happen simultaneoulsy from multiple
587                    * workers but that's ok we are not interested in being
588                    * very accurate.
589                    */
590                   t0 = 0;
591                   ge0->ge_last_time = time_now;
592                 }
593             }
594           else
595             {
596               ip4_0 = vlib_buffer_get_current (b0);
597               eth0 = (ethernet_header_t *) (((u8 *) ip4_0) - sizeof (*eth0));
598
599               gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
600               ge0 = gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0);
601
602               if ((NULL == ge0) ||
603                   gbp_endpoint_update_required (ge0, sw_if_index0, sclass0))
604                 {
605                   t0 = throttle_check (&glm->gl_l3_throttle, thread_index,
606                                        ip4_0->src_address.as_u32, seed);
607
608                   if (!t0)
609                     {
610                       gbp_learn_ip4_dp (&ip4_0->src_address,
611                                         fib_index0, sw_if_index0, sclass0,
612                                         &outer_src, &outer_dst);
613                     }
614                 }
615               else
616                 {
617                   /*
618                    * this update could happen simultaneoulsy from multiple
619                    * workers but that's ok we are not interested in being
620                    * very accurate.
621                    */
622                   t0 = 0;
623                   ge0->ge_last_time = time_now;
624                 }
625             }
626         trace:
627           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
628             {
629               gbp_learn_l3_trace_t *t;
630
631               t = vlib_add_trace (vm, node, b0, sizeof (*t));
632               if (FIB_PROTOCOL_IP6 == fproto && ip6_0)
633                 ip46_address_set_ip6 (&t->ip, &ip6_0->src_address);
634               if (FIB_PROTOCOL_IP4 == fproto && ip4_0)
635                 ip46_address_set_ip4 (&t->ip, &ip4_0->src_address);
636               t->new = (NULL == ge0);
637               t->throttled = t0;
638               t->sw_if_index = sw_if_index0;
639               t->sclass = sclass0;
640             }
641
642           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
643                                            to_next, n_left_to_next,
644                                            bi0, next0);
645         }
646
647       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
648     }
649
650   return frame->n_vectors;
651 }
652
653 /* packet trace format function */
654 static u8 *
655 format_gbp_learn_l3_trace (u8 * s, va_list * args)
656 {
657   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
658   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
659   gbp_learn_l3_trace_t *t = va_arg (*args, gbp_learn_l3_trace_t *);
660
661   s = format (s, "new:%d throttled:%d ip:%U itf:%d sclass:%d",
662               t->new, t->throttled,
663               format_ip46_address, &t->ip, IP46_TYPE_ANY, t->sw_if_index,
664               t->sclass);
665
666   return s;
667 }
668
669 VLIB_NODE_FN (gbp_learn_ip4_node) (vlib_main_t * vm,
670                                    vlib_node_runtime_t * node,
671                                    vlib_frame_t * frame)
672 {
673   return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP4));
674 }
675
676 VLIB_NODE_FN (gbp_learn_ip6_node) (vlib_main_t * vm,
677                                    vlib_node_runtime_t * node,
678                                    vlib_frame_t * frame)
679 {
680   return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP6));
681 }
682
683 /* *INDENT-OFF* */
684 VLIB_REGISTER_NODE (gbp_learn_ip4_node) = {
685   .name = "gbp-learn-ip4",
686   .vector_size = sizeof (u32),
687   .format_trace = format_gbp_learn_l3_trace,
688   .type = VLIB_NODE_TYPE_INTERNAL,
689 };
690
691 VNET_FEATURE_INIT (gbp_learn_ip4, static) =
692 {
693   .arc_name = "ip4-unicast",
694   .node_name = "gbp-learn-ip4",
695 };
696
697 VLIB_REGISTER_NODE (gbp_learn_ip6_node) = {
698   .name = "gbp-learn-ip6",
699   .vector_size = sizeof (u32),
700   .format_trace = format_gbp_learn_l3_trace,
701   .type = VLIB_NODE_TYPE_INTERNAL,
702 };
703
704 VNET_FEATURE_INIT (gbp_learn_ip6, static) =
705 {
706   .arc_name = "ip6-unicast",
707   .node_name = "gbp-learn-ip6",
708 };
709
710 /* *INDENT-ON* */
711
712 /*
713  * fd.io coding-style-patch-verification: ON
714  *
715  * Local Variables:
716  * eval: (c-set-style "gnu")
717  * End:
718  */