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