2 * nat44_ei.c - nat44 endpoint dependent plugin
4 * Copyright (c) 2020 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
18 #include <vnet/vnet.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/plugin/plugin.h>
23 #include <nat/nat_dpo.h>
24 #include <nat/lib/ipfix_logging.h>
25 #include <nat/lib/nat_syslog.h>
26 #include <nat/nat_inlines.h>
27 #include <nat/nat44/inlines.h>
28 #include <nat/nat_affinity.h>
29 #include <vnet/fib/fib_table.h>
30 #include <vnet/fib/ip4_fib.h>
31 #include <vnet/ip/reass/ip4_sv_reass.h>
32 #include <vppinfra/bihash_16_8.h>
33 #include <nat/nat44/ed_inlines.h>
34 #include <vnet/ip/ip_table.h>
36 #include <nat/nat44-ei/nat44_ei_inlines.h>
37 #include <nat/nat44-ei/nat44_ei.h>
40 nat44_ei_plugin_enable ()
42 nat44_ei_set_alloc_default ();
48 nat44_ei_plugin_disable ()
54 nat44_ei_free_session_data (snat_main_t *sm, snat_session_t *s,
55 u32 thread_index, u8 is_ha)
57 clib_bihash_kv_8_8_t kv;
59 snat_main_per_thread_data_t *tsm =
60 vec_elt_at_index (sm->per_thread_data, thread_index);
62 init_nat_i2o_k (&kv, s);
63 if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
64 nat_elog_warn ("in2out key del failed");
66 init_nat_o2i_k (&kv, s);
67 if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
68 nat_elog_warn ("out2in key del failed");
72 nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
73 &s->in2out.addr, s->in2out.port,
74 &s->out2in.addr, s->out2in.port, s->nat_proto);
76 nat_ipfix_logging_nat44_ses_delete (
77 thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
78 s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
80 nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
81 s->ext_host_port, s->nat_proto, s->out2in.fib_index,
85 if (snat_is_session_static (s))
88 snat_free_outside_address_and_port (sm->addresses, thread_index,
89 &s->out2in.addr, s->out2in.port,
93 static_always_inline void
94 nat44_ei_user_del_sessions (snat_user_t *u, u32 thread_index)
99 snat_main_t *sm = &snat_main;
100 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
104 pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index);
106 elt = pool_elt_at_index (tsm->list_pool, elt->next);
108 while (elt->value != ~0)
110 s = pool_elt_at_index (tsm->sessions, elt->value);
111 elt = pool_elt_at_index (tsm->list_pool, elt->next);
113 nat44_ei_free_session_data (sm, s, thread_index, 0);
114 nat44_delete_session (sm, s, thread_index);
119 nat44_ei_user_del (ip4_address_t *addr, u32 fib_index)
123 snat_main_t *sm = &snat_main;
124 snat_main_per_thread_data_t *tsm;
126 snat_user_key_t user_key;
127 clib_bihash_kv_8_8_t kv, value;
129 if (sm->endpoint_dependent)
132 user_key.addr.as_u32 = addr->as_u32;
133 user_key.fib_index = fib_index;
134 kv.key = user_key.as_u64;
136 if (sm->num_workers > 1)
138 vec_foreach (tsm, sm->per_thread_data)
140 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
142 nat44_ei_user_del_sessions (
143 pool_elt_at_index (tsm->users, value.value),
152 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
153 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
155 nat44_ei_user_del_sessions (
156 pool_elt_at_index (tsm->users, value.value), tsm->thread_index);
164 nat44_ei_static_mapping_del_sessions (snat_main_t *sm,
165 snat_main_per_thread_data_t *tsm,
166 snat_user_key_t u_key, int addr_only,
167 ip4_address_t e_addr, u16 e_port)
169 clib_bihash_kv_8_8_t kv, value;
170 kv.key = u_key.as_u64;
172 dlist_elt_t *head, *elt;
175 u32 elt_index, head_index, ses_index;
177 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
179 user_index = value.value;
180 u = pool_elt_at_index (tsm->users, user_index);
181 if (u->nstaticsessions)
183 head_index = u->sessions_per_user_list_head_index;
184 head = pool_elt_at_index (tsm->list_pool, head_index);
185 elt_index = head->next;
186 elt = pool_elt_at_index (tsm->list_pool, elt_index);
187 ses_index = elt->value;
188 while (ses_index != ~0)
190 s = pool_elt_at_index (tsm->sessions, ses_index);
191 elt = pool_elt_at_index (tsm->list_pool, elt->next);
192 ses_index = elt->value;
196 if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
197 (s->out2in.port != e_port))
201 if (is_lb_session (s))
204 if (!snat_is_session_static (s))
207 nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
208 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
218 nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
221 snat_main_t *sm = &snat_main;
222 u32 next_worker_index = 0;
225 next_worker_index = sm->first_worker_index;
226 hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
227 (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
229 if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
230 next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
232 next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
234 return next_worker_index;
238 nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
239 u32 rx_fib_index0, u8 is_output)
241 snat_main_t *sm = &snat_main;
244 clib_bihash_kv_8_8_t kv, value;
245 snat_static_mapping_t *m;
247 u32 next_worker_index = 0;
249 /* first try static mappings without port */
250 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
252 init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0);
253 if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
256 m = pool_elt_at_index (sm->static_mappings, value.value);
257 return m->workers[0];
261 proto = ip_proto_to_nat_proto (ip0->protocol);
262 udp = ip4_next_header (ip0);
263 port = udp->dst_port;
265 /* unknown protocol */
266 if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER))
268 /* use current thread */
269 return vlib_get_thread_index ();
272 if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
274 icmp46_header_t *icmp = (icmp46_header_t *) udp;
275 icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
276 if (!icmp_type_is_error_message (
277 vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
278 port = vnet_buffer (b)->ip.reass.l4_src_port;
281 /* if error message, then it's not fragmented and we can access it */
282 ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
283 proto = ip_proto_to_nat_proto (inner_ip->protocol);
284 void *l4_header = ip4_next_header (inner_ip);
287 case NAT_PROTOCOL_ICMP:
288 icmp = (icmp46_header_t *) l4_header;
289 echo = (icmp_echo_header_t *) (icmp + 1);
290 port = echo->identifier;
292 case NAT_PROTOCOL_UDP:
293 case NAT_PROTOCOL_TCP:
294 port = ((tcp_udp_header_t *) l4_header)->src_port;
297 return vlib_get_thread_index ();
302 /* try static mappings with port */
303 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
305 init_nat_k (&kv, ip0->dst_address, port, rx_fib_index0, proto);
306 if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
309 m = pool_elt_at_index (sm->static_mappings, value.value);
310 return m->workers[0];
314 /* worker by outside port */
315 next_worker_index = sm->first_worker_index;
317 sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
318 return next_worker_index;
322 nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index,
323 u32 thread_index, nat_protocol_t proto,
324 ip4_address_t *addr, u16 *port, u16 port_per_thread,
325 u32 snat_thread_index)
328 snat_address_t *a, *ga = 0;
331 for (i = 0; i < vec_len (addresses); i++)
336 #define _(N, j, n, s) \
337 case NAT_PROTOCOL_##N: \
338 if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
340 if (a->fib_index == fib_index) \
344 portnum = (port_per_thread * snat_thread_index) + \
345 snat_random_port (0, port_per_thread - 1) + 1024; \
346 if (a->busy_##n##_port_refcounts[portnum]) \
348 --a->busy_##n##_port_refcounts[portnum]; \
349 a->busy_##n##_ports_per_thread[thread_index]++; \
350 a->busy_##n##_ports++; \
352 *port = clib_host_to_net_u16 (portnum); \
356 else if (a->fib_index == ~0) \
364 default : nat_elog_info ("unknown protocol");
374 #define _(N, j, n, s) \
375 case NAT_PROTOCOL_##N: \
378 portnum = (port_per_thread * snat_thread_index) + \
379 snat_random_port (0, port_per_thread - 1) + 1024; \
380 if (a->busy_##n##_port_refcounts[portnum]) \
382 ++a->busy_##n##_port_refcounts[portnum]; \
383 a->busy_##n##_ports_per_thread[thread_index]++; \
384 a->busy_##n##_ports++; \
386 *port = clib_host_to_net_u16 (portnum); \
392 default : nat_elog_info ("unknown protocol");
397 /* Totally out of translations to use... */
398 nat_ipfix_logging_addresses_exhausted (thread_index, 0);
403 nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index,
404 u32 thread_index, nat_protocol_t proto,
405 ip4_address_t *addr, u16 *port, u16 port_per_thread,
406 u32 snat_thread_index)
408 snat_main_t *sm = &snat_main;
409 snat_address_t *a = addresses;
412 ports = sm->end_port - sm->start_port + 1;
414 if (!vec_len (addresses))
419 #define _(N, i, n, s) \
420 case NAT_PROTOCOL_##N: \
421 if (a->busy_##n##_ports < ports) \
425 portnum = snat_random_port (sm->start_port, sm->end_port); \
426 if (a->busy_##n##_port_refcounts[portnum]) \
428 ++a->busy_##n##_port_refcounts[portnum]; \
429 a->busy_##n##_ports++; \
431 *port = clib_host_to_net_u16 (portnum); \
438 default : nat_elog_info ("unknown protocol");
443 /* Totally out of translations to use... */
444 nat_ipfix_logging_addresses_exhausted (thread_index, 0);
449 nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index,
450 u32 thread_index, nat_protocol_t proto,
451 ip4_address_t *addr, u16 *port, u16 port_per_thread,
452 u32 snat_thread_index)
454 snat_main_t *sm = &snat_main;
455 snat_address_t *a = addresses;
456 u16 m, ports, portnum, A, j;
457 m = 16 - (sm->psid_offset + sm->psid_length);
458 ports = (1 << (16 - sm->psid_length)) - (1 << m);
460 if (!vec_len (addresses))
465 #define _(N, i, n, s) \
466 case NAT_PROTOCOL_##N: \
467 if (a->busy_##n##_ports < ports) \
471 A = snat_random_port (1, pow2_mask (sm->psid_offset)); \
472 j = snat_random_port (0, pow2_mask (m)); \
473 portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \
474 if (a->busy_##n##_port_refcounts[portnum]) \
476 ++a->busy_##n##_port_refcounts[portnum]; \
477 a->busy_##n##_ports++; \
479 *port = clib_host_to_net_u16 (portnum); \
486 default : nat_elog_info ("unknown protocol");
491 /* Totally out of translations to use... */
492 nat_ipfix_logging_addresses_exhausted (thread_index, 0);
497 nat44_ei_set_alloc_default ()
499 snat_main_t *sm = &snat_main;
501 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
502 sm->alloc_addr_and_port = nat44_ei_alloc_default_cb;
506 nat44_ei_set_alloc_range (u16 start_port, u16 end_port)
508 snat_main_t *sm = &snat_main;
510 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
511 sm->alloc_addr_and_port = nat44_ei_alloc_range_cb;
512 sm->start_port = start_port;
513 sm->end_port = end_port;
517 nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length)
519 snat_main_t *sm = &snat_main;
521 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
522 sm->alloc_addr_and_port = nat44_ei_alloc_mape_cb;
524 sm->psid_offset = psid_offset;
525 sm->psid_length = psid_length;
529 * fd.io coding-style-patch-verification: ON
532 * eval: (c-set-style "gnu")