7f196c986d96cabc58b67b768dbd5717ebe9b4a1
[vpp.git] / src / plugins / lb / node.c
1 /*
2  * Copyright (c) 2016 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 <lb/lb.h>
17 #include <vnet/fib/ip4_fib.h>
18
19 #include <vnet/gre/packet.h>
20 #include <lb/lbhash.h>
21
22 #define foreach_lb_error \
23  _(NONE, "no error") \
24  _(PROTO_NOT_SUPPORTED, "protocol not supported")
25
26 typedef enum
27 {
28 #define _(sym,str) LB_ERROR_##sym,
29   foreach_lb_error
30 #undef _
31   LB_N_ERROR,
32 } lb_error_t;
33
34 static char *lb_error_strings[] =
35   {
36 #define _(sym,string) string,
37       foreach_lb_error
38 #undef _
39     };
40
41 typedef struct
42 {
43   u32 vip_index;
44   u32 as_index;
45 } lb_trace_t;
46
47 typedef struct
48 {
49   u32 vip_index;
50
51   u32 node_port;
52 } lb_nodeport_trace_t;
53
54 typedef struct
55 {
56   u32 vip_index;
57   u32 as_index;
58   u32 rx_sw_if_index;
59   u32 next_index;
60 } lb_nat_trace_t;
61
62 u8 *
63 format_lb_trace (u8 * s, va_list * args)
64 {
65   lb_main_t *lbm = &lb_main;
66   CLIB_UNUSED(vlib_main_t * vm)
67 = va_arg (*args, vlib_main_t *);
68     CLIB_UNUSED(vlib_node_t * node)
69   = va_arg (*args, vlib_node_t *);
70   lb_trace_t *t = va_arg (*args, lb_trace_t *);
71   if (pool_is_free_index(lbm->vips, t->vip_index))
72     {
73       s = format (s, "lb vip[%d]: This VIP was freed since capture\n");
74     }
75   else
76     {
77       s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip,
78                   &lbm->vips[t->vip_index]);
79     }
80   if (pool_is_free_index(lbm->ass, t->as_index))
81     {
82       s = format (s, "lb as[%d]: This AS was freed since capture\n");
83     }
84   else
85     {
86       s = format (s, "lb as[%d]: %U\n", t->as_index, format_lb_as,
87                   &lbm->ass[t->as_index]);
88     }
89   return s;
90 }
91
92 u8 *
93 format_lb_nat_trace (u8 * s, va_list * args)
94 {
95   lb_main_t *lbm = &lb_main;
96   CLIB_UNUSED(vlib_main_t * vm)
97 = va_arg (*args, vlib_main_t *);
98     CLIB_UNUSED(vlib_node_t * node)
99   = va_arg (*args, vlib_node_t *);
100   lb_nat_trace_t *t = va_arg (*args, lb_nat_trace_t *);
101
102   if (pool_is_free_index(lbm->vips, t->vip_index))
103     {
104       s = format (s, "lb vip[%d]: This VIP was freed since capture\n");
105     }
106   else
107     {
108       s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip,
109                   &lbm->vips[t->vip_index]);
110     }
111   if (pool_is_free_index(lbm->ass, t->as_index))
112     {
113       s = format (s, "lb as[%d]: This AS was freed since capture\n");
114     }
115   else
116     {
117       s = format (s, "lb as[%d]: %U\n", t->as_index, format_lb_as,
118                   &lbm->ass[t->as_index]);
119     }
120   s = format (s, "lb nat: rx_sw_if_index = %d, next_index = %d",
121               t->rx_sw_if_index, t->next_index);
122
123   return s;
124 }
125
126 lb_hash_t *
127 lb_get_sticky_table (u32 thread_index)
128 {
129   lb_main_t *lbm = &lb_main;
130   lb_hash_t *sticky_ht = lbm->per_cpu[thread_index].sticky_ht;
131   //Check if size changed
132   if (PREDICT_FALSE(
133       sticky_ht && (lbm->per_cpu_sticky_buckets != lb_hash_nbuckets(sticky_ht))))
134     {
135       //Dereference everything in there
136       lb_hash_bucket_t *b;
137       u32 i;
138       lb_hash_foreach_entry(sticky_ht, b, i)
139         {
140           vlib_refcount_add (&lbm->as_refcount, thread_index, b->value[i], -1);
141           vlib_refcount_add (&lbm->as_refcount, thread_index, 0, 1);
142         }
143
144       lb_hash_free (sticky_ht);
145       sticky_ht = NULL;
146     }
147
148   //Create if necessary
149   if (PREDICT_FALSE(sticky_ht == NULL))
150     {
151       lbm->per_cpu[thread_index].sticky_ht = lb_hash_alloc (
152           lbm->per_cpu_sticky_buckets, lbm->flow_timeout);
153       sticky_ht = lbm->per_cpu[thread_index].sticky_ht;
154       clib_warning("Regenerated sticky table %p", sticky_ht);
155     }
156
157   ASSERT(sticky_ht);
158
159   //Update timeout
160   sticky_ht->timeout = lbm->flow_timeout;
161   return sticky_ht;
162 }
163
164 u64
165 lb_node_get_other_ports4 (ip4_header_t *ip40)
166 {
167   return 0;
168 }
169
170 u64
171 lb_node_get_other_ports6 (ip6_header_t *ip60)
172 {
173   return 0;
174 }
175
176 static_always_inline void
177 lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash,
178                   u32 *vip_idx, u8 per_port_vip)
179 {
180   vip_port_key_t key;
181   clib_bihash_kv_8_8_t kv, value;
182   ip4_header_t *ip40;
183   ip6_header_t *ip60;
184   lb_vip_t *vip0;
185   u64 ports;
186
187   /* For vip case, retrieve vip index for ip lookup */
188   *vip_idx = vnet_buffer (p)->ip.adj_index[VLIB_TX];
189
190   /* Extract the L4 port number from the packet */
191   if (is_input_v4)
192     {
193       ip40 = vlib_buffer_get_current (p);
194       if (PREDICT_TRUE(
195           ip40->protocol == IP_PROTOCOL_TCP
196               || ip40->protocol == IP_PROTOCOL_UDP))
197         ports = ((u64) ((udp_header_t *) (ip40 + 1))->src_port << 16)
198             | ((u64) ((udp_header_t *) (ip40 + 1))->dst_port);
199       else
200         ports = lb_node_get_other_ports4 (ip40);
201     }
202   else
203     {
204       ip60 = vlib_buffer_get_current (p);
205
206       if (PREDICT_TRUE(
207           ip60->protocol == IP_PROTOCOL_TCP
208               || ip60->protocol == IP_PROTOCOL_UDP))
209         ports = ((u64) ((udp_header_t *) (ip60 + 1))->src_port << 16)
210             | ((u64) ((udp_header_t *) (ip60 + 1))->dst_port);
211       else
212         ports = lb_node_get_other_ports6 (ip60);
213     }
214
215   if (per_port_vip)
216     {
217       /* For per-port-vip case, ip lookup stores placeholder index */
218       key.vip_prefix_index = *vip_idx;
219       key.port = (u16) (ports & 0xFFFF);
220       if (is_input_v4)
221         {
222           key.protocol = ip40->protocol;
223         }
224       else
225         {
226           key.protocol = ip60->protocol;
227         }
228
229       /* For per-port-vip case, retrieve vip index for vip_port_filter table */
230       kv.key = key.as_u64;
231       if (clib_bihash_search_8_8 (&lbm->vip_index_per_port, &kv, &value) < 0)
232         {
233           /* Set default vip */
234           *vip_idx = 0;
235         }
236       else
237         {
238           *vip_idx = value.value;
239         }
240     }
241
242   vip0 = pool_elt_at_index (lbm->vips, *vip_idx);
243
244   if (is_input_v4)
245     {
246       if (lb_vip_is_src_ip_sticky (vip0))
247         {
248           *hash = lb_hash_hash (*((u64 *) &ip40->address_pair), 0, 0, 0, 0);
249         }
250       else
251         {
252           *hash =
253             lb_hash_hash (*((u64 *) &ip40->address_pair), ports, 0, 0, 0);
254         }
255     }
256   else
257     {
258       if (lb_vip_is_src_ip_sticky (vip0))
259         {
260           *hash = lb_hash_hash (
261             ip60->src_address.as_u64[0], ip60->src_address.as_u64[1],
262             ip60->dst_address.as_u64[0], ip60->dst_address.as_u64[1], 0);
263         }
264       else
265         {
266           *hash = lb_hash_hash (
267             ip60->src_address.as_u64[0], ip60->src_address.as_u64[1],
268             ip60->dst_address.as_u64[0], ip60->dst_address.as_u64[1], ports);
269         }
270     }
271 }
272
273 /* clang-format off */
274 static_always_inline uword
275 lb_node_fn (vlib_main_t * vm,
276             vlib_node_runtime_t * node,
277             vlib_frame_t * frame,
278             u8 is_input_v4, //Compile-time parameter stating that is input is v4 (or v6)
279             lb_encap_type_t encap_type, //Compile-time parameter is GRE4/GRE6/L3DSR/NAT4/NAT6
280             u8 per_port_vip) //Compile-time parameter stating that is per_port_vip or not
281 {
282   lb_main_t *lbm = &lb_main;
283   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
284   u32 thread_index = vm->thread_index;
285   u32 lb_time = lb_hash_time_now (vm);
286
287   lb_hash_t *sticky_ht = lb_get_sticky_table (thread_index);
288   from = vlib_frame_vector_args (frame);
289   n_left_from = frame->n_vectors;
290   next_index = node->cached_next_index;
291
292   u32 nexthash0 = 0;
293   u32 next_vip_idx0 = ~0;
294   if (PREDICT_TRUE(n_left_from > 0))
295     {
296       vlib_buffer_t *p0 = vlib_get_buffer (vm, from[0]);
297       lb_node_get_hash (lbm, p0, is_input_v4, &nexthash0,
298                         &next_vip_idx0, per_port_vip);
299     }
300
301   while (n_left_from > 0)
302     {
303       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
304       while (n_left_from > 0 && n_left_to_next > 0)
305         {
306           u32 pi0;
307           vlib_buffer_t *p0;
308           lb_vip_t *vip0;
309           u32 asindex0 = 0;
310           u16 len0;
311           u32 available_index0;
312           u8 counter = 0;
313           u32 hash0 = nexthash0;
314           u32 vip_index0 = next_vip_idx0;
315           u32 next0;
316
317           if (PREDICT_TRUE(n_left_from > 1))
318             {
319               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[1]);
320               //Compute next hash and prefetch bucket
321               lb_node_get_hash (lbm, p1, is_input_v4,
322                                 &nexthash0, &next_vip_idx0,
323                                 per_port_vip);
324               lb_hash_prefetch_bucket (sticky_ht, nexthash0);
325               //Prefetch for encap, next
326               CLIB_PREFETCH(vlib_buffer_get_current (p1) - 64, 64, STORE);
327             }
328
329           if (PREDICT_TRUE(n_left_from > 2))
330             {
331               vlib_buffer_t *p2;
332               p2 = vlib_get_buffer (vm, from[2]);
333               /* prefetch packet header and data */
334               vlib_prefetch_buffer_header(p2, STORE);
335               CLIB_PREFETCH(vlib_buffer_get_current (p2), 64, STORE);
336             }
337
338           pi0 = to_next[0] = from[0];
339           from += 1;
340           n_left_from -= 1;
341           to_next += 1;
342           n_left_to_next -= 1;
343
344           p0 = vlib_get_buffer (vm, pi0);
345
346           vip0 = pool_elt_at_index(lbm->vips, vip_index0);
347
348           if (is_input_v4)
349             {
350               ip4_header_t *ip40;
351               ip40 = vlib_buffer_get_current (p0);
352               len0 = clib_net_to_host_u16 (ip40->length);
353             }
354           else
355             {
356               ip6_header_t *ip60;
357               ip60 = vlib_buffer_get_current (p0);
358               len0 = clib_net_to_host_u16 (ip60->payload_length)
359                   + sizeof(ip6_header_t);
360             }
361
362           lb_hash_get (sticky_ht, hash0,
363                        vip_index0, lb_time,
364                        &available_index0, &asindex0);
365
366           if (PREDICT_TRUE(asindex0 != 0))
367             {
368               //Found an existing entry
369               counter = LB_VIP_COUNTER_NEXT_PACKET;
370             }
371           else if (PREDICT_TRUE(available_index0 != ~0))
372             {
373               //There is an available slot for a new flow
374               asindex0 =
375                   vip0->new_flow_table[hash0 & vip0->new_flow_table_mask].as_index;
376               counter = LB_VIP_COUNTER_FIRST_PACKET;
377               counter = (asindex0 == 0) ? LB_VIP_COUNTER_NO_SERVER : counter;
378
379               //TODO: There are race conditions with as0 and vip0 manipulation.
380               //Configuration may be changed, vectors resized, etc...
381
382               //Dereference previously used
383               vlib_refcount_add (
384                   &lbm->as_refcount, thread_index,
385                   lb_hash_available_value (sticky_ht, hash0, available_index0),
386                   -1);
387               vlib_refcount_add (&lbm->as_refcount, thread_index, asindex0, 1);
388
389               //Add sticky entry
390               //Note that when there is no AS configured, an entry is configured anyway.
391               //But no configured AS is not something that should happen
392               lb_hash_put (sticky_ht, hash0, asindex0,
393                            vip_index0,
394                            available_index0, lb_time);
395             }
396           else
397             {
398               //Could not store new entry in the table
399               asindex0 =
400                   vip0->new_flow_table[hash0 & vip0->new_flow_table_mask].as_index;
401               counter = LB_VIP_COUNTER_UNTRACKED_PACKET;
402             }
403
404           vlib_increment_simple_counter (
405               &lbm->vip_counters[counter], thread_index,
406               vip_index0,
407               1);
408
409           //Now let's encap
410           if ((encap_type == LB_ENCAP_TYPE_GRE4)
411               || (encap_type == LB_ENCAP_TYPE_GRE6))
412             {
413               gre_header_t *gre0;
414               if (encap_type == LB_ENCAP_TYPE_GRE4) /* encap GRE4*/
415                 {
416                   ip4_header_t *ip40;
417                   vlib_buffer_advance (
418                       p0, -sizeof(ip4_header_t) - sizeof(gre_header_t));
419                   ip40 = vlib_buffer_get_current (p0);
420                   gre0 = (gre_header_t *) (ip40 + 1);
421                   ip40->src_address = lbm->ip4_src_address;
422                   ip40->dst_address = lbm->ass[asindex0].address.ip4;
423                   ip40->ip_version_and_header_length = 0x45;
424                   ip40->ttl = 128;
425                   ip40->fragment_id = 0;
426                   ip40->flags_and_fragment_offset = 0;
427                   ip40->length = clib_host_to_net_u16 (
428                       len0 + sizeof(gre_header_t) + sizeof(ip4_header_t));
429                   ip40->protocol = IP_PROTOCOL_GRE;
430                   ip40->checksum = ip4_header_checksum (ip40);
431                 }
432               else /* encap GRE6*/
433                 {
434                   ip6_header_t *ip60;
435                   vlib_buffer_advance (
436                       p0, -sizeof(ip6_header_t) - sizeof(gre_header_t));
437                   ip60 = vlib_buffer_get_current (p0);
438                   gre0 = (gre_header_t *) (ip60 + 1);
439                   ip60->dst_address = lbm->ass[asindex0].address.ip6;
440                   ip60->src_address = lbm->ip6_src_address;
441                   ip60->hop_limit = 128;
442                   ip60->ip_version_traffic_class_and_flow_label =
443                       clib_host_to_net_u32 (0x6 << 28);
444                   ip60->payload_length = clib_host_to_net_u16 (
445                       len0 + sizeof(gre_header_t));
446                   ip60->protocol = IP_PROTOCOL_GRE;
447                 }
448
449               gre0->flags_and_version = 0;
450               gre0->protocol =
451                   (is_input_v4) ?
452                       clib_host_to_net_u16 (0x0800) :
453                       clib_host_to_net_u16 (0x86DD);
454             }
455           else if (encap_type == LB_ENCAP_TYPE_L3DSR) /* encap L3DSR*/
456             {
457               ip4_header_t *ip40;
458               ip_csum_t csum;
459               u32 old_dst, new_dst;
460               u8 old_tos, new_tos;
461
462               ip40 = vlib_buffer_get_current (p0);
463               old_dst = ip40->dst_address.as_u32;
464               new_dst = lbm->ass[asindex0].address.ip4.as_u32;
465               ip40->dst_address.as_u32 = lbm->ass[asindex0].address.ip4.as_u32;
466               /* Get and rewrite DSCP bit */
467               old_tos = ip40->tos;
468               new_tos = (u8) ((vip0->encap_args.dscp & 0x3F) << 2);
469               ip40->tos = (u8) ((vip0->encap_args.dscp & 0x3F) << 2);
470
471               csum = ip40->checksum;
472               csum = ip_csum_update (csum, old_tos, new_tos,
473                                      ip4_header_t,
474                                      tos /* changed member */);
475               csum = ip_csum_update (csum, old_dst, new_dst,
476                                      ip4_header_t,
477                                      dst_address /* changed member */);
478               ip40->checksum = ip_csum_fold (csum);
479
480               /* Recomputing L4 checksum after dst-IP modifying */
481               if (ip40->protocol == IP_PROTOCOL_TCP)
482                 {
483                   tcp_header_t *th0;
484                   th0 = ip4_next_header (ip40);
485                   th0->checksum = 0;
486                   th0->checksum = ip4_tcp_udp_compute_checksum (vm, p0, ip40);
487                 }
488               else if (ip40->protocol == IP_PROTOCOL_UDP)
489                 {
490                   udp_header_t *uh0;
491                   uh0 = ip4_next_header (ip40);
492                   uh0->checksum = 0;
493                   uh0->checksum = ip4_tcp_udp_compute_checksum (vm, p0, ip40);
494                 }
495             }
496           else if ((encap_type == LB_ENCAP_TYPE_NAT4)
497               || (encap_type == LB_ENCAP_TYPE_NAT6))
498             {
499               ip_csum_t csum;
500               udp_header_t *uh;
501
502               /* do NAT */
503               if ((is_input_v4 == 1) && (encap_type == LB_ENCAP_TYPE_NAT4))
504                 {
505                   /* NAT44 */
506                   ip4_header_t *ip40;
507                   u32 old_dst;
508                   ip40 = vlib_buffer_get_current (p0);
509                   uh = (udp_header_t *) (ip40 + 1);
510                   old_dst = ip40->dst_address.as_u32;
511                   ip40->dst_address = lbm->ass[asindex0].address.ip4;
512
513                   csum = ip40->checksum;
514                   csum = ip_csum_sub_even (csum, old_dst);
515                   csum = ip_csum_add_even (
516                       csum, lbm->ass[asindex0].address.ip4.as_u32);
517                   ip40->checksum = ip_csum_fold (csum);
518
519                   if (ip40->protocol == IP_PROTOCOL_UDP)
520                     {
521                       uh->dst_port = vip0->encap_args.target_port;
522                       csum = uh->checksum;
523                       csum = ip_csum_sub_even (csum, old_dst);
524                       csum = ip_csum_add_even (
525                           csum, lbm->ass[asindex0].address.ip4.as_u32);
526                       uh->checksum = ip_csum_fold (csum);
527                     }
528                   else
529                     {
530                       asindex0 = 0;
531                     }
532                 }
533               else if ((is_input_v4 == 0) && (encap_type == LB_ENCAP_TYPE_NAT6))
534                 {
535                   /* NAT66 */
536                   ip6_header_t *ip60;
537                   ip6_address_t old_dst;
538
539                   ip60 = vlib_buffer_get_current (p0);
540                   uh = (udp_header_t *) (ip60 + 1);
541
542                   old_dst.as_u64[0] = ip60->dst_address.as_u64[0];
543                   old_dst.as_u64[1] = ip60->dst_address.as_u64[1];
544                   ip60->dst_address.as_u64[0] =
545                       lbm->ass[asindex0].address.ip6.as_u64[0];
546                   ip60->dst_address.as_u64[1] =
547                       lbm->ass[asindex0].address.ip6.as_u64[1];
548
549                   if (PREDICT_TRUE(ip60->protocol == IP_PROTOCOL_UDP))
550                     {
551                       uh->dst_port = vip0->encap_args.target_port;
552                       csum = uh->checksum;
553                       csum = ip_csum_sub_even (csum, old_dst.as_u64[0]);
554                       csum = ip_csum_sub_even (csum, old_dst.as_u64[1]);
555                       csum = ip_csum_add_even (
556                           csum, lbm->ass[asindex0].address.ip6.as_u64[0]);
557                       csum = ip_csum_add_even (
558                           csum, lbm->ass[asindex0].address.ip6.as_u64[1]);
559                       uh->checksum = ip_csum_fold (csum);
560                     }
561                   else
562                     {
563                       asindex0 = 0;
564                     }
565                 }
566             }
567           next0 = lbm->ass[asindex0].dpo.dpoi_next_node;
568           //Note that this is going to error if asindex0 == 0
569           vnet_buffer (p0)->ip.adj_index[VLIB_TX] =
570               lbm->ass[asindex0].dpo.dpoi_index;
571
572           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
573             {
574               lb_trace_t *tr = vlib_add_trace (vm, node, p0, sizeof(*tr));
575               tr->as_index = asindex0;
576               tr->vip_index = vip_index0;
577             }
578
579           //Enqueue to next
580           vlib_validate_buffer_enqueue_x1(
581               vm, node, next_index, to_next, n_left_to_next, pi0, next0);
582         }
583       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
584     }
585
586   return frame->n_vectors;
587 }
588 /* clang-format on */
589
590 u8 *
591 format_nodeport_lb_trace (u8 * s, va_list * args)
592 {
593   lb_main_t *lbm = &lb_main;
594   CLIB_UNUSED(vlib_main_t * vm)
595 = va_arg (*args, vlib_main_t *);
596     CLIB_UNUSED(vlib_node_t * node)
597   = va_arg (*args, vlib_node_t *);
598   lb_nodeport_trace_t *t = va_arg (*args, lb_nodeport_trace_t *);
599   if (pool_is_free_index(lbm->vips, t->vip_index))
600     {
601       s = format (s, "lb vip[%d]: This VIP was freed since capture\n");
602     }
603   else
604     {
605       s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip,
606                   &lbm->vips[t->vip_index]);
607     }
608
609   s = format (s, "  lb node_port: %d", t->node_port);
610
611   return s;
612 }
613
614 static uword
615 lb_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
616                      vlib_frame_t * frame, u8 is_input_v4)
617 {
618   lb_main_t *lbm = &lb_main;
619   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
620
621   from = vlib_frame_vector_args (frame);
622   n_left_from = frame->n_vectors;
623   next_index = node->cached_next_index;
624
625   while (n_left_from > 0)
626     {
627       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
628
629       while (n_left_from > 0 && n_left_to_next > 0)
630         {
631           u32 pi0;
632           vlib_buffer_t *p0;
633           udp_header_t * udp_0;
634           uword * entry0;
635
636           if (PREDICT_TRUE(n_left_from > 1))
637             {
638               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[1]);
639               //Prefetch for encap, next
640               CLIB_PREFETCH(vlib_buffer_get_current (p1) - 64, 64, STORE);
641             }
642
643           if (PREDICT_TRUE(n_left_from > 2))
644             {
645               vlib_buffer_t *p2;
646               p2 = vlib_get_buffer (vm, from[2]);
647               /* prefetch packet header and data */
648               vlib_prefetch_buffer_header(p2, STORE);
649               CLIB_PREFETCH(vlib_buffer_get_current (p2), 64, STORE);
650             }
651
652           pi0 = to_next[0] = from[0];
653           from += 1;
654           n_left_from -= 1;
655           to_next += 1;
656           n_left_to_next -= 1;
657
658           p0 = vlib_get_buffer (vm, pi0);
659
660           if (is_input_v4)
661             {
662               ip4_header_t *ip40;
663               vlib_buffer_advance (
664                   p0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t)));
665               ip40 = vlib_buffer_get_current (p0);
666               udp_0 = (udp_header_t *) (ip40 + 1);
667             }
668           else
669             {
670               ip6_header_t *ip60;
671               vlib_buffer_advance (
672                   p0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t)));
673               ip60 = vlib_buffer_get_current (p0);
674               udp_0 = (udp_header_t *) (ip60 + 1);
675             }
676
677           entry0 = hash_get_mem(lbm->vip_index_by_nodeport, &(udp_0->dst_port));
678
679           //Enqueue to next
680           vnet_buffer(p0)->ip.adj_index[VLIB_TX] = entry0 ? entry0[0]
681               : ADJ_INDEX_INVALID;
682
683           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
684             {
685               lb_nodeport_trace_t *tr = vlib_add_trace (vm, node, p0,
686                                                         sizeof(*tr));
687               tr->vip_index = entry0 ? entry0[0] : ADJ_INDEX_INVALID;
688               tr->node_port = (u32) clib_net_to_host_u16 (udp_0->dst_port);
689             }
690
691           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
692               n_left_to_next, pi0,
693               is_input_v4 ?
694                   LB4_NODEPORT_NEXT_IP4_NAT4 : LB6_NODEPORT_NEXT_IP6_NAT6);
695         }
696       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
697     }
698
699   return frame->n_vectors;
700
701 }
702
703 /**
704  * @brief Match NAT44 static mapping.
705  *
706  * @param sm          NAT main.
707  * @param match       Address and port to match.
708  * @param index       index to the pool.
709  *
710  * @returns 0 if match found, otherwise -1.
711  */
712 int
713 lb_nat44_mapping_match (lb_main_t *lbm, lb_snat4_key_t * match, u32 *index)
714 {
715   clib_bihash_kv_8_8_t kv4, value;
716   clib_bihash_8_8_t *mapping_hash = &lbm->mapping_by_as4;
717
718   kv4.key = match->as_u64;
719   kv4.value = 0;
720   if (clib_bihash_search_8_8 (mapping_hash, &kv4, &value))
721     {
722       return 1;
723     }
724
725   *index = value.value;
726   return 0;
727 }
728
729 /**
730  * @brief Match NAT66 static mapping.
731  *
732  * @param sm          NAT main.
733  * @param match       Address and port to match.
734  * @param mapping     External or local address and port of the matched mapping.
735  *
736  * @returns 0 if match found otherwise 1.
737  */
738 int
739 lb_nat66_mapping_match (lb_main_t *lbm, lb_snat6_key_t * match, u32 *index)
740 {
741   clib_bihash_kv_24_8_t kv6, value;
742   lb_snat6_key_t m_key6;
743   clib_bihash_24_8_t *mapping_hash = &lbm->mapping_by_as6;
744
745   m_key6.addr.as_u64[0] = match->addr.as_u64[0];
746   m_key6.addr.as_u64[1] = match->addr.as_u64[1];
747   m_key6.port = match->port;
748   m_key6.protocol = 0;
749   m_key6.fib_index = 0;
750
751   kv6.key[0] = m_key6.as_u64[0];
752   kv6.key[1] = m_key6.as_u64[1];
753   kv6.key[2] = m_key6.as_u64[2];
754   kv6.value = 0;
755   if (clib_bihash_search_24_8 (mapping_hash, &kv6, &value))
756     {
757       return 1;
758     }
759
760   *index = value.value;
761   return 0;
762 }
763
764 static uword
765 lb_nat_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
766                        vlib_frame_t * frame, u32 is_nat4)
767 {
768   u32 n_left_from, *from, *to_next;
769   u32 next_index;
770   u32 pkts_processed = 0;
771   lb_main_t *lbm = &lb_main;
772   u32 stats_node_index;
773
774   stats_node_index =
775       is_nat4 ? lb_nat4_in2out_node.index : lb_nat6_in2out_node.index;
776
777   from = vlib_frame_vector_args (frame);
778   n_left_from = frame->n_vectors;
779   next_index = node->cached_next_index;
780
781   while (n_left_from > 0)
782     {
783       u32 n_left_to_next;
784
785       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
786
787       while (n_left_from > 0 && n_left_to_next > 0)
788         {
789           u32 bi0;
790           vlib_buffer_t * b0;
791           u32 next0;
792           u32 sw_if_index0;
793           ip_csum_t csum;
794           u16 old_port0, new_port0;
795           udp_header_t * udp0;
796           tcp_header_t * tcp0;
797
798           u32 proto0;
799           u32 rx_fib_index0;
800
801           /* speculatively enqueue b0 to the current next frame */
802           bi0 = from[0];
803           to_next[0] = bi0;
804           from += 1;
805           to_next += 1;
806           n_left_from -= 1;
807           n_left_to_next -= 1;
808
809           b0 = vlib_get_buffer (vm, bi0);
810           next0 = LB_NAT4_IN2OUT_NEXT_LOOKUP;
811           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
812           rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (
813               sw_if_index0);
814
815           if (is_nat4)
816             {
817               ip4_header_t * ip40;
818               u32 old_addr0, new_addr0;
819               lb_snat4_key_t key40;
820               lb_snat_mapping_t *sm40;
821               u32 index40;
822
823               ip40 = vlib_buffer_get_current (b0);
824               udp0 = ip4_next_header (ip40);
825               tcp0 = (tcp_header_t *) udp0;
826               proto0 = lb_ip_proto_to_nat_proto (ip40->protocol);
827
828               key40.addr = ip40->src_address;
829               key40.protocol = proto0;
830               key40.port = udp0->src_port;
831               key40.fib_index = rx_fib_index0;
832
833               if (lb_nat44_mapping_match (lbm, &key40, &index40))
834                 {
835                   next0 = LB_NAT4_IN2OUT_NEXT_DROP;
836                   goto trace0;
837                 }
838
839               sm40 = pool_elt_at_index(lbm->snat_mappings, index40);
840               new_addr0 = sm40->src_ip.ip4.as_u32;
841               new_port0 = sm40->src_port;
842               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm40->fib_index;
843               old_addr0 = ip40->src_address.as_u32;
844               ip40->src_address.as_u32 = new_addr0;
845
846               csum = ip40->checksum;
847               csum = ip_csum_sub_even (csum, old_addr0);
848               csum = ip_csum_add_even (csum, new_addr0);
849               ip40->checksum = ip_csum_fold (csum);
850
851               if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_TCP))
852                 {
853                   old_port0 = tcp0->src_port;
854                   tcp0->src_port = new_port0;
855
856                   csum = tcp0->checksum;
857                   csum = ip_csum_sub_even (csum, old_addr0);
858                   csum = ip_csum_sub_even (csum, old_port0);
859                   csum = ip_csum_add_even (csum, new_addr0);
860                   csum = ip_csum_add_even (csum, new_port0);
861                   tcp0->checksum = ip_csum_fold (csum);
862                 }
863               else if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_UDP))
864                 {
865                   old_port0 = udp0->src_port;
866                   udp0->src_port = new_port0;
867
868                   csum = udp0->checksum;
869                   csum = ip_csum_sub_even (csum, old_addr0);
870                   csum = ip_csum_sub_even (csum, old_port0);
871                   csum = ip_csum_add_even (csum, new_addr0);
872                   csum = ip_csum_add_even (csum, new_port0);
873                   udp0->checksum = ip_csum_fold (csum);
874                 }
875
876               pkts_processed += next0 != LB_NAT4_IN2OUT_NEXT_DROP;
877             }
878           else
879             {
880               ip6_header_t * ip60;
881               ip6_address_t old_addr0, new_addr0;
882               lb_snat6_key_t key60;
883               lb_snat_mapping_t *sm60;
884               u32 index60;
885
886               ip60 = vlib_buffer_get_current (b0);
887               udp0 = ip6_next_header (ip60);
888               tcp0 = (tcp_header_t *) udp0;
889               proto0 = lb_ip_proto_to_nat_proto (ip60->protocol);
890
891               key60.addr.as_u64[0] = ip60->src_address.as_u64[0];
892               key60.addr.as_u64[1] = ip60->src_address.as_u64[1];
893               key60.protocol = proto0;
894               key60.port = udp0->src_port;
895               key60.fib_index = rx_fib_index0;
896
897               if (lb_nat66_mapping_match (lbm, &key60, &index60))
898                 {
899                   next0 = LB_NAT6_IN2OUT_NEXT_DROP;
900                   goto trace0;
901                 }
902
903               sm60 = pool_elt_at_index(lbm->snat_mappings, index60);
904               new_addr0.as_u64[0] = sm60->src_ip.as_u64[0];
905               new_addr0.as_u64[1] = sm60->src_ip.as_u64[1];
906               new_port0 = sm60->src_port;
907               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm60->fib_index;
908               old_addr0.as_u64[0] = ip60->src_address.as_u64[0];
909               old_addr0.as_u64[1] = ip60->src_address.as_u64[1];
910               ip60->src_address.as_u64[0] = new_addr0.as_u64[0];
911               ip60->src_address.as_u64[1] = new_addr0.as_u64[1];
912
913               if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_TCP))
914                 {
915                   old_port0 = tcp0->src_port;
916                   tcp0->src_port = new_port0;
917
918                   csum = tcp0->checksum;
919                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[0]);
920                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[1]);
921                   csum = ip_csum_add_even (csum, new_addr0.as_u64[0]);
922                   csum = ip_csum_add_even (csum, new_addr0.as_u64[1]);
923                   csum = ip_csum_sub_even (csum, old_port0);
924                   csum = ip_csum_add_even (csum, new_port0);
925                   tcp0->checksum = ip_csum_fold (csum);
926                 }
927               else if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_UDP))
928                 {
929                   old_port0 = udp0->src_port;
930                   udp0->src_port = new_port0;
931
932                   csum = udp0->checksum;
933                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[0]);
934                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[1]);
935                   csum = ip_csum_add_even (csum, new_addr0.as_u64[0]);
936                   csum = ip_csum_add_even (csum, new_addr0.as_u64[1]);
937                   csum = ip_csum_sub_even (csum, old_port0);
938                   csum = ip_csum_add_even (csum, new_port0);
939                   udp0->checksum = ip_csum_fold (csum);
940                 }
941
942               pkts_processed += next0 != LB_NAT4_IN2OUT_NEXT_DROP;
943             }
944
945           trace0: if (PREDICT_FALSE(
946               (node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED)))
947             {
948               lb_nat_trace_t *t = vlib_add_trace (vm, node, b0, sizeof(*t));
949               t->rx_sw_if_index = sw_if_index0;
950               t->next_index = next0;
951             }
952
953           /* verify speculative enqueue, maybe switch current next frame */
954           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
955                                           n_left_to_next, bi0, next0);
956         }
957
958       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
959     }
960
961   vlib_node_increment_counter (vm, stats_node_index,
962                                LB_NAT_IN2OUT_ERROR_IN2OUT_PACKETS,
963                                pkts_processed);
964   return frame->n_vectors;
965 }
966
967 static uword
968 lb6_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
969                   vlib_frame_t * frame)
970 {
971   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 0);
972 }
973
974 static uword
975 lb6_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
976                   vlib_frame_t * frame)
977 {
978   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 0);
979 }
980
981 static uword
982 lb4_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
983                   vlib_frame_t * frame)
984 {
985   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 0);
986 }
987
988 static uword
989 lb4_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
990                   vlib_frame_t * frame)
991 {
992   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 0);
993 }
994
995 static uword
996 lb6_gre6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
997                        vlib_frame_t * frame)
998 {
999   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 1);
1000 }
1001
1002 static uword
1003 lb6_gre4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1004                        vlib_frame_t * frame)
1005 {
1006   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 1);
1007 }
1008
1009 static uword
1010 lb4_gre6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1011                        vlib_frame_t * frame)
1012 {
1013   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 1);
1014 }
1015
1016 static uword
1017 lb4_gre4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1018                        vlib_frame_t * frame)
1019 {
1020   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 1);
1021 }
1022
1023 static uword
1024 lb4_l3dsr_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1025                         vlib_frame_t * frame)
1026 {
1027   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 0);
1028 }
1029
1030 static uword
1031 lb4_l3dsr_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1032                         vlib_frame_t * frame)
1033 {
1034   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 1);
1035 }
1036
1037 static uword
1038 lb6_nat6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1039                        vlib_frame_t * frame)
1040 {
1041   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_NAT6, 1);
1042 }
1043
1044 static uword
1045 lb4_nat4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1046                        vlib_frame_t * frame)
1047 {
1048   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_NAT4, 1);
1049 }
1050
1051 static uword
1052 lb_nat4_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1053                         vlib_frame_t * frame)
1054 {
1055   return lb_nat_in2out_node_fn (vm, node, frame, 1);
1056 }
1057
1058 static uword
1059 lb_nat6_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1060                         vlib_frame_t * frame)
1061 {
1062   return lb_nat_in2out_node_fn (vm, node, frame, 0);
1063 }
1064
1065 VLIB_REGISTER_NODE (lb6_gre6_node) =
1066   {
1067     .function = lb6_gre6_node_fn,
1068     .name = "lb6-gre6",
1069     .vector_size = sizeof(u32),
1070     .format_trace = format_lb_trace,
1071     .n_errors = LB_N_ERROR,
1072     .error_strings = lb_error_strings,
1073     .n_next_nodes = LB_N_NEXT,
1074     .next_nodes =
1075         { [LB_NEXT_DROP] = "error-drop" },
1076   };
1077
1078 VLIB_REGISTER_NODE (lb6_gre4_node) =
1079   {
1080     .function = lb6_gre4_node_fn,
1081     .name = "lb6-gre4",
1082     .vector_size = sizeof(u32),
1083     .format_trace = format_lb_trace,
1084     .n_errors = LB_N_ERROR,
1085     .error_strings = lb_error_strings,
1086     .n_next_nodes = LB_N_NEXT,
1087     .next_nodes =
1088         { [LB_NEXT_DROP] = "error-drop" },
1089   };
1090
1091 VLIB_REGISTER_NODE (lb4_gre6_node) =
1092   {
1093     .function = lb4_gre6_node_fn,
1094     .name = "lb4-gre6",
1095     .vector_size = sizeof(u32),
1096     .format_trace = format_lb_trace,
1097     .n_errors = LB_N_ERROR,
1098     .error_strings = lb_error_strings,
1099     .n_next_nodes = LB_N_NEXT,
1100     .next_nodes =
1101         { [LB_NEXT_DROP] = "error-drop" },
1102   };
1103
1104 VLIB_REGISTER_NODE (lb4_gre4_node) =
1105   {
1106     .function = lb4_gre4_node_fn,
1107     .name = "lb4-gre4",
1108     .vector_size = sizeof(u32),
1109     .format_trace = format_lb_trace,
1110     .n_errors = LB_N_ERROR,
1111     .error_strings = lb_error_strings,
1112     .n_next_nodes = LB_N_NEXT,
1113     .next_nodes =
1114         { [LB_NEXT_DROP] = "error-drop" },
1115   };
1116
1117 VLIB_REGISTER_NODE (lb6_gre6_port_node) =
1118   {
1119     .function = lb6_gre6_port_node_fn,
1120     .name = "lb6-gre6-port",
1121     .vector_size = sizeof(u32),
1122     .format_trace = format_lb_trace,
1123     .n_errors = LB_N_ERROR,
1124     .error_strings = lb_error_strings,
1125     .n_next_nodes = LB_N_NEXT,
1126     .next_nodes =
1127         { [LB_NEXT_DROP] = "error-drop" },
1128   };
1129
1130 VLIB_REGISTER_NODE (lb6_gre4_port_node) =
1131   {
1132     .function = lb6_gre4_port_node_fn,
1133     .name = "lb6-gre4-port",
1134     .vector_size = sizeof(u32),
1135     .format_trace = format_lb_trace,
1136     .n_errors = LB_N_ERROR,
1137     .error_strings = lb_error_strings,
1138     .n_next_nodes = LB_N_NEXT,
1139     .next_nodes =
1140         { [LB_NEXT_DROP] = "error-drop" },
1141   };
1142
1143 VLIB_REGISTER_NODE (lb4_gre6_port_node) =
1144   {
1145     .function = lb4_gre6_port_node_fn,
1146     .name = "lb4-gre6-port",
1147     .vector_size = sizeof(u32),
1148     .format_trace = format_lb_trace,
1149     .n_errors = LB_N_ERROR,
1150     .error_strings = lb_error_strings,
1151     .n_next_nodes = LB_N_NEXT,
1152     .next_nodes =
1153         { [LB_NEXT_DROP] = "error-drop" },
1154   };
1155
1156 VLIB_REGISTER_NODE (lb4_gre4_port_node) =
1157   {
1158     .function = lb4_gre4_port_node_fn,
1159     .name = "lb4-gre4-port",
1160     .vector_size = sizeof(u32),
1161     .format_trace = format_lb_trace,
1162     .n_errors = LB_N_ERROR,
1163     .error_strings = lb_error_strings,
1164     .n_next_nodes = LB_N_NEXT,
1165     .next_nodes =
1166         { [LB_NEXT_DROP] = "error-drop" },
1167   };
1168
1169 VLIB_REGISTER_NODE (lb4_l3dsr_port_node) =
1170   {
1171     .function = lb4_l3dsr_port_node_fn,
1172     .name = "lb4-l3dsr-port",
1173     .vector_size = sizeof(u32),
1174     .format_trace = format_lb_trace,
1175     .n_errors = LB_N_ERROR,
1176     .error_strings = lb_error_strings,
1177     .n_next_nodes = LB_N_NEXT,
1178     .next_nodes =
1179         { [LB_NEXT_DROP] = "error-drop" },
1180   };
1181
1182 VLIB_REGISTER_NODE (lb4_l3dsr_node) =
1183   {
1184     .function = lb4_l3dsr_node_fn,
1185     .name = "lb4-l3dsr",
1186     .vector_size = sizeof(u32),
1187     .format_trace = format_lb_trace,
1188     .n_errors = LB_N_ERROR,
1189     .error_strings = lb_error_strings,
1190     .n_next_nodes = LB_N_NEXT,
1191     .next_nodes =
1192         { [LB_NEXT_DROP] = "error-drop" },
1193   };
1194
1195 VLIB_REGISTER_NODE (lb6_nat6_port_node) =
1196   {
1197     .function = lb6_nat6_port_node_fn,
1198     .name = "lb6-nat6-port",
1199     .vector_size = sizeof(u32),
1200     .format_trace = format_lb_trace,
1201     .n_errors = LB_N_ERROR,
1202     .error_strings = lb_error_strings,
1203     .n_next_nodes = LB_N_NEXT,
1204     .next_nodes =
1205         { [LB_NEXT_DROP] = "error-drop" },
1206   };
1207
1208 VLIB_REGISTER_NODE (lb4_nat4_port_node) =
1209   {
1210     .function = lb4_nat4_port_node_fn,
1211     .name = "lb4-nat4-port",
1212     .vector_size = sizeof(u32),
1213     .format_trace = format_lb_trace,
1214     .n_errors = LB_N_ERROR,
1215     .error_strings = lb_error_strings,
1216     .n_next_nodes = LB_N_NEXT,
1217     .next_nodes =
1218         { [LB_NEXT_DROP] = "error-drop" },
1219   };
1220
1221 static uword
1222 lb4_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1223                       vlib_frame_t * frame)
1224 {
1225   return lb_nodeport_node_fn (vm, node, frame, 1);
1226 }
1227
1228 static uword
1229 lb6_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1230                       vlib_frame_t * frame)
1231 {
1232   return lb_nodeport_node_fn (vm, node, frame, 0);
1233 }
1234
1235 VLIB_REGISTER_NODE (lb4_nodeport_node) =
1236   {
1237     .function = lb4_nodeport_node_fn,
1238     .name = "lb4-nodeport",
1239     .vector_size = sizeof(u32),
1240     .format_trace = format_nodeport_lb_trace,
1241     .n_errors = LB_N_ERROR,
1242     .error_strings = lb_error_strings,
1243     .n_next_nodes = LB4_NODEPORT_N_NEXT,
1244     .next_nodes =
1245         {
1246             [LB4_NODEPORT_NEXT_IP4_NAT4] = "lb4-nat4-port",
1247             [LB4_NODEPORT_NEXT_DROP] = "error-drop",
1248         },
1249   };
1250
1251 VLIB_REGISTER_NODE (lb6_nodeport_node) =
1252   {
1253     .function = lb6_nodeport_node_fn,
1254     .name = "lb6-nodeport",
1255     .vector_size = sizeof(u32),
1256     .format_trace = format_nodeport_lb_trace,
1257     .n_errors = LB_N_ERROR,
1258     .error_strings = lb_error_strings,
1259     .n_next_nodes = LB6_NODEPORT_N_NEXT,
1260     .next_nodes =
1261       {
1262           [LB6_NODEPORT_NEXT_IP6_NAT6] = "lb6-nat6-port",
1263           [LB6_NODEPORT_NEXT_DROP] = "error-drop",
1264       },
1265   };
1266
1267 VNET_FEATURE_INIT (lb_nat4_in2out_node_fn, static) =
1268   {
1269     .arc_name = "ip4-unicast",
1270     .node_name = "lb-nat4-in2out",
1271     .runs_before =  VNET_FEATURES("ip4-lookup"),
1272   };
1273
1274 VLIB_REGISTER_NODE (lb_nat4_in2out_node) =
1275   {
1276     .function = lb_nat4_in2out_node_fn,
1277     .name = "lb-nat4-in2out",
1278     .vector_size = sizeof(u32),
1279     .format_trace = format_lb_nat_trace,
1280     .n_errors = LB_N_ERROR,
1281     .error_strings = lb_error_strings,
1282     .n_next_nodes = LB_NAT4_IN2OUT_N_NEXT,
1283     .next_nodes =
1284       {
1285           [LB_NAT4_IN2OUT_NEXT_DROP] = "error-drop",
1286           [LB_NAT4_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1287       },
1288   };
1289
1290 VNET_FEATURE_INIT (lb_nat6_in2out_node_fn, static) =
1291   {
1292     .arc_name = "ip6-unicast",
1293     .node_name = "lb-nat6-in2out",
1294     .runs_before = VNET_FEATURES("ip6-lookup"),
1295   };
1296
1297 VLIB_REGISTER_NODE (lb_nat6_in2out_node) =
1298   {
1299     .function = lb_nat6_in2out_node_fn,
1300     .name = "lb-nat6-in2out",
1301     .vector_size = sizeof(u32),
1302     .format_trace = format_lb_nat_trace,
1303     .n_errors = LB_N_ERROR,
1304     .error_strings = lb_error_strings,
1305     .n_next_nodes = LB_NAT6_IN2OUT_N_NEXT,
1306     .next_nodes =
1307       {
1308           [LB_NAT6_IN2OUT_NEXT_DROP] = "error-drop",
1309           [LB_NAT6_IN2OUT_NEXT_LOOKUP] = "ip6-lookup",
1310       },
1311   };