Rework kube-proxy into LB plugin
[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 = vlib_get_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;
391               u32 old_dscp;
392
393               ip40 = vlib_buffer_get_current (p0);
394               old_dst = ip40->dst_address.as_u32;
395               old_dscp = ip40->tos;
396               ip40->dst_address = lbm->ass[asindex0].address.ip4;
397               /* Get and rewrite DSCP bit */
398               ip40->tos = (u8) ((vip0->encap_args.dscp & 0x3F) << 2);
399
400               csum = ip40->checksum;
401               csum = ip_csum_sub_even (csum, old_dst);
402               csum = ip_csum_sub_even (csum, old_dscp);
403               csum = ip_csum_add_even (csum,
404                                        lbm->ass[asindex0].address.ip4.as_u32);
405               csum = ip_csum_add_even (csum, ip40->tos);
406               ip40->checksum = ip_csum_fold (csum);
407
408               /* Recomputing L4 checksum after dst-IP modifying */
409               th0 = ip4_next_header (ip40);
410               th0->checksum = 0;
411               th0->checksum = ip4_tcp_udp_compute_checksum (vm, p0, ip40);
412             }
413           else if ((encap_type == LB_ENCAP_TYPE_NAT4)
414               || (encap_type == LB_ENCAP_TYPE_NAT6))
415             {
416               ip_csum_t csum;
417               udp_header_t *uh;
418
419               /* do NAT */
420               if ((is_input_v4 == 1) && (encap_type == LB_ENCAP_TYPE_NAT4))
421                 {
422                   /* NAT44 */
423                   ip4_header_t *ip40;
424                   u32 old_dst;
425                   ip40 = vlib_buffer_get_current (p0);
426                   uh = (udp_header_t *) (ip40 + 1);
427                   old_dst = ip40->dst_address.as_u32;
428                   ip40->dst_address = lbm->ass[asindex0].address.ip4;
429
430                   csum = ip40->checksum;
431                   csum = ip_csum_sub_even (csum, old_dst);
432                   csum = ip_csum_add_even (
433                       csum, lbm->ass[asindex0].address.ip4.as_u32);
434                   ip40->checksum = ip_csum_fold (csum);
435
436                   if ((ip40->protocol == IP_PROTOCOL_UDP)
437                       || (uh->dst_port == vip0->encap_args.port))
438                     {
439                       uh->dst_port = vip0->encap_args.target_port;
440                       csum = uh->checksum;
441                       csum = ip_csum_sub_even (csum, old_dst);
442                       csum = ip_csum_add_even (
443                           csum, lbm->ass[asindex0].address.ip4.as_u32);
444                       uh->checksum = ip_csum_fold (csum);
445                     }
446                   else
447                     {
448                       next_index = LB_NEXT_DROP;
449                     }
450                 }
451               else if ((is_input_v4 == 0) && (encap_type == LB_ENCAP_TYPE_NAT6))
452                 {
453                   /* NAT66 */
454                   ip6_header_t *ip60;
455                   ip6_address_t old_dst;
456
457                   ip60 = vlib_buffer_get_current (p0);
458                   uh = (udp_header_t *) (ip60 + 1);
459
460                   old_dst.as_u64[0] = ip60->dst_address.as_u64[0];
461                   old_dst.as_u64[1] = ip60->dst_address.as_u64[1];
462                   ip60->dst_address.as_u64[0] =
463                       lbm->ass[asindex0].address.ip6.as_u64[0];
464                   ip60->dst_address.as_u64[1] =
465                       lbm->ass[asindex0].address.ip6.as_u64[1];
466
467                   if (PREDICT_TRUE(ip60->protocol == IP_PROTOCOL_UDP))
468                     {
469                       uh->dst_port = vip0->encap_args.target_port;
470                       csum = uh->checksum;
471                       csum = ip_csum_sub_even (csum, old_dst.as_u64[0]);
472                       csum = ip_csum_sub_even (csum, old_dst.as_u64[1]);
473                       csum = ip_csum_add_even (
474                           csum, lbm->ass[asindex0].address.ip6.as_u64[0]);
475                       csum = ip_csum_add_even (
476                           csum, lbm->ass[asindex0].address.ip6.as_u64[1]);
477                       uh->checksum = ip_csum_fold (csum);
478                     }
479                   else
480                     {
481                       next_index = LB_NEXT_DROP;
482                     }
483                 }
484             }
485
486           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
487             {
488               lb_trace_t *tr = vlib_add_trace (vm, node, p0, sizeof(*tr));
489               tr->as_index = asindex0;
490               tr->vip_index = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
491             }
492
493           //Enqueue to next
494           //Note that this is going to error if asindex0 == 0
495           vnet_buffer (p0)->ip.adj_index[VLIB_TX] =
496               lbm->ass[asindex0].dpo.dpoi_index;
497           vlib_validate_buffer_enqueue_x1(
498               vm, node, next_index, to_next, n_left_to_next, pi0,
499               lbm->ass[asindex0].dpo.dpoi_next_node);
500         }
501       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
502     }
503
504   return frame->n_vectors;
505 }
506
507 u8 *
508 format_nodeport_lb_trace (u8 * s, va_list * args)
509 {
510   lb_main_t *lbm = &lb_main;
511   CLIB_UNUSED(vlib_main_t * vm)
512 = va_arg (*args, vlib_main_t *);
513     CLIB_UNUSED(vlib_node_t * node)
514   = va_arg (*args, vlib_node_t *);
515   lb_nodeport_trace_t *t = va_arg (*args, lb_nodeport_trace_t *);
516   if (pool_is_free_index(lbm->vips, t->vip_index))
517     {
518       s = format (s, "lb vip[%d]: This VIP was freed since capture\n");
519     }
520   else
521     {
522       s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip,
523                   &lbm->vips[t->vip_index]);
524     }
525
526   s = format (s, "  lb node_port: %d", t->node_port);
527
528   return s;
529 }
530
531 static uword
532 lb_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
533                      vlib_frame_t * frame, u8 is_input_v4)
534 {
535   lb_main_t *lbm = &lb_main;
536   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
537
538   from = vlib_frame_vector_args (frame);
539   n_left_from = frame->n_vectors;
540   next_index = node->cached_next_index;
541
542   while (n_left_from > 0)
543     {
544       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
545
546       while (n_left_from > 0 && n_left_to_next > 0)
547         {
548           u32 pi0;
549           vlib_buffer_t *p0;
550           udp_header_t * udp_0;
551           uword * entry0;
552
553           if (PREDICT_TRUE(n_left_from > 1))
554             {
555               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[1]);
556               //Prefetch for encap, next
557               CLIB_PREFETCH(vlib_buffer_get_current (p1) - 64, 64, STORE);
558             }
559
560           if (PREDICT_TRUE(n_left_from > 2))
561             {
562               vlib_buffer_t *p2;
563               p2 = vlib_get_buffer (vm, from[2]);
564               /* prefetch packet header and data */
565               vlib_prefetch_buffer_header(p2, STORE);
566               CLIB_PREFETCH(vlib_buffer_get_current (p2), 64, STORE);
567             }
568
569           pi0 = to_next[0] = from[0];
570           from += 1;
571           n_left_from -= 1;
572           to_next += 1;
573           n_left_to_next -= 1;
574
575           p0 = vlib_get_buffer (vm, pi0);
576
577           if (is_input_v4)
578             {
579               ip4_header_t *ip40;
580               vlib_buffer_advance (
581                   p0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t)));
582               ip40 = vlib_buffer_get_current (p0);
583               udp_0 = (udp_header_t *) (ip40 + 1);
584             }
585           else
586             {
587               ip6_header_t *ip60;
588               vlib_buffer_advance (
589                   p0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t)));
590               ip60 = vlib_buffer_get_current (p0);
591               udp_0 = (udp_header_t *) (ip60 + 1);
592             }
593
594           entry0 = hash_get_mem(lbm->vip_index_by_nodeport, &(udp_0->dst_port));
595
596           //Enqueue to next
597           vnet_buffer(p0)->ip.adj_index[VLIB_TX] = entry0[0];
598
599           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
600             {
601               lb_nodeport_trace_t *tr = vlib_add_trace (vm, node, p0,
602                                                         sizeof(*tr));
603               tr->vip_index = entry0[0];
604               tr->node_port = (u32) clib_net_to_host_u16 (udp_0->dst_port);
605             }
606
607           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
608               n_left_to_next, pi0,
609               is_input_v4 ?
610                   LB4_NODEPORT_NEXT_IP4_NAT4 : LB6_NODEPORT_NEXT_IP6_NAT6);
611         }
612       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
613     }
614
615   return frame->n_vectors;
616
617 }
618
619 /**
620  * @brief Match NAT44 static mapping.
621  *
622  * @param sm          NAT main.
623  * @param match       Address and port to match.
624  * @param index       index to the pool.
625  *
626  * @returns 0 if match found, otherwise -1.
627  */
628 int
629 lb_nat44_mapping_match (lb_main_t *lbm, lb_snat4_key_t * match, u32 *index)
630 {
631   clib_bihash_kv_8_8_t kv4, value;
632   clib_bihash_8_8_t *mapping_hash = &lbm->mapping_by_as4;
633
634   kv4.key = match->as_u64;
635   kv4.value = 0;
636   if (clib_bihash_search_8_8 (mapping_hash, &kv4, &value))
637     {
638       return 1;
639     }
640
641   *index = value.value;
642   return 0;
643 }
644
645 /**
646  * @brief Match NAT66 static mapping.
647  *
648  * @param sm          NAT main.
649  * @param match       Address and port to match.
650  * @param mapping     External or local address and port of the matched mapping.
651  *
652  * @returns 0 if match found otherwise 1.
653  */
654 int
655 lb_nat66_mapping_match (lb_main_t *lbm, lb_snat6_key_t * match, u32 *index)
656 {
657   clib_bihash_kv_24_8_t kv6, value;
658   lb_snat6_key_t m_key6;
659   clib_bihash_24_8_t *mapping_hash = &lbm->mapping_by_as6;
660
661   m_key6.addr.as_u64[0] = match->addr.as_u64[0];
662   m_key6.addr.as_u64[1] = match->addr.as_u64[1];
663   m_key6.port = match->port;
664   m_key6.protocol = 0;
665   m_key6.fib_index = 0;
666
667   kv6.key[0] = m_key6.as_u64[0];
668   kv6.key[1] = m_key6.as_u64[1];
669   kv6.key[2] = m_key6.as_u64[2];
670   kv6.value = 0;
671   if (clib_bihash_search_24_8 (mapping_hash, &kv6, &value))
672     {
673       return 1;
674     }
675
676   *index = value.value;
677   return 0;
678 }
679
680 static uword
681 lb_nat_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
682                        vlib_frame_t * frame, u32 is_nat4)
683 {
684   u32 n_left_from, *from, *to_next;
685   u32 next_index;
686   u32 pkts_processed = 0;
687   lb_main_t *lbm = &lb_main;
688   u32 stats_node_index;
689
690   stats_node_index =
691       is_nat4 ? lb_nat4_in2out_node.index : lb_nat6_in2out_node.index;
692
693   from = vlib_frame_vector_args (frame);
694   n_left_from = frame->n_vectors;
695   next_index = node->cached_next_index;
696
697   while (n_left_from > 0)
698     {
699       u32 n_left_to_next;
700
701       vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
702
703       while (n_left_from > 0 && n_left_to_next > 0)
704         {
705           u32 bi0;
706           vlib_buffer_t * b0;
707           u32 next0;
708           u32 sw_if_index0;
709           ip_csum_t csum;
710           u16 old_port0, new_port0;
711           udp_header_t * udp0;
712           tcp_header_t * tcp0;
713
714           u32 proto0;
715           u32 rx_fib_index0;
716
717           /* speculatively enqueue b0 to the current next frame */
718           bi0 = from[0];
719           to_next[0] = bi0;
720           from += 1;
721           to_next += 1;
722           n_left_from -= 1;
723           n_left_to_next -= 1;
724
725           b0 = vlib_get_buffer (vm, bi0);
726           next0 = LB_NAT4_IN2OUT_NEXT_LOOKUP;
727           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
728           rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (
729               sw_if_index0);
730
731           if (is_nat4)
732             {
733               ip4_header_t * ip40;
734               u32 old_addr0, new_addr0;
735               lb_snat4_key_t key40;
736               lb_snat_mapping_t *sm40;
737               u32 index40;
738
739               ip40 = vlib_buffer_get_current (b0);
740               udp0 = ip4_next_header (ip40);
741               tcp0 = (tcp_header_t *) udp0;
742               proto0 = lb_ip_proto_to_nat_proto (ip40->protocol);
743
744               key40.addr = ip40->src_address;
745               key40.protocol = proto0;
746               key40.port = udp0->src_port;
747               key40.fib_index = rx_fib_index0;
748
749               if (lb_nat44_mapping_match (lbm, &key40, &index40))
750                 {
751                   next0 = LB_NAT4_IN2OUT_NEXT_DROP;
752                   goto trace0;
753                 }
754
755               sm40 = pool_elt_at_index(lbm->snat_mappings, index40);
756               new_addr0 = sm40->src_ip.ip4.as_u32;
757               new_port0 = sm40->src_port;
758               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm40->fib_index;
759               old_addr0 = ip40->src_address.as_u32;
760               ip40->src_address.as_u32 = new_addr0;
761
762               csum = ip40->checksum;
763               csum = ip_csum_sub_even (csum, old_addr0);
764               csum = ip_csum_add_even (csum, new_addr0);
765               ip40->checksum = ip_csum_fold (csum);
766
767               if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_TCP))
768                 {
769                   old_port0 = tcp0->src_port;
770                   tcp0->src_port = new_port0;
771
772                   csum = tcp0->checksum;
773                   csum = ip_csum_sub_even (csum, old_addr0);
774                   csum = ip_csum_sub_even (csum, old_port0);
775                   csum = ip_csum_add_even (csum, new_addr0);
776                   csum = ip_csum_add_even (csum, new_port0);
777                   tcp0->checksum = ip_csum_fold (csum);
778                 }
779               else if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_UDP))
780                 {
781                   old_port0 = udp0->src_port;
782                   udp0->src_port = new_port0;
783
784                   csum = udp0->checksum;
785                   csum = ip_csum_sub_even (csum, old_addr0);
786                   csum = ip_csum_sub_even (csum, old_port0);
787                   csum = ip_csum_add_even (csum, new_addr0);
788                   csum = ip_csum_add_even (csum, new_port0);
789                   udp0->checksum = ip_csum_fold (csum);
790                 }
791
792               pkts_processed += next0 != LB_NAT4_IN2OUT_NEXT_DROP;
793             }
794           else
795             {
796               ip6_header_t * ip60;
797               ip6_address_t old_addr0, new_addr0;
798               lb_snat6_key_t key60;
799               lb_snat_mapping_t *sm60;
800               u32 index60;
801
802               ip60 = vlib_buffer_get_current (b0);
803               udp0 = ip6_next_header (ip60);
804               tcp0 = (tcp_header_t *) udp0;
805               proto0 = lb_ip_proto_to_nat_proto (ip60->protocol);
806
807               key60.addr.as_u64[0] = ip60->src_address.as_u64[0];
808               key60.addr.as_u64[1] = ip60->src_address.as_u64[1];
809               key60.protocol = proto0;
810               key60.port = udp0->src_port;
811               key60.fib_index = rx_fib_index0;
812
813               if (lb_nat66_mapping_match (lbm, &key60, &index60))
814                 {
815                   next0 = LB_NAT6_IN2OUT_NEXT_DROP;
816                   goto trace0;
817                 }
818
819               sm60 = pool_elt_at_index(lbm->snat_mappings, index60);
820               new_addr0.as_u64[0] = sm60->src_ip.as_u64[0];
821               new_addr0.as_u64[1] = sm60->src_ip.as_u64[1];
822               new_port0 = sm60->src_port;
823               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm60->fib_index;
824               old_addr0.as_u64[0] = ip60->src_address.as_u64[0];
825               old_addr0.as_u64[1] = ip60->src_address.as_u64[1];
826               ip60->src_address.as_u64[0] = new_addr0.as_u64[0];
827               ip60->src_address.as_u64[1] = new_addr0.as_u64[1];
828
829               if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_TCP))
830                 {
831                   old_port0 = tcp0->src_port;
832                   tcp0->src_port = new_port0;
833
834                   csum = tcp0->checksum;
835                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[0]);
836                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[1]);
837                   csum = ip_csum_add_even (csum, new_addr0.as_u64[0]);
838                   csum = ip_csum_add_even (csum, new_addr0.as_u64[1]);
839                   csum = ip_csum_sub_even (csum, old_port0);
840                   csum = ip_csum_add_even (csum, new_port0);
841                   tcp0->checksum = ip_csum_fold (csum);
842                 }
843               else if (PREDICT_TRUE(proto0 == LB_NAT_PROTOCOL_UDP))
844                 {
845                   old_port0 = udp0->src_port;
846                   udp0->src_port = new_port0;
847
848                   csum = udp0->checksum;
849                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[0]);
850                   csum = ip_csum_sub_even (csum, old_addr0.as_u64[1]);
851                   csum = ip_csum_add_even (csum, new_addr0.as_u64[0]);
852                   csum = ip_csum_add_even (csum, new_addr0.as_u64[1]);
853                   csum = ip_csum_sub_even (csum, old_port0);
854                   csum = ip_csum_add_even (csum, new_port0);
855                   udp0->checksum = ip_csum_fold (csum);
856                 }
857
858               pkts_processed += next0 != LB_NAT4_IN2OUT_NEXT_DROP;
859             }
860
861           trace0: if (PREDICT_FALSE(
862               (node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED)))
863             {
864               lb_nat_trace_t *t = vlib_add_trace (vm, node, b0, sizeof(*t));
865               t->rx_sw_if_index = sw_if_index0;
866               t->next_index = next0;
867             }
868
869           /* verify speculative enqueue, maybe switch current next frame */
870           vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
871                                           n_left_to_next, bi0, next0);
872         }
873
874       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
875     }
876
877   vlib_node_increment_counter (vm, stats_node_index,
878                                LB_NAT_IN2OUT_ERROR_IN2OUT_PACKETS,
879                                pkts_processed);
880   return frame->n_vectors;
881 }
882
883 static uword
884 lb6_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
885                   vlib_frame_t * frame)
886 {
887   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6);
888 }
889
890 static uword
891 lb6_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
892                   vlib_frame_t * frame)
893 {
894   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4);
895 }
896
897 static uword
898 lb4_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
899                   vlib_frame_t * frame)
900 {
901   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6);
902 }
903
904 static uword
905 lb4_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
906                   vlib_frame_t * frame)
907 {
908   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4);
909 }
910
911 static uword
912 lb4_l3dsr_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
913                    vlib_frame_t * frame)
914 {
915   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR);
916 }
917
918 static uword
919 lb6_nat6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
920                   vlib_frame_t * frame)
921 {
922   return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_NAT6);
923 }
924
925 static uword
926 lb4_nat4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
927                   vlib_frame_t * frame)
928 {
929   return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_NAT4);
930 }
931
932 static uword
933 lb_nat4_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
934                         vlib_frame_t * frame)
935 {
936   return lb_nat_in2out_node_fn (vm, node, frame, 1);
937 }
938
939 static uword
940 lb_nat6_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
941                         vlib_frame_t * frame)
942 {
943   return lb_nat_in2out_node_fn (vm, node, frame, 0);
944 }
945
946 VLIB_REGISTER_NODE (lb6_gre6_node) =
947   {
948     .function = lb6_gre6_node_fn,
949     .name = "lb6-gre6",
950     .vector_size = sizeof(u32),
951     .format_trace = format_lb_trace,
952     .n_errors = LB_N_ERROR, .error_strings = lb_error_strings,
953     .n_next_nodes = LB_N_NEXT,
954     .next_nodes =
955         { [LB_NEXT_DROP] = "error-drop" },
956   };
957
958 VLIB_REGISTER_NODE (lb6_gre4_node) =
959   {
960     .function = lb6_gre4_node_fn,
961     .name = "lb6-gre4",
962     .vector_size = sizeof(u32),
963     .format_trace = format_lb_trace,
964     .n_errors = LB_N_ERROR,
965     .error_strings = lb_error_strings,
966     .n_next_nodes = LB_N_NEXT,
967     .next_nodes =
968         { [LB_NEXT_DROP] = "error-drop" },
969   };
970
971 VLIB_REGISTER_NODE (lb4_gre6_node) =
972   {
973     .function = lb4_gre6_node_fn,
974     .name = "lb4-gre6",
975     .vector_size = sizeof(u32),
976     .format_trace = format_lb_trace,
977     .n_errors = LB_N_ERROR,
978     .error_strings = lb_error_strings,
979     .n_next_nodes = LB_N_NEXT,
980     .next_nodes =
981         { [LB_NEXT_DROP] = "error-drop" }, 
982   };
983
984 VLIB_REGISTER_NODE (lb4_gre4_node) =
985   {
986     .function = lb4_gre4_node_fn,
987     .name = "lb4-gre4",
988     .vector_size = sizeof(u32),
989     .format_trace = format_lb_trace,
990     .n_errors = LB_N_ERROR,
991     .error_strings = lb_error_strings,
992     .n_next_nodes = LB_N_NEXT, 
993     .next_nodes =
994         { [LB_NEXT_DROP] = "error-drop" },
995   };
996
997 VLIB_REGISTER_NODE (lb4_l3dsr_node) =
998   {
999     .function = lb4_l3dsr_node_fn,
1000     .name = "lb4-l3dsr",
1001     .vector_size = sizeof(u32),
1002     .format_trace = format_lb_trace,
1003     .n_errors = LB_N_ERROR,
1004     .error_strings = lb_error_strings,
1005     .n_next_nodes = LB_N_NEXT,
1006     .next_nodes =
1007         { [LB_NEXT_DROP] = "error-drop" },
1008   };
1009
1010 VLIB_REGISTER_NODE (lb6_nat6_node) =
1011   {
1012     .function = lb6_nat6_node_fn,
1013     .name = "lb6-nat6",
1014     .vector_size = sizeof(u32),
1015     .format_trace = format_lb_trace,
1016     .n_errors = LB_N_ERROR,
1017     .error_strings = lb_error_strings,
1018     .n_next_nodes = LB_N_NEXT,
1019     .next_nodes =
1020         { [LB_NEXT_DROP] = "error-drop" },
1021   };
1022
1023 VLIB_REGISTER_NODE (lb4_nat4_node) =
1024   {
1025     .function = lb4_nat4_node_fn,
1026     .name = "lb4-nat4",
1027     .vector_size = sizeof(u32),
1028     .format_trace = format_lb_trace,
1029     .n_errors = LB_N_ERROR,
1030     .error_strings = lb_error_strings,
1031     .n_next_nodes = LB_N_NEXT,
1032     .next_nodes =
1033         { [LB_NEXT_DROP] = "error-drop" },
1034   };
1035
1036 static uword
1037 lb4_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1038                       vlib_frame_t * frame)
1039 {
1040   return lb_nodeport_node_fn (vm, node, frame, 1);
1041 }
1042
1043 static uword
1044 lb6_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1045                       vlib_frame_t * frame)
1046 {
1047   return lb_nodeport_node_fn (vm, node, frame, 0);
1048 }
1049
1050 VLIB_REGISTER_NODE (lb4_nodeport_node) =
1051   {
1052     .function = lb4_nodeport_node_fn,
1053     .name = "lb4-nodeport",
1054     .vector_size = sizeof(u32),
1055     .format_trace = format_nodeport_lb_trace,
1056     .n_errors = LB_N_ERROR,
1057     .error_strings = lb_error_strings,
1058     .n_next_nodes = LB4_NODEPORT_N_NEXT,
1059     .next_nodes =
1060         {
1061             [LB4_NODEPORT_NEXT_IP4_NAT4] = "lb4-nat4",
1062             [LB4_NODEPORT_NEXT_DROP] = "error-drop",
1063         },
1064   };
1065
1066 VLIB_REGISTER_NODE (lb6_nodeport_node) =
1067   {
1068     .function = lb6_nodeport_node_fn,
1069     .name = "lb6-nodeport",
1070     .vector_size = sizeof(u32),
1071     .format_trace = format_nodeport_lb_trace,
1072     .n_errors = LB_N_ERROR,
1073     .error_strings = lb_error_strings,
1074     .n_next_nodes = LB6_NODEPORT_N_NEXT,
1075     .next_nodes =
1076       {
1077           [LB6_NODEPORT_NEXT_IP6_NAT6] = "lb6-nat6",
1078           [LB6_NODEPORT_NEXT_DROP] = "error-drop",
1079       },
1080   };
1081
1082 VNET_FEATURE_INIT (lb_nat4_in2out_node_fn, static) =
1083   {
1084     .arc_name = "ip4-unicast",
1085     .node_name = "lb-nat4-in2out",
1086     .runs_before =  VNET_FEATURES("ip4-lookup"),
1087   };
1088
1089 VLIB_REGISTER_NODE (lb_nat4_in2out_node) =
1090   {
1091     .function = lb_nat4_in2out_node_fn,
1092     .name = "lb-nat4-in2out",
1093     .vector_size = sizeof(u32),
1094     .format_trace = format_lb_nat_trace,
1095     .n_errors = LB_N_ERROR,
1096     .error_strings = lb_error_strings,
1097     .n_next_nodes = LB_NAT4_IN2OUT_N_NEXT,
1098     .next_nodes =
1099       {
1100           [LB_NAT4_IN2OUT_NEXT_DROP] = "error-drop",
1101           [LB_NAT4_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1102       },
1103   };
1104
1105 VNET_FEATURE_INIT (lb_nat6_in2out_node_fn, static) =
1106   {
1107     .arc_name = "ip6-unicast",
1108     .node_name = "lb-nat6-in2out",
1109     .runs_before = VNET_FEATURES("ip6-lookup"),
1110   };
1111
1112 VLIB_REGISTER_NODE (lb_nat6_in2out_node) =
1113   {
1114     .function = lb_nat6_in2out_node_fn,
1115     .name = "lb-nat6-in2out",
1116     .vector_size = sizeof(u32),
1117     .format_trace = format_lb_nat_trace,
1118     .n_errors = LB_N_ERROR,
1119     .error_strings = lb_error_strings,
1120     .n_next_nodes = LB_NAT6_IN2OUT_N_NEXT,
1121     .next_nodes =
1122       {
1123           [LB_NAT6_IN2OUT_NEXT_DROP] = "error-drop",
1124           [LB_NAT6_IN2OUT_NEXT_LOOKUP] = "ip6-lookup",
1125       },
1126   };
1127