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