7f32cbd8a49a1f39e1fdf138237f5887dd8384e1
[vpp.git] / vnet / vnet / vxlan-gpe / decap.c
1 /*
2  * decap.c - decapsulate VXLAN GPE
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 <vlib/vlib.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/vxlan-gpe/vxlan_gpe.h>
21
22 vlib_node_registration_t vxlan_gpe_input_node;
23
24 typedef struct {
25   u32 next_index;
26   u32 tunnel_index;
27   u32 error;
28 } vxlan_gpe_rx_trace_t;
29
30 static u8 * format_vxlan_gpe_rx_trace (u8 * s, va_list * args)
31 {
32   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
33   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
34   vxlan_gpe_rx_trace_t * t = va_arg (*args, vxlan_gpe_rx_trace_t *);
35
36   if (t->tunnel_index != ~0)
37   {
38     s = format (s, "VXLAN-GPE: tunnel %d next %d error %d", t->tunnel_index,
39         t->next_index, t->error);
40   }
41   else
42   {
43     s = format (s, "VXLAN-GPE: no tunnel next %d error %d\n", t->next_index,
44         t->error);
45   }
46   return s;
47 }
48
49
50 static u8 * format_vxlan_gpe_with_length (u8 * s, va_list * args)
51 {
52   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
53   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
54
55
56   return s;
57 }
58
59 always_inline uword
60 vxlan_gpe_input (vlib_main_t * vm,
61                      vlib_node_runtime_t * node,
62                      vlib_frame_t * from_frame,
63                                          u8 is_ip4)
64 {
65   u32 n_left_from, next_index, *from, *to_next;
66   vxlan_gpe_main_t * ngm = &vxlan_gpe_main;
67   vnet_main_t * vnm = ngm->vnet_main;
68   vnet_interface_main_t * im = &vnm->interface_main;
69   u32 last_tunnel_index = ~0;
70   vxlan4_gpe_tunnel_key_t last_key4;
71   vxlan6_gpe_tunnel_key_t last_key6;
72   u32 pkts_decapsulated = 0;
73   u32 cpu_index = os_get_cpu_number ();
74   u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
75
76   if (is_ip4)
77       memset (&last_key4, 0xff, sizeof(last_key4));
78   else
79       memset (&last_key6, 0xff, sizeof(last_key6));
80
81   from = vlib_frame_vector_args (from_frame);
82   n_left_from = from_frame->n_vectors;
83
84   next_index = node->cached_next_index;
85   stats_sw_if_index = node->runtime_data[0];
86   stats_n_packets = stats_n_bytes = 0;
87
88   while (n_left_from > 0)
89   {
90     u32 n_left_to_next;
91
92     vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
93
94     while (n_left_from >= 4 && n_left_to_next >= 2)
95     {
96       u32 bi0, bi1;
97       vlib_buffer_t * b0, *b1;
98       u32 next0, next1;
99       ip4_vxlan_gpe_header_t * iuvn4_0, *iuvn4_1;
100       ip6_vxlan_gpe_header_t * iuvn6_0, *iuvn6_1;
101       uword * p0, *p1;
102       u32 tunnel_index0, tunnel_index1;
103       vxlan_gpe_tunnel_t * t0, *t1;
104       vxlan4_gpe_tunnel_key_t key4_0, key4_1;
105       vxlan6_gpe_tunnel_key_t key6_0, key6_1;
106       u32 error0, error1;
107       u32 sw_if_index0, sw_if_index1, len0, len1;
108
109       /* Prefetch next iteration. */
110       {
111         vlib_buffer_t * p2, *p3;
112
113         p2 = vlib_get_buffer (vm, from[2]);
114         p3 = vlib_get_buffer (vm, from[3]);
115
116         vlib_prefetch_buffer_header(p2, LOAD);
117         vlib_prefetch_buffer_header(p3, LOAD);
118
119         CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
120         CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
121       }
122
123       bi0 = from[0];
124       bi1 = from[1];
125       to_next[0] = bi0;
126       to_next[1] = bi1;
127       from += 2;
128       to_next += 2;
129       n_left_to_next -= 2;
130       n_left_from -= 2;
131
132       b0 = vlib_get_buffer (vm, bi0);
133       b1 = vlib_get_buffer (vm, bi1);
134
135       if (is_ip4)
136       {
137         /* udp leaves current_data pointing at the vxlan-gpe header */
138         vlib_buffer_advance (b0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t)));
139         vlib_buffer_advance (b1, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t)));
140
141         iuvn4_0 = vlib_buffer_get_current (b0);
142         iuvn4_1 = vlib_buffer_get_current (b1);
143
144         /* pop (ip, udp, vxlan) */
145         vlib_buffer_advance (b0, sizeof(*iuvn4_0));
146         vlib_buffer_advance (b1, sizeof(*iuvn4_1));
147       }
148       else
149       {
150         /* udp leaves current_data pointing at the vxlan-gpe header */
151         vlib_buffer_advance (b0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t)));
152         vlib_buffer_advance (b1, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t)));
153
154         iuvn6_0 = vlib_buffer_get_current (b0);
155         iuvn6_1 = vlib_buffer_get_current (b1);
156
157         /* pop (ip, udp, vxlan) */
158         vlib_buffer_advance (b0, sizeof(*iuvn6_0));
159         vlib_buffer_advance (b1, sizeof(*iuvn6_1));
160       }
161
162       tunnel_index0 = ~0;
163       tunnel_index1 = ~0;
164       error0 = 0;
165       error1 = 0;
166
167       if (is_ip4)
168       {
169         next0 = (iuvn4_0->vxlan.protocol < node->n_next_nodes) ?
170                 iuvn4_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
171         next1 = (iuvn4_1->vxlan.protocol < node->n_next_nodes) ?
172                 iuvn4_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
173
174         key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
175         key4_1.local = iuvn4_1->ip4.dst_address.as_u32;
176
177         key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
178         key4_1.remote = iuvn4_1->ip4.src_address.as_u32;
179
180         key4_0.vni = iuvn4_0->vxlan.vni_res;
181         key4_1.vni = iuvn4_1->vxlan.vni_res;
182
183         key4_0.pad = 0;
184         key4_1.pad = 0;
185
186         /* Processing for key4_0 */
187         if (PREDICT_FALSE((key4_0.as_u64[0] != last_key4.as_u64[0])
188                 || (key4_0.as_u64[1] != last_key4.as_u64[1])))
189         {
190           p0 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_0);
191
192           if (p0 == 0)
193           {
194             error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
195             goto trace0;
196           }
197
198           last_key4.as_u64[0] = key4_0.as_u64[0];
199           last_key4.as_u64[1] = key4_0.as_u64[1];
200           tunnel_index0 = last_tunnel_index = p0[0];
201         }
202         else
203           tunnel_index0 = last_tunnel_index;
204       }
205       else /* is_ip6 */
206       {
207         next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ?
208                 iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
209         next1 = (iuvn6_1->vxlan.protocol < node->n_next_nodes) ?
210                 iuvn6_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
211
212         key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
213         key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
214         key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0];
215         key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1];
216
217         key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
218         key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
219         key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0];
220         key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1];
221
222         key6_0.vni = iuvn6_0->vxlan.vni_res;
223         key6_1.vni = iuvn6_1->vxlan.vni_res;
224
225         /* Processing for key6_0 */
226         if (PREDICT_FALSE(memcmp (&key6_0, &last_key6, sizeof(last_key6)) != 0))
227         {
228           p0 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_0);
229
230           if (p0 == 0)
231           {
232             error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
233             goto trace0;
234           }
235
236           memcpy (&last_key6, &key6_0, sizeof(key6_0));
237           tunnel_index0 = last_tunnel_index = p0[0];
238         }
239         else
240           tunnel_index0 = last_tunnel_index;
241       }
242
243       t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0);
244
245       next0 = t0->protocol;
246
247       sw_if_index0 = t0->sw_if_index;
248       len0 = vlib_buffer_length_in_chain (vm, b0);
249
250       /* Required to make the l2 tag push / pop code work on l2 subifs */
251       vnet_update_l2_len (b0);
252
253       /*
254        * ip[46] lookup in the configured FIB
255        */
256       vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
257
258       pkts_decapsulated++;
259       stats_n_packets += 1;
260       stats_n_bytes += len0;
261
262       if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index))
263       {
264         stats_n_packets -= 1;
265         stats_n_bytes -= len0;
266         if (stats_n_packets)
267           vlib_increment_combined_counter (
268               im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
269               cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
270         stats_n_packets = 1;
271         stats_n_bytes = len0;
272         stats_sw_if_index = sw_if_index0;
273       }
274
275       trace0: b0->error = error0 ? node->errors[error0] : 0;
276
277       if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
278       {
279         vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof(*tr));
280         tr->next_index = next0;
281         tr->error = error0;
282         tr->tunnel_index = tunnel_index0;
283       }
284
285       /* Process packet 1 */
286       if (is_ip4)
287       {
288         /* Processing for key4_1 */
289         if (PREDICT_FALSE(
290             (key4_1.as_u64[0] != last_key4.as_u64[0])
291                 || (key4_1.as_u64[1] != last_key4.as_u64[1])))
292         {
293           p1 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_1);
294
295           if (p1 == 0)
296           {
297             error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
298             goto trace1;
299           }
300
301           last_key4.as_u64[0] = key4_1.as_u64[0];
302           last_key4.as_u64[1] = key4_1.as_u64[1];
303           tunnel_index1 = last_tunnel_index = p1[0];
304         }
305         else
306           tunnel_index1 = last_tunnel_index;
307       }
308       else /* is_ip6 */
309       {
310         /* Processing for key6_1 */
311         if (PREDICT_FALSE(memcmp (&key6_1, &last_key6, sizeof(last_key6)) != 0))
312         {
313           p1 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_1);
314
315           if (p1 == 0)
316           {
317             error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
318             goto trace1;
319           }
320
321           memcpy (&last_key6, &key6_1, sizeof(key6_1));
322           tunnel_index1 = last_tunnel_index = p1[0];
323         }
324         else
325           tunnel_index1 = last_tunnel_index;
326       }
327
328       t1 = pool_elt_at_index(ngm->tunnels, tunnel_index1);
329
330       next1 = t1->protocol;
331       sw_if_index1 = t1->sw_if_index;
332       len1 = vlib_buffer_length_in_chain (vm, b1);
333
334       /* Required to make the l2 tag push / pop code work on l2 subifs */
335       vnet_update_l2_len (b1);
336
337       /*
338        * ip[46] lookup in the configured FIB
339        */
340       vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;
341
342       pkts_decapsulated++;
343       stats_n_packets += 1;
344       stats_n_bytes += len1;
345
346       /* Batch stats increment on the same vxlan tunnel so counter
347        is not incremented per packet */
348       if (PREDICT_FALSE(sw_if_index1 != stats_sw_if_index))
349       {
350         stats_n_packets -= 1;
351         stats_n_bytes -= len1;
352         if (stats_n_packets)
353           vlib_increment_combined_counter (
354               im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
355               cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
356         stats_n_packets = 1;
357         stats_n_bytes = len1;
358         stats_sw_if_index = sw_if_index1;
359       }
360       vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;
361
362       trace1: b1->error = error1 ? node->errors[error1] : 0;
363
364       if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
365       {
366         vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b1, sizeof(*tr));
367         tr->next_index = next1;
368         tr->error = error1;
369         tr->tunnel_index = tunnel_index1;
370       }
371
372       vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
373                                       n_left_to_next, bi0, bi1, next0, next1);
374     }
375
376     while (n_left_from > 0 && n_left_to_next > 0)
377     {
378       u32 bi0;
379       vlib_buffer_t * b0;
380       u32 next0;
381       ip4_vxlan_gpe_header_t * iuvn4_0;
382       ip6_vxlan_gpe_header_t * iuvn6_0;
383       uword * p0;
384       u32 tunnel_index0;
385       vxlan_gpe_tunnel_t * t0;
386       vxlan4_gpe_tunnel_key_t key4_0;
387       vxlan6_gpe_tunnel_key_t key6_0;
388       u32 error0;
389       u32 sw_if_index0, len0;
390
391       bi0 = from[0];
392       to_next[0] = bi0;
393       from += 1;
394       to_next += 1;
395       n_left_from -= 1;
396       n_left_to_next -= 1;
397
398       b0 = vlib_get_buffer (vm, bi0);
399
400       if (is_ip4)
401       {
402         /* udp leaves current_data pointing at the vxlan-gpe header */
403         vlib_buffer_advance (
404             b0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t)));
405
406         iuvn4_0 = vlib_buffer_get_current (b0);
407
408         /* pop (ip, udp, vxlan) */
409         vlib_buffer_advance (b0, sizeof(*iuvn4_0));
410       }
411       else
412       {
413         /* udp leaves current_data pointing at the vxlan-gpe header */
414         vlib_buffer_advance (
415             b0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t)));
416
417         iuvn6_0 = vlib_buffer_get_current (b0);
418
419         /* pop (ip, udp, vxlan) */
420         vlib_buffer_advance (b0, sizeof(*iuvn6_0));
421       }
422
423       tunnel_index0 = ~0;
424       error0 = 0;
425
426       if (is_ip4)
427       {
428         next0 =
429             (iuvn4_0->vxlan.protocol < node->n_next_nodes) ?
430                 iuvn4_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
431
432         key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
433         key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
434         key4_0.vni = iuvn4_0->vxlan.vni_res;
435         key4_0.pad = 0;
436
437         /* Processing for key4_0 */
438         if (PREDICT_FALSE(
439             (key4_0.as_u64[0] != last_key4.as_u64[0])
440                 || (key4_0.as_u64[1] != last_key4.as_u64[1])))
441         {
442           p0 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_0);
443
444           if (p0 == 0)
445           {
446             error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
447             goto trace00;
448           }
449
450           last_key4.as_u64[0] = key4_0.as_u64[0];
451           last_key4.as_u64[1] = key4_0.as_u64[1];
452           tunnel_index0 = last_tunnel_index = p0[0];
453         }
454         else
455           tunnel_index0 = last_tunnel_index;
456       }
457       else /* is_ip6 */
458       {
459         next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ?
460                 iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
461
462         key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
463         key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
464         key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
465         key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
466         key6_0.vni = iuvn6_0->vxlan.vni_res;
467
468         /* Processing for key6_0 */
469         if (PREDICT_FALSE(memcmp (&key6_0, &last_key6, sizeof(last_key6)) != 0))
470         {
471           p0 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_0);
472
473           if (p0 == 0)
474           {
475             error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
476             goto trace00;
477           }
478
479           memcpy (&last_key6, &key6_0, sizeof(key6_0));
480           tunnel_index0 = last_tunnel_index = p0[0];
481         }
482         else
483           tunnel_index0 = last_tunnel_index;
484       }
485
486       t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0);
487
488       next0 = t0->protocol;
489
490       sw_if_index0 = t0->sw_if_index;
491       len0 = vlib_buffer_length_in_chain (vm, b0);
492
493       /* Required to make the l2 tag push / pop code work on l2 subifs */
494       vnet_update_l2_len (b0);
495
496       /*
497        * ip[46] lookup in the configured FIB
498        */
499       vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
500
501       pkts_decapsulated++;
502       stats_n_packets += 1;
503       stats_n_bytes += len0;
504
505       /* Batch stats increment on the same vxlan-gpe tunnel so counter
506        is not incremented per packet */
507       if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index))
508       {
509         stats_n_packets -= 1;
510         stats_n_bytes -= len0;
511         if (stats_n_packets)
512           vlib_increment_combined_counter (
513               im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
514               cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
515         stats_n_packets = 1;
516         stats_n_bytes = len0;
517         stats_sw_if_index = sw_if_index0;
518       }
519
520       trace00: b0->error = error0 ? node->errors[error0] : 0;
521
522       if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
523       {
524         vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof(*tr));
525         tr->next_index = next0;
526         tr->error = error0;
527         tr->tunnel_index = tunnel_index0;
528       }
529       vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
530                                       n_left_to_next, bi0, next0);
531     }
532
533     vlib_put_next_frame (vm, node, next_index, n_left_to_next);
534   }
535   vlib_node_increment_counter (vm, vxlan_gpe_input_node.index,
536                                VXLAN_GPE_ERROR_DECAPSULATED, pkts_decapsulated);
537   /* Increment any remaining batch stats */
538   if (stats_n_packets)
539   {
540     vlib_increment_combined_counter (
541         im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, cpu_index,
542         stats_sw_if_index, stats_n_packets, stats_n_bytes);
543     node->runtime_data[0] = stats_sw_if_index;
544   }
545   return from_frame->n_vectors;
546 }
547
548 static uword
549 vxlan4_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node,
550                   vlib_frame_t * from_frame)
551 {
552   return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */1);
553 }
554
555 static uword
556 vxlan6_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node,
557                   vlib_frame_t * from_frame)
558 {
559   return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */0);
560 }
561
562 static char * vxlan_gpe_error_strings[] = {
563 #define vxlan_gpe_error(n,s) s,
564 #include <vnet/vxlan-gpe/vxlan_gpe_error.def>
565 #undef vxlan_gpe_error
566 #undef _
567 };
568
569 VLIB_REGISTER_NODE (vxlan4_gpe_input_node) = {
570   .function = vxlan4_gpe_input,
571   .name = "vxlan4-gpe-input",
572   /* Takes a vector of packets. */
573   .vector_size = sizeof (u32),
574   .type = VLIB_NODE_TYPE_INTERNAL,
575   .n_errors = ARRAY_LEN(vxlan_gpe_error_strings),
576   .error_strings = vxlan_gpe_error_strings,
577
578   .n_next_nodes = VXLAN_GPE_INPUT_N_NEXT,
579   .next_nodes = {
580 #define _(s,n) [VXLAN_GPE_INPUT_NEXT_##s] = n,
581     foreach_vxlan_gpe_input_next
582 #undef _
583   },
584
585   .format_buffer = format_vxlan_gpe_with_length,
586   .format_trace = format_vxlan_gpe_rx_trace,
587   // $$$$ .unformat_buffer = unformat_vxlan_gpe_header,
588 };
589
590 VLIB_NODE_FUNCTION_MULTIARCH (vxlan4_gpe_input_node, vxlan4_gpe_input)
591
592 VLIB_REGISTER_NODE (vxlan6_gpe_input_node) = {
593   .function = vxlan6_gpe_input,
594   .name = "vxlan6-gpe-input",
595   /* Takes a vector of packets. */
596   .vector_size = sizeof (u32),
597   .type = VLIB_NODE_TYPE_INTERNAL,
598   .n_errors = ARRAY_LEN(vxlan_gpe_error_strings),
599   .error_strings = vxlan_gpe_error_strings,
600
601   .n_next_nodes = VXLAN_GPE_INPUT_N_NEXT,
602   .next_nodes = {
603 #define _(s,n) [VXLAN_GPE_INPUT_NEXT_##s] = n,
604     foreach_vxlan_gpe_input_next
605 #undef _
606   },
607
608   .format_buffer = format_vxlan_gpe_with_length,
609   .format_trace = format_vxlan_gpe_rx_trace,
610   // $$$$ .unformat_buffer = unformat_vxlan_gpe_header,
611 };
612
613 VLIB_NODE_FUNCTION_MULTIARCH (vxlan6_gpe_input_node, vxlan6_gpe_input)