e7d49b027cebdc07a67d9f2e4a73cf7b6b557a0b
[vpp.git] / vnet / vnet / vxlan / encap.c
1 /*
2  * Copyright (c) 2015 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 #include <vppinfra/error.h>
16 #include <vppinfra/hash.h>
17 #include <vnet/vnet.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/vxlan/vxlan.h>
21
22 /* Statistics (not all errors) */
23 #define foreach_vxlan_encap_error    \
24 _(ENCAPSULATED, "good packets encapsulated")
25
26 static char * vxlan_encap_error_strings[] = {
27 #define _(sym,string) string,
28   foreach_vxlan_encap_error
29 #undef _
30 };
31
32 typedef enum {
33 #define _(sym,str) VXLAN_ENCAP_ERROR_##sym,
34     foreach_vxlan_encap_error
35 #undef _
36     VXLAN_ENCAP_N_ERROR,
37 } vxlan_encap_error_t;
38
39 typedef enum {
40     VXLAN_ENCAP_NEXT_IP4_LOOKUP,
41     VXLAN_ENCAP_NEXT_IP6_LOOKUP,
42     VXLAN_ENCAP_NEXT_DROP,
43     VXLAN_ENCAP_N_NEXT,
44 } vxlan_encap_next_t;
45
46 typedef struct {
47   u32 tunnel_index;
48   u32 vni;
49 } vxlan_encap_trace_t;
50
51 u8 * format_vxlan_encap_trace (u8 * s, va_list * args)
52 {
53   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
54   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
55   vxlan_encap_trace_t * t 
56       = va_arg (*args, vxlan_encap_trace_t *);
57
58   s = format (s, "VXLAN-ENCAP: tunnel %d vni %d", t->tunnel_index, t->vni);
59   return s;
60 }
61
62
63 #define foreach_fixed_header4_offset            \
64     _(0) _(1) _(2) _(3)
65
66 #define foreach_fixed_header6_offset            \
67     _(0) _(1) _(2) _(3) _(4) _(5) _(6)
68
69 static uword
70 vxlan_encap (vlib_main_t * vm,
71                vlib_node_runtime_t * node,
72                vlib_frame_t * from_frame)
73 {
74   u32 n_left_from, next_index, * from, * to_next;
75   vxlan_main_t * vxm = &vxlan_main;
76   vnet_main_t * vnm = vxm->vnet_main;
77   vnet_interface_main_t * im = &vnm->interface_main;
78   u32 pkts_encapsulated = 0;
79   u16 old_l0 = 0, old_l1 = 0;
80   u32 cpu_index = os_get_cpu_number();
81   u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
82
83   from = vlib_frame_vector_args (from_frame);
84   n_left_from = from_frame->n_vectors;
85
86   next_index = node->cached_next_index;
87   stats_sw_if_index = node->runtime_data[0];
88   stats_n_packets = stats_n_bytes = 0;
89
90   while (n_left_from > 0)
91     {
92       u32 n_left_to_next;
93
94       vlib_get_next_frame (vm, node, next_index,
95                            to_next, n_left_to_next);
96
97       while (n_left_from >= 4 && n_left_to_next >= 2)
98         {
99           u32 bi0, bi1;
100           vlib_buffer_t * b0, * b1;
101           u32 flow_hash0, flow_hash1;
102           u32 next0 = VXLAN_ENCAP_NEXT_IP4_LOOKUP;
103           u32 next1 = VXLAN_ENCAP_NEXT_IP4_LOOKUP;
104           u32 sw_if_index0, sw_if_index1, len0, len1;
105           vnet_hw_interface_t * hi0, * hi1;
106           ip4_header_t * ip4_0, * ip4_1;
107           ip6_header_t * ip6_0, * ip6_1;
108           udp_header_t * udp0, * udp1;
109           u64 * copy_src0, * copy_dst0;
110           u64 * copy_src1, * copy_dst1;
111           u32 * copy_src_last0, * copy_dst_last0;
112           u32 * copy_src_last1, * copy_dst_last1;
113           vxlan_tunnel_t * t0, * t1;
114           u16 new_l0, new_l1;
115           ip_csum_t sum0, sum1;
116           u8 is_ip4_0, is_ip4_1;
117
118           /* Prefetch next iteration. */
119           {
120             vlib_buffer_t * p2, * p3;
121
122             p2 = vlib_get_buffer (vm, from[2]);
123             p3 = vlib_get_buffer (vm, from[3]);
124
125             vlib_prefetch_buffer_header (p2, LOAD);
126             vlib_prefetch_buffer_header (p3, LOAD);
127
128             CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
129             CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
130           }
131
132           bi0 = from[0];
133           bi1 = from[1];
134           to_next[0] = bi0;
135           to_next[1] = bi1;
136           from += 2;
137           to_next += 2;
138           n_left_to_next -= 2;
139           n_left_from -= 2;
140
141           b0 = vlib_get_buffer (vm, bi0);
142           b1 = vlib_get_buffer (vm, bi1);
143
144           flow_hash0 = vnet_l2_compute_flow_hash (b0);
145           flow_hash1 = vnet_l2_compute_flow_hash (b1);
146
147           /* 1-wide cache? */
148           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
149           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX];
150           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
151           hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1); 
152
153           t0 = &vxm->tunnels[hi0->dev_instance];
154           t1 = &vxm->tunnels[hi1->dev_instance];
155
156           is_ip4_0 = (t0->flags & VXLAN_TUNNEL_IS_IPV4);
157           is_ip4_1 = (t1->flags & VXLAN_TUNNEL_IS_IPV4);
158
159           if (PREDICT_FALSE(!is_ip4_0)) next0 = VXLAN_ENCAP_NEXT_IP6_LOOKUP;
160           if (PREDICT_FALSE(!is_ip4_1)) next1 = VXLAN_ENCAP_NEXT_IP6_LOOKUP;
161
162           /* IP4 VXLAN header sizeof(ip4_vxlan_header_t) should be 36 octects */
163           /* IP6 VXLAN header sizeof(ip6_vxlan_header_t) should be 56 octects */
164           if (PREDICT_TRUE(is_ip4_0))
165             ASSERT(vec_len(t0->rewrite) == 36);
166           else
167             ASSERT(vec_len(t0->rewrite) == 56);
168           if (PREDICT_TRUE(is_ip4_1))
169             ASSERT(vec_len(t1->rewrite) == 36);
170           else
171             ASSERT(vec_len(t1->rewrite) == 56);
172
173           /* Apply the rewrite string. $$$$ vnet_rewrite? */
174           vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite));
175           vlib_buffer_advance (b1, -(word)_vec_len(t1->rewrite));
176
177           /* assign both v4 and v6; avoid a branch, optimizer will help us */
178           ip4_0 = vlib_buffer_get_current(b0);
179           ip6_0 = (void *)ip4_0;
180           ip4_1 = vlib_buffer_get_current(b1);
181           ip6_1 = (void *)ip4_1;
182
183           /* Copy the fixed header (v4 and v6 variables point to the same
184            * place at this point)
185            */
186           copy_dst0 = (u64 *) ip4_0;
187           copy_src0 = (u64 *) t0->rewrite;
188
189           copy_dst1 = (u64 *) ip4_1;
190           copy_src1 = (u64 *) t1->rewrite;
191
192           /* Copy first 32 (ip4)/56 (ip6) octets 8-bytes at a time */
193 #define _(offs) copy_dst0[offs] = copy_src0[offs];
194           if (PREDICT_TRUE(is_ip4_0)) {
195             foreach_fixed_header4_offset;
196           } else {
197             foreach_fixed_header6_offset;
198           }
199 #undef _
200 #define _(offs) copy_dst1[offs] = copy_src1[offs];
201           if (PREDICT_TRUE(is_ip4_1)) {
202             foreach_fixed_header4_offset;
203           } else {
204             foreach_fixed_header6_offset;
205           }
206 #undef _
207           /* Last 4 octets. Hopefully gcc will be our friend */
208           if (PREDICT_TRUE(is_ip4_0)) {
209               copy_dst_last0 = (u32 *)(&copy_dst0[4]);
210               copy_src_last0 = (u32 *)(&copy_src0[4]);
211               copy_dst_last0[0] = copy_src_last0[0];
212           }
213           if (PREDICT_TRUE(is_ip4_1)) {
214               copy_dst_last1 = (u32 *)(&copy_dst1[4]);
215               copy_src_last1 = (u32 *)(&copy_src1[4]);
216               copy_dst_last1[0] = copy_src_last1[0];
217           }
218
219           if (PREDICT_TRUE(is_ip4_0)) {
220             /* fix the <bleep>ing outer-IP checksum */
221             sum0 = ip4_0->checksum;
222
223             /* old_l0 always 0, see the rewrite setup */
224             new_l0 = 
225               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
226               sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
227                                    length /* changed member */);
228             ip4_0->checksum = ip_csum_fold (sum0);
229             ip4_0->length = new_l0;
230           } else {
231             new_l0 =
232               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
233                                            - sizeof(*ip6_0));
234             ip6_0->payload_length = new_l0;
235           }
236
237           if (PREDICT_TRUE(is_ip4_1)) {
238             /* fix the <bleep>ing outer-IP checksum */
239             sum1 = ip4_1->checksum;
240
241             /* old_l1 always 0, see the rewrite setup */
242             new_l1 = 
243               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
244               sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
245                                    length /* changed member */);
246             ip4_1->checksum = ip_csum_fold (sum1);
247             ip4_1->length = new_l1;
248           } else {
249             new_l1 =
250               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
251                                            - sizeof(*ip6_1));
252             ip6_1->payload_length = new_l1;
253           }
254           
255           /* Fix UDP length */
256           if (PREDICT_TRUE(is_ip4_0)) {
257             udp0 = (udp_header_t *)(ip4_0+1);
258             new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
259                                            - sizeof (*ip4_0));
260           } else {
261             udp0 = (udp_header_t *)(ip6_0+1);
262             new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
263                                            - sizeof (*ip6_0));
264           }
265           if (PREDICT_TRUE(is_ip4_1)) {
266             udp1 = (udp_header_t *)(ip4_1+1);
267             new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
268                                            - sizeof (*ip4_1));
269           } else {
270             udp1 = (udp_header_t *)(ip6_1+1);
271             new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
272                                            - sizeof (*ip6_1));
273           }
274           
275           udp0->length = new_l0;
276           udp0->src_port = flow_hash0;
277
278           udp1->length = new_l1;
279           udp1->src_port = flow_hash1;
280
281           if (PREDICT_FALSE(!is_ip4_0)) {
282                 int bogus = 0;
283                 /* IPv6 UDP checksum is mandatory */
284                 udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0,
285                                                         ip6_0, &bogus);
286                 ASSERT(bogus == 0);
287                 if (udp0->checksum == 0)
288                     udp0->checksum = 0xffff;
289           }
290
291           if (PREDICT_FALSE(!is_ip4_1)) {
292                 int bogus = 0;
293                 /* IPv6 UDP checksum is mandatory */
294                 udp1->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b1,
295                                                         ip6_1, &bogus);
296                 ASSERT(bogus == 0);
297                 if (udp1->checksum == 0)
298                     udp1->checksum = 0xffff;
299           }
300
301           /* Reset to look up tunnel partner in the configured FIB */
302           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index;
303           vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index;
304           vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;
305           vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1;
306           pkts_encapsulated += 2;
307
308           len0 = vlib_buffer_length_in_chain (vm, b0);
309           len1 = vlib_buffer_length_in_chain (vm, b0);
310           stats_n_packets += 2;
311           stats_n_bytes += len0 + len1;
312
313           /* Batch stats increment on the same vxlan tunnel so counter is not
314              incremented per packet. Note stats are still incremented for deleted
315              and admin-down tunnel where packets are dropped. It is not worthwhile
316              to check for this rare case and affect normal path performance. */
317           if (PREDICT_FALSE ((sw_if_index0 != stats_sw_if_index) ||
318                              (sw_if_index1 != stats_sw_if_index))) 
319             {
320               stats_n_packets -= 2;
321               stats_n_bytes -= len0 + len1;
322               if (sw_if_index0 == sw_if_index1) 
323                 {
324                   if (stats_n_packets) 
325                     vlib_increment_combined_counter 
326                       (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX,
327                        cpu_index, stats_sw_if_index, 
328                        stats_n_packets, stats_n_bytes);
329                   stats_sw_if_index = sw_if_index0;
330                   stats_n_packets = 2;
331                   stats_n_bytes = len0 + len1;
332                 }
333               else 
334                 {
335                   vlib_increment_combined_counter 
336                       (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX,
337                        cpu_index, sw_if_index0, 1, len0);
338                   vlib_increment_combined_counter 
339                       (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX,
340                        cpu_index, sw_if_index1, 1, len1);
341                 }
342             }
343
344           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
345             {
346               vxlan_encap_trace_t *tr = 
347                 vlib_add_trace (vm, node, b0, sizeof (*tr));
348               tr->tunnel_index = t0 - vxm->tunnels;
349               tr->vni = t0->vni;
350            }
351
352           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
353             {
354               vxlan_encap_trace_t *tr = 
355                 vlib_add_trace (vm, node, b1, sizeof (*tr));
356               tr->tunnel_index = t1 - vxm->tunnels;
357               tr->vni = t1->vni;
358             }
359
360           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
361                                            to_next, n_left_to_next,
362                                            bi0, bi1, next0, next1);
363         }
364
365       while (n_left_from > 0 && n_left_to_next > 0)
366         {
367           u32 bi0;
368           vlib_buffer_t * b0;
369           u32 flow_hash0;
370           u32 next0 = VXLAN_ENCAP_NEXT_IP4_LOOKUP;
371           u32 sw_if_index0, len0;
372           vnet_hw_interface_t * hi0;
373           ip4_header_t * ip4_0;
374           ip6_header_t * ip6_0;
375           udp_header_t * udp0;
376           u64 * copy_src0, * copy_dst0;
377           u32 * copy_src_last0, * copy_dst_last0;
378           vxlan_tunnel_t * t0;
379           u16 new_l0;
380           ip_csum_t sum0;
381           u8 is_ip4_0;
382
383           bi0 = from[0];
384           to_next[0] = bi0;
385           from += 1;
386           to_next += 1;
387           n_left_from -= 1;
388           n_left_to_next -= 1;
389
390           b0 = vlib_get_buffer (vm, bi0);
391
392           flow_hash0 = vnet_l2_compute_flow_hash(b0);
393
394           /* 1-wide cache? */
395           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
396           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
397
398           t0 = &vxm->tunnels[hi0->dev_instance];
399
400           is_ip4_0 = (t0->flags & VXLAN_TUNNEL_IS_IPV4);
401
402           if (PREDICT_FALSE(!is_ip4_0)) next0 = VXLAN_ENCAP_NEXT_IP6_LOOKUP;
403
404           /* IP4 VXLAN header sizeof(ip4_vxlan_header_t) should be 36 octets */
405           /* IP6 VXLAN header sizeof(ip4_vxlan_header_t) should be 56 octets */
406           if (PREDICT_TRUE(is_ip4_0))
407             ASSERT(vec_len(t0->rewrite) == 36);
408           else
409             ASSERT(vec_len(t0->rewrite) == 56);
410
411           /* Apply the rewrite string. $$$$ vnet_rewrite? */
412           vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite));
413
414           /* assign both v4 and v6; avoid a branch, optimizer will help us */
415           ip4_0 = vlib_buffer_get_current(b0);
416           ip6_0 = (void *)ip4_0;
417
418           /* Copy the fixed header (v4 and v6 variables point to the same
419            * place at this point)
420            */
421           copy_dst0 = (u64 *) ip4_0;
422           copy_src0 = (u64 *) t0->rewrite;
423
424           /* Copy first 32 octets 8-bytes at a time */
425 #define _(offs) copy_dst0[offs] = copy_src0[offs];
426           if (PREDICT_TRUE(is_ip4_0)) {
427             foreach_fixed_header4_offset;
428           } else {
429             foreach_fixed_header6_offset;
430           }
431 #undef _
432           if (PREDICT_TRUE(is_ip4_0)) {
433             /* Last 4 octets. Hopefully gcc will be our friend */
434             copy_dst_last0 = (u32 *)(&copy_dst0[4]);
435             copy_src_last0 = (u32 *)(&copy_src0[4]);
436           
437             copy_dst_last0[0] = copy_src_last0[0];
438           }
439
440           if (PREDICT_TRUE(is_ip4_0)) {
441             /* fix the <bleep>ing outer-IP checksum */
442             sum0 = ip4_0->checksum;
443
444             /* old_l0 always 0, see the rewrite setup */
445             new_l0 = 
446               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
447               sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
448                                  length /* changed member */);
449             ip4_0->checksum = ip_csum_fold (sum0);
450             ip4_0->length = new_l0;
451           } else {
452             new_l0 =
453               clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
454                                            - sizeof(*ip6_0));
455             ip6_0->payload_length = new_l0;
456           }
457           
458           /* Fix UDP length */
459           if (PREDICT_TRUE(is_ip4_0)) {
460             udp0 = (udp_header_t *)(ip4_0+1);
461             new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
462                                            - sizeof (*ip4_0));
463           } else {
464             udp0 = (udp_header_t *)(ip6_0+1);
465             new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
466                                            - sizeof (*ip6_0));
467           }
468           
469           udp0->length = new_l0;
470           udp0->src_port = flow_hash0;
471
472           if (PREDICT_FALSE(!is_ip4_0)) {
473                 int bogus = 0;
474                 /* IPv6 UDP checksum is mandatory */
475                 udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0,
476                                                         ip6_0, &bogus);
477                 ASSERT(bogus == 0);
478                 if (udp0->checksum == 0)
479                     udp0->checksum = 0xffff;
480           }
481
482
483           /* vnet_update_l2_len (b0);  do we need this? cluke */
484
485           /* Reset to look up tunnel partner in the configured FIB */
486           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index;
487           vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0;
488           pkts_encapsulated ++;
489
490           len0 = vlib_buffer_length_in_chain (vm, b0);
491           stats_n_packets += 1;
492           stats_n_bytes += len0;
493
494           /* Batch stats increment on the same vxlan tunnel so counter is not
495              incremented per packet. Note stats are still incremented for deleted
496              and admin-down tunnel where packets are dropped. It is not worthwhile
497              to check for this rare case and affect normal path performance. */
498           if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) 
499             {
500               stats_n_packets -= 1;
501               stats_n_bytes -= len0;
502               if (stats_n_packets)
503                 vlib_increment_combined_counter 
504                   (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX,
505                    cpu_index, stats_sw_if_index, 
506                    stats_n_packets, stats_n_bytes);
507               stats_n_packets = 1;
508               stats_n_bytes = len0;
509               stats_sw_if_index = sw_if_index0;
510             }
511
512           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
513             {
514               vxlan_encap_trace_t *tr = 
515                 vlib_add_trace (vm, node, b0, sizeof (*tr));
516               tr->tunnel_index = t0 - vxm->tunnels;
517               tr->vni = t0->vni;
518             }
519           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
520                                            to_next, n_left_to_next,
521                                            bi0, next0);
522         }
523
524       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
525     }
526
527   /* Do we still need this now that tunnel tx stats is kept? */
528   vlib_node_increment_counter (vm, node->node_index, 
529                                VXLAN_ENCAP_ERROR_ENCAPSULATED, 
530                                pkts_encapsulated);
531
532   /* Increment any remaining batch stats */
533   if (stats_n_packets)
534     {
535       vlib_increment_combined_counter 
536         (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX,
537          cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
538       node->runtime_data[0] = stats_sw_if_index;
539     }
540
541   return from_frame->n_vectors;
542 }
543
544 VLIB_REGISTER_NODE (vxlan_encap_node) = {
545   .function = vxlan_encap,
546   .name = "vxlan-encap",
547   .vector_size = sizeof (u32),
548   .format_trace = format_vxlan_encap_trace,
549   .type = VLIB_NODE_TYPE_INTERNAL,
550
551   .n_errors = ARRAY_LEN(vxlan_encap_error_strings),
552   .error_strings = vxlan_encap_error_strings,
553
554   .n_next_nodes = VXLAN_ENCAP_N_NEXT,
555
556   .next_nodes = {
557         [VXLAN_ENCAP_NEXT_IP4_LOOKUP] = "ip4-lookup",
558         [VXLAN_ENCAP_NEXT_IP6_LOOKUP] = "ip6-lookup",
559         [VXLAN_ENCAP_NEXT_DROP] = "error-drop",
560   },
561 };
562
563 VLIB_NODE_FUNCTION_MULTIARCH (vxlan_encap_node, vxlan_encap)
564