cnat: Destination based NAT
[vpp.git] / src / plugins / cnat / cnat_node.h
1 /*
2  * Copyright (c) 2020 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 #ifndef __CNAT_NODE_H__
17 #define __CNAT_NODE_H__
18
19 #include <vlibmemory/api.h>
20 #include <cnat/cnat_session.h>
21 #include <cnat/cnat_client.h>
22
23 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
24                                   vlib_node_runtime_t * node,
25                                   vlib_buffer_t * b,
26                                   cnat_node_ctx_t * ctx, int rv,
27                                   cnat_session_t * session);
28
29 /**
30  * Inline translation functions
31  */
32
33 static_always_inline u8
34 has_ip6_address (ip6_address_t * a)
35 {
36   return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
37 }
38
39 static_always_inline void
40 cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
41                        u16 * checksum,
42                        ip4_address_t new_addr[VLIB_N_DIR],
43                        u16 new_port[VLIB_N_DIR])
44 {
45   u16 old_port[VLIB_N_DIR];
46   ip4_address_t old_addr[VLIB_N_DIR];
47   ip_csum_t sum;
48
49   old_port[VLIB_TX] = udp->dst_port;
50   old_port[VLIB_RX] = udp->src_port;
51   old_addr[VLIB_TX] = ip4->dst_address;
52   old_addr[VLIB_RX] = ip4->src_address;
53
54   sum = *checksum;
55   if (new_addr[VLIB_TX].as_u32)
56     sum =
57       ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
58                       ip4_header_t, dst_address);
59   if (new_port[VLIB_TX])
60     {
61       udp->dst_port = new_port[VLIB_TX];
62       sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
63                             ip4_header_t /* cheat */ ,
64                             length /* changed member */ );
65     }
66   if (new_addr[VLIB_RX].as_u32)
67     sum =
68       ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
69                       ip4_header_t, src_address);
70
71   if (new_port[VLIB_RX])
72     {
73       udp->src_port = new_port[VLIB_RX];
74       sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
75                             ip4_header_t /* cheat */ ,
76                             length /* changed member */ );
77     }
78   *checksum = ip_csum_fold (sum);
79 }
80
81 static_always_inline void
82 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
83 {
84   ip4_address_t old_addr[VLIB_N_DIR];
85   ip_csum_t sum;
86
87   old_addr[VLIB_TX] = ip4->dst_address;
88   old_addr[VLIB_RX] = ip4->src_address;
89
90   sum = ip4->checksum;
91   if (new_addr[VLIB_TX].as_u32)
92     {
93       ip4->dst_address = new_addr[VLIB_TX];
94       sum =
95         ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
96                         new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
97     }
98   if (new_addr[VLIB_RX].as_u32)
99     {
100       ip4->src_address = new_addr[VLIB_RX];
101       sum =
102         ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
103                         new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
104     }
105   ip4->checksum = ip_csum_fold (sum);
106 }
107
108 static_always_inline void
109 cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
110 {
111   cnat_main_t *cm = &cnat_main;
112   if (PREDICT_FALSE (tcp_fin (tcp)))
113     {
114       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
115     }
116
117   if (PREDICT_FALSE (tcp_rst (tcp)))
118     {
119       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
120     }
121
122   if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
123     {
124       cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
125     }
126 }
127
128 static_always_inline void
129 cnat_translation_ip4 (const cnat_session_t * session,
130                       ip4_header_t * ip4, udp_header_t * udp)
131 {
132   tcp_header_t *tcp = (tcp_header_t *) udp;
133   ip4_address_t new_addr[VLIB_N_DIR];
134   u16 new_port[VLIB_N_DIR];
135
136   new_addr[VLIB_TX] = session->value.cs_ip[VLIB_TX].ip4;
137   new_addr[VLIB_RX] = session->value.cs_ip[VLIB_RX].ip4;
138   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
139   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
140
141   if (ip4->protocol == IP_PROTOCOL_TCP)
142     {
143       if (PREDICT_FALSE (tcp->checksum))
144         cnat_ip4_translate_l4 (ip4, udp, &tcp->checksum, new_addr, new_port);
145       else
146         {
147           udp->dst_port = new_port[VLIB_TX];
148           udp->src_port = new_port[VLIB_RX];
149         }
150       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
151     }
152   else if (ip4->protocol == IP_PROTOCOL_UDP)
153     {
154       if (PREDICT_FALSE (udp->checksum))
155         cnat_ip4_translate_l4 (ip4, udp, &udp->checksum, new_addr, new_port);
156       else
157         {
158           udp->dst_port = new_port[VLIB_TX];
159           udp->src_port = new_port[VLIB_RX];
160         }
161     }
162
163   cnat_ip4_translate_l3 (ip4, new_addr);
164 }
165
166 static_always_inline void
167 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
168 {
169   if (has_ip6_address (&new_addr[VLIB_TX]))
170     ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
171   if (has_ip6_address (&new_addr[VLIB_RX]))
172     ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
173 }
174
175 static_always_inline void
176 cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
177                        u16 * checksum,
178                        ip6_address_t new_addr[VLIB_N_DIR],
179                        u16 new_port[VLIB_N_DIR])
180 {
181   u16 old_port[VLIB_N_DIR];
182   ip6_address_t old_addr[VLIB_N_DIR];
183   ip_csum_t sum;
184
185   old_port[VLIB_TX] = udp->dst_port;
186   old_port[VLIB_RX] = udp->src_port;
187   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
188   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
189
190   sum = *checksum;
191   if (has_ip6_address (&new_addr[VLIB_TX]))
192     {
193       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
194       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
195       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
196       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
197     }
198
199   if (new_port[VLIB_TX])
200     {
201       udp->dst_port = new_port[VLIB_TX];
202       sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
203                             ip4_header_t /* cheat */ ,
204                             length /* changed member */ );
205     }
206   if (has_ip6_address (&new_addr[VLIB_RX]))
207     {
208       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
209       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
210       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
211       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
212     }
213
214   if (new_port[VLIB_RX])
215     {
216       udp->src_port = new_port[VLIB_RX];
217       sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
218                             ip4_header_t /* cheat */ ,
219                             length /* changed member */ );
220     }
221   *checksum = ip_csum_fold (sum);
222 }
223
224 static_always_inline void
225 cnat_translation_ip6 (const cnat_session_t * session,
226                       ip6_header_t * ip6, udp_header_t * udp)
227 {
228   tcp_header_t *tcp = (tcp_header_t *) udp;
229   ip6_address_t new_addr[VLIB_N_DIR];
230   u16 new_port[VLIB_N_DIR];
231
232   ip6_address_copy (&new_addr[VLIB_TX], &session->value.cs_ip[VLIB_TX].ip6);
233   ip6_address_copy (&new_addr[VLIB_RX], &session->value.cs_ip[VLIB_RX].ip6);
234   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
235   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
236
237   if (ip6->protocol == IP_PROTOCOL_TCP)
238     {
239       if (PREDICT_FALSE (tcp->checksum))
240         cnat_ip6_translate_l4 (ip6, udp, &tcp->checksum, new_addr, new_port);
241       else
242         {
243           udp->dst_port = new_port[VLIB_TX];
244           udp->src_port = new_port[VLIB_RX];
245         }
246       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
247     }
248   else if (ip6->protocol == IP_PROTOCOL_UDP)
249     {
250       if (PREDICT_FALSE (udp->checksum))
251         cnat_ip6_translate_l4 (ip6, udp, &udp->checksum, new_addr, new_port);
252       else
253         {
254           udp->dst_port = new_port[VLIB_TX];
255           udp->src_port = new_port[VLIB_RX];
256         }
257     }
258
259   cnat_ip6_translate_l3 (ip6, new_addr);
260 }
261
262 static_always_inline void
263 cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
264                        clib_bihash_kv_40_48_t * bkey)
265 {
266   udp_header_t *udp;
267   cnat_session_t *session = (cnat_session_t *) bkey;
268   if (AF_IP4 == af)
269     {
270       ip4_header_t *ip4;
271       ip4 = vlib_buffer_get_current (b);
272       udp = (udp_header_t *) (ip4 + 1);
273       session->key.cs_af = AF_IP4;
274       session->key.__cs_pad[0] = 0;
275       session->key.__cs_pad[1] = 0;
276
277       ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX], &ip4->dst_address);
278       ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX], &ip4->src_address);
279       session->key.cs_port[VLIB_RX] = udp->src_port;
280       session->key.cs_port[VLIB_TX] = udp->dst_port;
281       session->key.cs_proto = ip4->protocol;
282     }
283   else
284     {
285       ip6_header_t *ip6;
286       ip6 = vlib_buffer_get_current (b);
287       udp = (udp_header_t *) (ip6 + 1);
288       session->key.cs_af = AF_IP6;
289       session->key.__cs_pad[0] = 0;
290       session->key.__cs_pad[1] = 0;
291
292       ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX], &ip6->dst_address);
293       ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX], &ip6->src_address);
294       session->key.cs_port[VLIB_RX] = udp->src_port;
295       session->key.cs_port[VLIB_TX] = udp->dst_port;
296       session->key.cs_proto = ip6->protocol;
297     }
298 }
299
300 /**
301  * Create NAT sessions
302  */
303
304 static_always_inline void
305 cnat_session_create (cnat_session_t * session, cnat_node_ctx_t * ctx,
306                      u8 rsession_flags)
307 {
308   cnat_client_t *cc;
309   clib_bihash_kv_40_48_t rkey;
310   cnat_session_t *rsession = (cnat_session_t *) & rkey;
311   clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
312   clib_bihash_kv_40_48_t rvalue;
313   int rv;
314
315   /* create the reverse flow key */
316   ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
317                      &session->value.cs_ip[VLIB_TX]);
318   ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
319                      &session->value.cs_ip[VLIB_RX]);
320   rsession->key.cs_proto = session->key.cs_proto;
321   rsession->key.__cs_pad[0] = 0;
322   rsession->key.__cs_pad[1] = 0;
323   rsession->key.cs_af = ctx->af;
324   rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
325   rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
326
327   /* First search for existing reverse session */
328   rv = clib_bihash_search_inline_2_40_48 (&cnat_session_db, &rkey, &rvalue);
329   if (!rv)
330     {
331       /* Reverse session already exists
332          corresponding client should also exist
333          we only need to refcnt the timestamp */
334       cnat_session_t *found_rsession = (cnat_session_t *) & rvalue;
335       session->value.cs_ts_index = found_rsession->value.cs_ts_index;
336       cnat_timestamp_inc_refcnt (session->value.cs_ts_index);
337       clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1 /* is_add */ );
338       goto create_rsession;
339     }
340
341   session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
342   clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1);
343
344   /* is this the first time we've seen this source address */
345   cc = (AF_IP4 == ctx->af ?
346         cnat_client_ip4_find (&session->value.cs_ip[VLIB_RX].ip4) :
347         cnat_client_ip6_find (&session->value.cs_ip[VLIB_RX].ip6));
348
349   if (NULL == cc)
350     {
351       u64 r0 = 17;
352       if (AF_IP4 == ctx->af)
353         r0 = (u64) session->value.cs_ip[VLIB_RX].ip4.as_u32;
354       else
355         {
356           r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[0];
357           r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[1];
358         }
359
360       /* Rate limit */
361       if (!throttle_check (&cnat_throttle, ctx->thread_index, r0, ctx->seed))
362         {
363           cnat_learn_arg_t l;
364           l.addr.version = ctx->af;
365           ip46_address_copy (&l.addr.ip, &session->value.cs_ip[VLIB_RX]);
366           /* fire client create to the main thread */
367           vl_api_rpc_call_main_thread (cnat_client_learn,
368                                        (u8 *) & l, sizeof (l));
369         }
370       else
371         {
372           /* Will still need to count those for session refcnt */
373           ip_address_t *addr;
374           clib_spinlock_lock (&cnat_client_db.throttle_pool_lock
375                               [ctx->thread_index]);
376           pool_get (cnat_client_db.throttle_pool[ctx->thread_index], addr);
377           addr->version = ctx->af;
378           ip46_address_copy (&addr->ip, &session->value.cs_ip[VLIB_RX]);
379           clib_spinlock_unlock (&cnat_client_db.throttle_pool_lock
380                                 [ctx->thread_index]);
381         }
382     }
383   else
384     {
385       cnat_client_cnt_session (cc);
386     }
387
388 create_rsession:
389   /* add the reverse flow */
390   ip46_address_copy (&rsession->value.cs_ip[VLIB_RX],
391                      &session->key.cs_ip[VLIB_TX]);
392   ip46_address_copy (&rsession->value.cs_ip[VLIB_TX],
393                      &session->key.cs_ip[VLIB_RX]);
394   rsession->value.cs_ts_index = session->value.cs_ts_index;
395   rsession->value.cs_lbi = INDEX_INVALID;
396   rsession->value.flags = rsession_flags;
397   rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX];
398   rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX];
399
400   clib_bihash_add_del_40_48 (&cnat_session_db, &rkey, 1);
401 }
402
403 always_inline uword
404 cnat_node_inline (vlib_main_t * vm,
405                   vlib_node_runtime_t * node,
406                   vlib_frame_t * frame,
407                   cnat_node_sub_t cnat_sub,
408                   ip_address_family_t af, u8 do_trace)
409 {
410   u32 n_left, *from, thread_index;
411   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
412   vlib_buffer_t **b = bufs;
413   u16 nexts[VLIB_FRAME_SIZE], *next;
414   f64 now;
415   u64 seed;
416
417   thread_index = vm->thread_index;
418   from = vlib_frame_vector_args (frame);
419   n_left = frame->n_vectors;
420   next = nexts;
421   vlib_get_buffers (vm, from, bufs, n_left);
422   now = vlib_time_now (vm);
423   seed = throttle_seed (&cnat_throttle, thread_index, vlib_time_now (vm));
424   cnat_session_t *session[4];
425   clib_bihash_kv_40_48_t bkey[4], bvalue[4];
426   u64 hash[4];
427   int rv[4];
428
429   cnat_node_ctx_t ctx = { now, seed, thread_index, af, do_trace };
430
431   if (n_left >= 8)
432     {
433       /* Kickstart our state */
434       cnat_session_make_key (b[3], af, &bkey[3]);
435       cnat_session_make_key (b[2], af, &bkey[2]);
436       cnat_session_make_key (b[1], af, &bkey[1]);
437       cnat_session_make_key (b[0], af, &bkey[0]);
438
439       hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
440       hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
441       hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
442       hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
443     }
444
445   while (n_left >= 8)
446     {
447       if (n_left >= 12)
448         {
449           vlib_prefetch_buffer_header (b[11], LOAD);
450           vlib_prefetch_buffer_header (b[10], LOAD);
451           vlib_prefetch_buffer_header (b[9], LOAD);
452           vlib_prefetch_buffer_header (b[8], LOAD);
453         }
454
455       rv[3] =
456         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
457                                                      hash[3], &bkey[3],
458                                                      &bvalue[3]);
459       session[3] = (cnat_session_t *) (rv[3] ? &bkey[3] : &bvalue[3]);
460       next[3] = cnat_sub (vm, node, b[3], &ctx, rv[3], session[3]);
461
462       rv[2] =
463         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
464                                                      hash[2], &bkey[2],
465                                                      &bvalue[2]);
466       session[2] = (cnat_session_t *) (rv[2] ? &bkey[2] : &bvalue[2]);
467       next[2] = cnat_sub (vm, node, b[2], &ctx, rv[2], session[2]);
468
469       rv[1] =
470         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
471                                                      hash[1], &bkey[1],
472                                                      &bvalue[1]);
473       session[1] = (cnat_session_t *) (rv[1] ? &bkey[1] : &bvalue[1]);
474       next[1] = cnat_sub (vm, node, b[1], &ctx, rv[1], session[1]);
475
476       rv[0] =
477         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
478                                                      hash[0], &bkey[0],
479                                                      &bvalue[0]);
480       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
481       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
482
483       cnat_session_make_key (b[7], af, &bkey[3]);
484       cnat_session_make_key (b[6], af, &bkey[2]);
485       cnat_session_make_key (b[5], af, &bkey[1]);
486       cnat_session_make_key (b[4], af, &bkey[0]);
487
488       hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
489       hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
490       hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
491       hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
492
493       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[3]);
494       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[2]);
495       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[1]);
496       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[0]);
497
498       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[3]);
499       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[2]);
500       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[1]);
501       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[0]);
502
503       b += 4;
504       next += 4;
505       n_left -= 4;
506     }
507
508   while (n_left > 0)
509     {
510       cnat_session_make_key (b[0], af, &bkey[0]);
511       rv[0] = clib_bihash_search_inline_2_40_48 (&cnat_session_db,
512                                                  &bkey[0], &bvalue[0]);
513
514       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
515       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
516
517       b++;
518       next++;
519       n_left--;
520     }
521
522   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
523
524   return frame->n_vectors;
525 }
526
527 /*
528  * fd.io coding-style-patch-verification: ON
529  *
530  * Local Variables:
531  * eval: (c-set-style "gnu")
532  * End:
533  */
534
535 #endif