blob: ae2e64e1f82edcfa305cc706bf8133b590a9f8e3 [file] [log] [blame]
Dave Barach20c02cb2016-06-26 10:42:08 -04001/*
2 * snat.c - simple nat plugin
3 *
4 * Copyright (c) 2016 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:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <vnet/vnet.h>
Dave Barachcab65ec2017-01-11 13:01:14 -050019#include <vnet/ip/ip.h>
20#include <vnet/ip/ip4.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040021#include <vnet/plugin/plugin.h>
Matus Fabian2ba92e32017-08-21 07:05:03 -070022#include <nat/nat.h>
Juraj Slobodacba69362017-12-19 02:09:32 +010023#include <nat/nat_dpo.h>
Matus Fabian2ba92e32017-08-21 07:05:03 -070024#include <nat/nat_ipfix_logging.h>
25#include <nat/nat_det.h>
26#include <nat/nat64.h>
Matus Fabianf2a23cc2018-01-22 03:41:53 -080027#include <nat/nat66.h>
Matus Fabian8ebe6252017-11-06 05:04:53 -080028#include <nat/dslite.h>
Matus Fabianefcd1e92017-08-15 06:59:19 -070029#include <nat/nat_reass.h>
Matus Fabian229c1aa2018-05-28 04:09:52 -070030#include <nat/nat_inlines.h>
Matus Fabianea5b5be2018-09-03 05:02:23 -070031#include <nat/nat_affinity.h>
Matus Fabiane1ae29a2017-01-27 00:47:58 -080032#include <vnet/fib/fib_table.h>
33#include <vnet/fib/ip4_fib.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040034
Damjan Marion3b46cba2017-01-23 21:13:45 +010035#include <vpp/app/version.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040036
37snat_main_t snat_main;
38
Matus Fabianab395ec2018-09-20 23:18:41 -070039/* *INDENT-OFF* */
Dave Barach20c02cb2016-06-26 10:42:08 -040040
Dave Barach20c02cb2016-06-26 10:42:08 -040041/* Hook up input features */
Damjan Marion8b3191e2016-11-09 19:54:20 +010042VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
43 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070044 .node_name = "nat44-in2out",
Matus Fabian16f05462018-02-08 05:28:28 -080045 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Dave Barach20c02cb2016-06-26 10:42:08 -040046};
Damjan Marion8b3191e2016-11-09 19:54:20 +010047VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
48 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070049 .node_name = "nat44-out2in",
Matus Fabiana6110b62018-06-13 05:39:07 -070050 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
Dave Barach525c9d02018-05-26 10:48:55 -040051 "ip4-dhcp-client-detect"),
Dave Barach20c02cb2016-06-26 10:42:08 -040052};
Matus Fabian36ea2d62017-10-24 04:13:49 -070053VNET_FEATURE_INIT (ip4_nat_classify, static) = {
54 .arc_name = "ip4-unicast",
55 .node_name = "nat44-classify",
Matus Fabian16f05462018-02-08 05:28:28 -080056 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian36ea2d62017-10-24 04:13:49 -070057};
Matus Fabian066f0342017-02-10 03:48:01 -080058VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = {
59 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070060 .node_name = "nat44-det-in2out",
Matus Fabian16f05462018-02-08 05:28:28 -080061 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian066f0342017-02-10 03:48:01 -080062};
63VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = {
64 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070065 .node_name = "nat44-det-out2in",
Matus Fabiana6110b62018-06-13 05:39:07 -070066 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
67 "ip4-dhcp-client-detect"),
Matus Fabian066f0342017-02-10 03:48:01 -080068};
Matus Fabian36ea2d62017-10-24 04:13:49 -070069VNET_FEATURE_INIT (ip4_nat_det_classify, static) = {
70 .arc_name = "ip4-unicast",
71 .node_name = "nat44-det-classify",
Matus Fabian16f05462018-02-08 05:28:28 -080072 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian36ea2d62017-10-24 04:13:49 -070073};
Matus Fabiana6110b62018-06-13 05:39:07 -070074VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = {
75 .arc_name = "ip4-unicast",
76 .node_name = "nat44-ed-in2out",
77 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
78};
79VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = {
80 .arc_name = "ip4-unicast",
81 .node_name = "nat44-ed-out2in",
82 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
83 "ip4-dhcp-client-detect"),
84};
85VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = {
86 .arc_name = "ip4-unicast",
87 .node_name = "nat44-ed-classify",
88 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
89};
Matus Fabian475f0552016-10-19 06:17:52 -070090VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = {
91 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070092 .node_name = "nat44-in2out-worker-handoff",
Matus Fabian16f05462018-02-08 05:28:28 -080093 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian475f0552016-10-19 06:17:52 -070094};
95VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = {
96 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070097 .node_name = "nat44-out2in-worker-handoff",
Matus Fabiana6110b62018-06-13 05:39:07 -070098 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
99 "ip4-dhcp-client-detect"),
Matus Fabian475f0552016-10-19 06:17:52 -0700100};
Matus Fabian36ea2d62017-10-24 04:13:49 -0700101VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
102 .arc_name = "ip4-unicast",
103 .node_name = "nat44-handoff-classify",
Matus Fabian16f05462018-02-08 05:28:28 -0800104 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian36ea2d62017-10-24 04:13:49 -0700105};
Damjan Marion8b3191e2016-11-09 19:54:20 +0100106VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
107 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700108 .node_name = "nat44-in2out-fast",
Matus Fabian16f05462018-02-08 05:28:28 -0800109 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabiandb649882016-08-26 05:45:27 -0700110};
Damjan Marion8b3191e2016-11-09 19:54:20 +0100111VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
112 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700113 .node_name = "nat44-out2in-fast",
Matus Fabiana6110b62018-06-13 05:39:07 -0700114 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
115 "ip4-dhcp-client-detect"),
Matus Fabiandb649882016-08-26 05:45:27 -0700116};
Matus Fabian161c59c2017-07-21 03:46:03 -0700117VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
118 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700119 .node_name = "nat44-hairpin-dst",
Matus Fabian16f05462018-02-08 05:28:28 -0800120 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
Matus Fabian161c59c2017-07-21 03:46:03 -0700121};
Matus Fabiana6110b62018-06-13 05:39:07 -0700122VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_dst, static) = {
123 .arc_name = "ip4-unicast",
124 .node_name = "nat44-ed-hairpin-dst",
125 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
126};
Matus Fabiandb649882016-08-26 05:45:27 -0700127
Matus Fabian93d84c92017-07-19 08:06:01 -0700128/* Hook up output features */
129VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
130 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700131 .node_name = "nat44-in2out-output",
Matus Fabian16f05462018-02-08 05:28:28 -0800132 .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
Matus Fabian93d84c92017-07-19 08:06:01 -0700133};
Matus Fabian93d84c92017-07-19 08:06:01 -0700134VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
135 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700136 .node_name = "nat44-in2out-output-worker-handoff",
Matus Fabian16f05462018-02-08 05:28:28 -0800137 .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
Matus Fabian93d84c92017-07-19 08:06:01 -0700138};
Matus Fabian161c59c2017-07-21 03:46:03 -0700139VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = {
140 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700141 .node_name = "nat44-hairpin-src",
Matus Fabian16f05462018-02-08 05:28:28 -0800142 .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
Matus Fabian161c59c2017-07-21 03:46:03 -0700143};
Matus Fabiana6110b62018-06-13 05:39:07 -0700144VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = {
145 .arc_name = "ip4-output",
146 .node_name = "nat44-ed-in2out-output",
147 .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
148};
149VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_src, static) = {
150 .arc_name = "ip4-output",
151 .node_name = "nat44-ed-hairpin-src",
152 .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
153};
Matus Fabian93d84c92017-07-19 08:06:01 -0700154
Matus Fabian87da4762017-10-04 08:03:56 -0700155/* Hook up ip4-local features */
156VNET_FEATURE_INIT (ip4_nat_hairpinning, static) =
157{
158 .arc_name = "ip4-local",
159 .node_name = "nat44-hairpinning",
160 .runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
161};
Matus Fabiana6110b62018-06-13 05:39:07 -0700162VNET_FEATURE_INIT (ip4_nat44_ed_hairpinning, static) =
163{
164 .arc_name = "ip4-local",
165 .node_name = "nat44-ed-hairpinning",
166 .runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
167};
Matus Fabian87da4762017-10-04 08:03:56 -0700168
Matus Fabian93d84c92017-07-19 08:06:01 -0700169
Damjan Marion3b46cba2017-01-23 21:13:45 +0100170VLIB_PLUGIN_REGISTER () = {
171 .version = VPP_BUILD_VER,
Damjan Marion1bfb0dd2017-03-22 11:08:39 +0100172 .description = "Network Address Translation",
Damjan Marion3b46cba2017-01-23 21:13:45 +0100173};
174/* *INDENT-ON* */
Dave Barach20c02cb2016-06-26 10:42:08 -0400175
Matus Fabianb932d262017-12-18 05:38:24 -0800176void
177nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
178{
179 snat_session_key_t key;
180 clib_bihash_kv_8_8_t kv;
181 nat_ed_ses_key_t ed_key;
182 clib_bihash_kv_16_8_t ed_kv;
Matus Fabianb932d262017-12-18 05:38:24 -0800183 snat_main_per_thread_data_t *tsm =
184 vec_elt_at_index (sm->per_thread_data, thread_index);
185
Matus Fabian36ed73a2018-04-18 01:39:17 -0700186 if (is_fwd_bypass_session (s))
187 {
188 ed_key.l_addr = s->in2out.addr;
189 ed_key.r_addr = s->ext_host_addr;
190 ed_key.l_port = s->in2out.port;
191 ed_key.r_port = s->ext_host_port;
192 ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
193 ed_key.fib_index = 0;
194 ed_kv.key[0] = ed_key.as_u64[0];
195 ed_kv.key[1] = ed_key.as_u64[1];
Matus Fabiana6110b62018-06-13 05:39:07 -0700196 if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
Matus Fabianab395ec2018-09-20 23:18:41 -0700197 nat_log_warn ("in2out_ed key del failed");
Matus Fabian36ed73a2018-04-18 01:39:17 -0700198 return;
199 }
200
Matus Fabiana6110b62018-06-13 05:39:07 -0700201 /* session lookup tables */
Matus Fabianb932d262017-12-18 05:38:24 -0800202 if (is_ed_session (s))
203 {
Matus Fabianea5b5be2018-09-03 05:02:23 -0700204 if (is_affinity_sessions (s))
Matus Fabianab395ec2018-09-20 23:18:41 -0700205 nat_affinity_unlock (s->ext_host_addr, s->out2in.addr,
206 s->in2out.protocol, s->out2in.port);
Matus Fabianb932d262017-12-18 05:38:24 -0800207 ed_key.l_addr = s->out2in.addr;
208 ed_key.r_addr = s->ext_host_addr;
209 ed_key.fib_index = s->out2in.fib_index;
210 if (snat_is_unk_proto_session (s))
Matus Fabianab395ec2018-09-20 23:18:41 -0700211 {
212 ed_key.proto = s->in2out.port;
213 ed_key.r_port = 0;
214 ed_key.l_port = 0;
215 }
Matus Fabianb932d262017-12-18 05:38:24 -0800216 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700217 {
218 ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
219 ed_key.l_port = s->out2in.port;
220 ed_key.r_port = s->ext_host_port;
221 }
Matus Fabianb932d262017-12-18 05:38:24 -0800222 ed_kv.key[0] = ed_key.as_u64[0];
223 ed_kv.key[1] = ed_key.as_u64[1];
Matus Fabiana6110b62018-06-13 05:39:07 -0700224 if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0))
Matus Fabianab395ec2018-09-20 23:18:41 -0700225 nat_log_warn ("out2in_ed key del failed");
Matus Fabianb932d262017-12-18 05:38:24 -0800226 ed_key.l_addr = s->in2out.addr;
227 ed_key.fib_index = s->in2out.fib_index;
228 if (!snat_is_unk_proto_session (s))
Matus Fabianab395ec2018-09-20 23:18:41 -0700229 ed_key.l_port = s->in2out.port;
Matus Fabianb932d262017-12-18 05:38:24 -0800230 if (is_twice_nat_session (s))
Matus Fabianab395ec2018-09-20 23:18:41 -0700231 {
232 ed_key.r_addr = s->ext_host_nat_addr;
233 ed_key.r_port = s->ext_host_nat_port;
234 }
Matus Fabianb932d262017-12-18 05:38:24 -0800235 ed_kv.key[0] = ed_key.as_u64[0];
236 ed_kv.key[1] = ed_key.as_u64[1];
Matus Fabiana6110b62018-06-13 05:39:07 -0700237 if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
Matus Fabianab395ec2018-09-20 23:18:41 -0700238 nat_log_warn ("in2out_ed key del failed");
Matus Fabianb932d262017-12-18 05:38:24 -0800239 }
Matus Fabiana6110b62018-06-13 05:39:07 -0700240 else
241 {
242 kv.key = s->in2out.as_u64;
243 if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
Matus Fabianab395ec2018-09-20 23:18:41 -0700244 nat_log_warn ("in2out key del failed");
Matus Fabiana6110b62018-06-13 05:39:07 -0700245 kv.key = s->out2in.as_u64;
246 if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
Matus Fabianab395ec2018-09-20 23:18:41 -0700247 nat_log_warn ("out2in key del failed");
Matus Fabiana6110b62018-06-13 05:39:07 -0700248 }
Matus Fabianb932d262017-12-18 05:38:24 -0800249
250 if (snat_is_unk_proto_session (s))
251 return;
252
253 /* log NAT event */
Matus Fabianab395ec2018-09-20 23:18:41 -0700254 snat_ipfix_logging_nat44_ses_delete (s->in2out.addr.as_u32,
255 s->out2in.addr.as_u32,
256 s->in2out.protocol,
257 s->in2out.port,
258 s->out2in.port, s->in2out.fib_index);
Matus Fabianb932d262017-12-18 05:38:24 -0800259
260 /* Twice NAT address and port for external host */
Matus Fabian70a26ac2018-05-14 06:20:28 -0700261 if (is_twice_nat_session (s))
Matus Fabianb932d262017-12-18 05:38:24 -0800262 {
shubing guo762a4932018-08-13 17:16:46 +0800263 key.protocol = s->in2out.protocol;
264 key.port = s->ext_host_nat_port;
265 key.addr.as_u32 = s->ext_host_nat_addr.as_u32;
266 snat_free_outside_address_and_port (sm->twice_nat_addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -0700267 thread_index, &key);
Matus Fabianb932d262017-12-18 05:38:24 -0800268 }
269
Matus Fabianb932d262017-12-18 05:38:24 -0800270 if (snat_is_session_static (s))
271 return;
272
Matus Fabianab395ec2018-09-20 23:18:41 -0700273 snat_free_outside_address_and_port (sm->addresses, thread_index,
274 &s->out2in);
Matus Fabianb932d262017-12-18 05:38:24 -0800275}
276
277snat_user_t *
Matus Fabianab395ec2018-09-20 23:18:41 -0700278nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
279 u32 thread_index)
Matus Fabianb932d262017-12-18 05:38:24 -0800280{
281 snat_user_t *u = 0;
282 snat_user_key_t user_key;
283 clib_bihash_kv_8_8_t kv, value;
284 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
Matus Fabianab395ec2018-09-20 23:18:41 -0700285 dlist_elt_t *per_user_list_head_elt;
Matus Fabianb932d262017-12-18 05:38:24 -0800286
287 user_key.addr.as_u32 = addr->as_u32;
288 user_key.fib_index = fib_index;
289 kv.key = user_key.as_u64;
290
291 /* Ever heard of the "user" = src ip4 address before? */
292 if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
293 {
294 /* no, make a new one */
295 pool_get (tsm->users, u);
296 memset (u, 0, sizeof (*u));
297 u->addr.as_u32 = addr->as_u32;
298 u->fib_index = fib_index;
299
300 pool_get (tsm->list_pool, per_user_list_head_elt);
301
302 u->sessions_per_user_list_head_index = per_user_list_head_elt -
Matus Fabianab395ec2018-09-20 23:18:41 -0700303 tsm->list_pool;
Matus Fabianb932d262017-12-18 05:38:24 -0800304
305 clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index);
306
307 kv.value = u - tsm->users;
308
309 /* add user */
310 if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
Matus Fabianab395ec2018-09-20 23:18:41 -0700311 nat_log_warn ("user_hash keay add failed");
Matus Fabianb932d262017-12-18 05:38:24 -0800312 }
313 else
314 {
315 u = pool_elt_at_index (tsm->users, value.value);
316 }
317
318 return u;
319}
320
321snat_session_t *
Matus Fabianab395ec2018-09-20 23:18:41 -0700322nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
323 u32 thread_index)
Matus Fabianb932d262017-12-18 05:38:24 -0800324{
325 snat_session_t *s;
326 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
327 u32 oldest_per_user_translation_list_index, session_index;
Matus Fabianab395ec2018-09-20 23:18:41 -0700328 dlist_elt_t *oldest_per_user_translation_list_elt;
329 dlist_elt_t *per_user_translation_list_elt;
Matus Fabianb932d262017-12-18 05:38:24 -0800330
331 /* Over quota? Recycle the least recently used translation */
332 if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
333 {
334 oldest_per_user_translation_list_index =
Matus Fabianab395ec2018-09-20 23:18:41 -0700335 clib_dlist_remove_head (tsm->list_pool,
336 u->sessions_per_user_list_head_index);
Matus Fabianb932d262017-12-18 05:38:24 -0800337
338 ASSERT (oldest_per_user_translation_list_index != ~0);
339
340 /* Add it back to the end of the LRU list */
341 clib_dlist_addtail (tsm->list_pool,
Matus Fabianab395ec2018-09-20 23:18:41 -0700342 u->sessions_per_user_list_head_index,
343 oldest_per_user_translation_list_index);
Matus Fabianb932d262017-12-18 05:38:24 -0800344 /* Get the list element */
345 oldest_per_user_translation_list_elt =
Matus Fabianab395ec2018-09-20 23:18:41 -0700346 pool_elt_at_index (tsm->list_pool,
347 oldest_per_user_translation_list_index);
Matus Fabianb932d262017-12-18 05:38:24 -0800348
349 /* Get the session index from the list element */
350 session_index = oldest_per_user_translation_list_elt->value;
351
352 /* Get the session */
353 s = pool_elt_at_index (tsm->sessions, session_index);
354 nat_free_session_data (sm, s, thread_index);
Matus Fabianab395ec2018-09-20 23:18:41 -0700355 if (snat_is_session_static (s))
356 u->nstaticsessions--;
Matus Fabian132dc492018-05-09 04:51:03 -0700357 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700358 u->nsessions--;
Matus Fabianb932d262017-12-18 05:38:24 -0800359 s->flags = 0;
360 s->total_bytes = 0;
361 s->total_pkts = 0;
Matus Fabianebdf1902018-05-04 03:57:42 -0700362 s->state = 0;
363 s->ext_host_addr.as_u32 = 0;
364 s->ext_host_port = 0;
365 s->ext_host_nat_addr.as_u32 = 0;
366 s->ext_host_nat_port = 0;
Matus Fabianb932d262017-12-18 05:38:24 -0800367 }
368 else
369 {
370 pool_get (tsm->sessions, s);
371 memset (s, 0, sizeof (*s));
Matus Fabianb932d262017-12-18 05:38:24 -0800372
373 /* Create list elts */
374 pool_get (tsm->list_pool, per_user_translation_list_elt);
375 clib_dlist_init (tsm->list_pool,
Matus Fabianab395ec2018-09-20 23:18:41 -0700376 per_user_translation_list_elt - tsm->list_pool);
Matus Fabianb932d262017-12-18 05:38:24 -0800377
378 per_user_translation_list_elt->value = s - tsm->sessions;
379 s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
380 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
381
382 clib_dlist_addtail (tsm->list_pool,
Matus Fabianab395ec2018-09-20 23:18:41 -0700383 s->per_user_list_head_index,
384 per_user_translation_list_elt - tsm->list_pool);
Matus Fabianb932d262017-12-18 05:38:24 -0800385 }
386
387 return s;
388}
389
Matus Fabian878c6462018-08-23 00:33:35 -0700390snat_session_t *
Matus Fabian8fdc0152018-09-24 04:41:28 -0700391nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
392 f64 now)
Matus Fabian878c6462018-08-23 00:33:35 -0700393{
394 snat_session_t *s;
395 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
Matus Fabian8fdc0152018-09-24 04:41:28 -0700396 dlist_elt_t *per_user_translation_list_elt, *oldest_elt;
397 u32 oldest_index;
398 u64 sess_timeout_time;
Matus Fabian878c6462018-08-23 00:33:35 -0700399
400 if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
401 {
Matus Fabian8fdc0152018-09-24 04:41:28 -0700402 oldest_index =
403 clib_dlist_remove_head (tsm->list_pool,
404 u->sessions_per_user_list_head_index);
405 oldest_elt = pool_elt_at_index (tsm->list_pool, oldest_index);
406 s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
407 sess_timeout_time =
408 s->last_heard + (f64) nat44_session_get_timeout (sm, s);
409 if (now >= sess_timeout_time)
410 {
411 clib_dlist_addtail (tsm->list_pool,
412 u->sessions_per_user_list_head_index,
413 oldest_index);
414 nat_free_session_data (sm, s, thread_index);
415 if (snat_is_session_static (s))
416 u->nstaticsessions--;
417 else
418 u->nsessions--;
419 s->flags = 0;
420 s->total_bytes = 0;
421 s->total_pkts = 0;
422 s->state = 0;
423 s->ext_host_addr.as_u32 = 0;
424 s->ext_host_port = 0;
425 s->ext_host_nat_addr.as_u32 = 0;
426 s->ext_host_nat_port = 0;
427 }
428 else
429 {
430 clib_dlist_addhead (tsm->list_pool,
431 u->sessions_per_user_list_head_index,
432 oldest_index);
433 nat_log_warn ("max translations per user %U", format_ip4_address,
434 &u->addr);
435 snat_ipfix_logging_max_entries_per_user
436 (sm->max_translations_per_user, u->addr.as_u32);
437 return 0;
438 }
Matus Fabian878c6462018-08-23 00:33:35 -0700439 }
Matus Fabian8fdc0152018-09-24 04:41:28 -0700440 else
441 {
442 pool_get (tsm->sessions, s);
443 memset (s, 0, sizeof (*s));
Matus Fabian878c6462018-08-23 00:33:35 -0700444
Matus Fabian8fdc0152018-09-24 04:41:28 -0700445 /* Create list elts */
446 pool_get (tsm->list_pool, per_user_translation_list_elt);
447 clib_dlist_init (tsm->list_pool,
448 per_user_translation_list_elt - tsm->list_pool);
Matus Fabian878c6462018-08-23 00:33:35 -0700449
Matus Fabian8fdc0152018-09-24 04:41:28 -0700450 per_user_translation_list_elt->value = s - tsm->sessions;
451 s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
452 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
Matus Fabian878c6462018-08-23 00:33:35 -0700453
Matus Fabian8fdc0152018-09-24 04:41:28 -0700454 clib_dlist_addtail (tsm->list_pool,
455 s->per_user_list_head_index,
456 per_user_translation_list_elt - tsm->list_pool);
457 }
Matus Fabian878c6462018-08-23 00:33:35 -0700458 return s;
459}
460
Matus Fabian066f0342017-02-10 03:48:01 -0800461void
462snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
Matus Fabianab395ec2018-09-20 23:18:41 -0700463 int is_add)
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800464{
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800465 fib_prefix_t prefix = {
Matus Fabian066f0342017-02-10 03:48:01 -0800466 .fp_len = p_len,
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800467 .fp_proto = FIB_PROTOCOL_IP4,
468 .fp_addr = {
Matus Fabianab395ec2018-09-20 23:18:41 -0700469 .ip4.as_u32 = addr->as_u32,
470 },
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800471 };
Matus Fabianab395ec2018-09-20 23:18:41 -0700472 u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800473
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800474 if (is_add)
Matus Fabianab395ec2018-09-20 23:18:41 -0700475 fib_table_entry_update_one_path (fib_index,
476 &prefix,
477 FIB_SOURCE_PLUGIN_LOW,
478 (FIB_ENTRY_FLAG_CONNECTED |
479 FIB_ENTRY_FLAG_LOCAL |
480 FIB_ENTRY_FLAG_EXCLUSIVE),
481 DPO_PROTO_IP4,
482 NULL,
483 sw_if_index,
484 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800485 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700486 fib_table_entry_delete (fib_index, &prefix, FIB_SOURCE_PLUGIN_LOW);
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800487}
488
Matus Fabianab395ec2018-09-20 23:18:41 -0700489int
490snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id,
491 u8 twice_nat)
Dave Barach20c02cb2016-06-26 10:42:08 -0400492{
Matus Fabianab395ec2018-09-20 23:18:41 -0700493 snat_address_t *ap;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800494 snat_interface_t *i;
Matus Fabian624b8d92017-09-12 04:15:30 -0700495 vlib_thread_main_t *tm = vlib_get_thread_main ();
Dave Barach20c02cb2016-06-26 10:42:08 -0400496
Matus Fabiana6110b62018-06-13 05:39:07 -0700497 if (twice_nat && !sm->endpoint_dependent)
498 return VNET_API_ERROR_FEATURE_DISABLED;
499
Matus Fabian860dacc2016-10-25 04:19:26 -0700500 /* Check if address already exists */
Matus Fabianab395ec2018-09-20 23:18:41 -0700501 /* *INDENT-OFF* */
Matus Fabianb932d262017-12-18 05:38:24 -0800502 vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses)
Matus Fabian860dacc2016-10-25 04:19:26 -0700503 {
504 if (ap->addr.as_u32 == addr->as_u32)
Matus Fabiana6110b62018-06-13 05:39:07 -0700505 return VNET_API_ERROR_VALUE_EXIST;
Matus Fabian860dacc2016-10-25 04:19:26 -0700506 }
Matus Fabianab395ec2018-09-20 23:18:41 -0700507 /* *INDENT-ON* */
Matus Fabian860dacc2016-10-25 04:19:26 -0700508
Matus Fabianb932d262017-12-18 05:38:24 -0800509 if (twice_nat)
510 vec_add2 (sm->twice_nat_addresses, ap, 1);
511 else
512 vec_add2 (sm->addresses, ap, 1);
513
Dave Barach20c02cb2016-06-26 10:42:08 -0400514 ap->addr = *addr;
Matus Fabianf8d84902017-07-23 23:41:03 -0700515 if (vrf_id != ~0)
516 ap->fib_index =
Neale Ranns15002542017-09-10 04:39:11 -0700517 fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
Matus Fabianab395ec2018-09-20 23:18:41 -0700518 FIB_SOURCE_PLUGIN_LOW);
Matus Fabianf8d84902017-07-23 23:41:03 -0700519 else
520 ap->fib_index = ~0;
Matus Fabian09d96f42017-02-02 01:43:00 -0800521#define _(N, i, n, s) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700522 clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); \
523 ap->busy_##n##_ports = 0; \
dongjuandf865202018-09-11 11:20:30 +0000524 ap->busy_##n##_ports_per_thread = 0;\
Matus Fabian624b8d92017-09-12 04:15:30 -0700525 vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
Matus Fabian09d96f42017-02-02 01:43:00 -0800526 foreach_snat_protocol
527#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -0700528 if (twice_nat)
Matus Fabiana6110b62018-06-13 05:39:07 -0700529 return 0;
Matus Fabianb932d262017-12-18 05:38:24 -0800530
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800531 /* Add external address to FIB */
Matus Fabianab395ec2018-09-20 23:18:41 -0700532 /* *INDENT-OFF* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800533 pool_foreach (i, sm->interfaces,
534 ({
Juraj Slobodacba69362017-12-19 02:09:32 +0100535 if (nat_interface_is_inside(i) || sm->out2in_dpo)
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800536 continue;
537
Matus Fabian066f0342017-02-10 03:48:01 -0800538 snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
Matus Fabiandccbee32017-01-31 22:20:30 -0800539 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800540 }));
Matus Fabian93d84c92017-07-19 08:06:01 -0700541 pool_foreach (i, sm->output_feature_interfaces,
542 ({
Juraj Slobodacba69362017-12-19 02:09:32 +0100543 if (nat_interface_is_inside(i) || sm->out2in_dpo)
Matus Fabian93d84c92017-07-19 08:06:01 -0700544 continue;
545
546 snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
547 break;
548 }));
Matus Fabianab395ec2018-09-20 23:18:41 -0700549 /* *INDENT-ON* */
Matus Fabiana6110b62018-06-13 05:39:07 -0700550
551 return 0;
Dave Barach20c02cb2016-06-26 10:42:08 -0400552}
553
Matus Fabianab395ec2018-09-20 23:18:41 -0700554static int
555is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr)
Matus Fabian724b8152016-10-04 03:23:43 -0700556{
557 snat_static_mapping_t *m;
Matus Fabianab395ec2018-09-20 23:18:41 -0700558 /* *INDENT-OFF* */
Matus Fabian724b8152016-10-04 03:23:43 -0700559 pool_foreach (m, sm->static_mappings,
560 ({
561 if (m->external_addr.as_u32 == addr.as_u32)
562 return 1;
563 }));
Matus Fabianab395ec2018-09-20 23:18:41 -0700564 /* *INDENT-ON* */
Matus Fabian724b8152016-10-04 03:23:43 -0700565
566 return 0;
567}
568
Matus Fabianab395ec2018-09-20 23:18:41 -0700569void
570increment_v4_address (ip4_address_t * a)
Dave Barach20c02cb2016-06-26 10:42:08 -0400571{
572 u32 v;
Matus Fabian2ba92e32017-08-21 07:05:03 -0700573
Matus Fabianab395ec2018-09-20 23:18:41 -0700574 v = clib_net_to_host_u32 (a->as_u32) + 1;
575 a->as_u32 = clib_host_to_net_u32 (v);
Dave Barach20c02cb2016-06-26 10:42:08 -0400576}
577
Matus Fabian2ba92e32017-08-21 07:05:03 -0700578static void
579snat_add_static_mapping_when_resolved (snat_main_t * sm,
Matus Fabianab395ec2018-09-20 23:18:41 -0700580 ip4_address_t l_addr,
581 u16 l_port,
582 u32 sw_if_index,
583 u16 e_port,
584 u32 vrf_id,
585 snat_protocol_t proto,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -0700586 int addr_only, int is_add, u8 * tag,
587 int twice_nat, int out2in_only,
588 int identity_nat)
Dave Barach8b275372017-01-16 10:54:02 -0500589{
590 snat_static_map_resolve_t *rp;
591
592 vec_add2 (sm->to_resolve, rp, 1);
593 rp->l_addr.as_u32 = l_addr.as_u32;
594 rp->l_port = l_port;
595 rp->sw_if_index = sw_if_index;
596 rp->e_port = e_port;
597 rp->vrf_id = vrf_id;
Matus Fabian09d96f42017-02-02 01:43:00 -0800598 rp->proto = proto;
Dave Barach8b275372017-01-16 10:54:02 -0500599 rp->addr_only = addr_only;
600 rp->is_add = is_add;
Matus Fabiane2f4e2f2018-10-07 21:28:23 -0700601 rp->twice_nat = twice_nat;
602 rp->out2in_only = out2in_only;
603 rp->identity_nat = identity_nat;
Matus Fabian5f224992018-01-25 21:59:16 -0800604 rp->tag = vec_dup (tag);
Dave Barach8b275372017-01-16 10:54:02 -0500605}
Matus Fabianab395ec2018-09-20 23:18:41 -0700606
607static u32
608get_thread_idx_by_port (u16 e_port)
dongjuan58f50f12018-09-04 17:40:53 +0800609{
Matus Fabianab395ec2018-09-20 23:18:41 -0700610 snat_main_t *sm = &snat_main;
611 u32 thread_idx = sm->num_workers;
612 if (sm->num_workers > 1)
dongjuan58f50f12018-09-04 17:40:53 +0800613 {
Matus Fabianab395ec2018-09-20 23:18:41 -0700614 thread_idx =
615 sm->first_worker_index +
616 sm->workers[(e_port - 1024) / sm->port_per_thread];
617 }
618 return thread_idx;
dongjuan58f50f12018-09-04 17:40:53 +0800619}
Dave Barach8b275372017-01-16 10:54:02 -0500620
Matus Fabianab395ec2018-09-20 23:18:41 -0700621int
622snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
623 u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
624 u32 sw_if_index, snat_protocol_t proto, int is_add,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -0700625 twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag,
626 u8 identity_nat)
Matus Fabiandb649882016-08-26 05:45:27 -0700627{
Matus Fabianab395ec2018-09-20 23:18:41 -0700628 snat_main_t *sm = &snat_main;
Matus Fabiandb649882016-08-26 05:45:27 -0700629 snat_static_mapping_t *m;
Matus Fabian09d96f42017-02-02 01:43:00 -0800630 snat_session_key_t m_key;
Matus Fabiandb649882016-08-26 05:45:27 -0700631 clib_bihash_kv_8_8_t kv, value;
632 snat_address_t *a = 0;
633 u32 fib_index = ~0;
Matus Fabianab395ec2018-09-20 23:18:41 -0700634 uword *p;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800635 snat_interface_t *interface;
Matus Fabiandb649882016-08-26 05:45:27 -0700636 int i;
Matus Fabianb932d262017-12-18 05:38:24 -0800637 snat_main_per_thread_data_t *tsm;
Matus Fabianb793d092018-01-31 05:50:21 -0800638 snat_user_key_t u_key;
639 snat_user_t *u;
Matus Fabianab395ec2018-09-20 23:18:41 -0700640 dlist_elt_t *head, *elt;
Matus Fabianb793d092018-01-31 05:50:21 -0800641 u32 elt_index, head_index;
642 u32 ses_index;
643 u64 user_index;
Matus Fabianab395ec2018-09-20 23:18:41 -0700644 snat_session_t *s;
Matus Fabianf13a8782018-04-06 02:54:40 -0700645 snat_static_map_resolve_t *rp, *rp_match = 0;
Matus Fabiandb649882016-08-26 05:45:27 -0700646
Matus Fabiana6110b62018-06-13 05:39:07 -0700647 if (!sm->endpoint_dependent)
648 {
649 if (twice_nat || out2in_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700650 return VNET_API_ERROR_FEATURE_DISABLED;
Matus Fabiana6110b62018-06-13 05:39:07 -0700651 }
652
Dave Barach8b275372017-01-16 10:54:02 -0500653 /* If the external address is a specific interface address */
654 if (sw_if_index != ~0)
655 {
Matus Fabianab395ec2018-09-20 23:18:41 -0700656 ip4_address_t *first_int_addr;
Matus Fabianea2600a2018-03-28 04:06:26 -0700657
658 for (i = 0; i < vec_len (sm->to_resolve); i++)
Matus Fabianab395ec2018-09-20 23:18:41 -0700659 {
660 rp = sm->to_resolve + i;
661 if (rp->sw_if_index != sw_if_index ||
662 rp->l_addr.as_u32 != l_addr.as_u32 ||
663 rp->vrf_id != vrf_id || rp->addr_only != addr_only)
664 continue;
Matus Fabianea2600a2018-03-28 04:06:26 -0700665
Matus Fabianab395ec2018-09-20 23:18:41 -0700666 if (!addr_only)
667 {
668 if (rp->l_port != l_port || rp->e_port != e_port
669 || rp->proto != proto)
670 continue;
671 }
Matus Fabianea2600a2018-03-28 04:06:26 -0700672
Matus Fabianab395ec2018-09-20 23:18:41 -0700673 rp_match = rp;
674 break;
675 }
Dave Barach8b275372017-01-16 10:54:02 -0500676
677 /* Might be already set... */
Matus Fabian2ba92e32017-08-21 07:05:03 -0700678 first_int_addr = ip4_interface_first_address
Matus Fabianab395ec2018-09-20 23:18:41 -0700679 (sm->ip4_main, sw_if_index, 0 /* just want the address */ );
Dave Barach8b275372017-01-16 10:54:02 -0500680
Matus Fabianea2600a2018-03-28 04:06:26 -0700681 if (is_add)
Matus Fabianab395ec2018-09-20 23:18:41 -0700682 {
683 if (rp_match)
684 return VNET_API_ERROR_VALUE_EXIST;
Matus Fabianea2600a2018-03-28 04:06:26 -0700685
Matus Fabianab395ec2018-09-20 23:18:41 -0700686 snat_add_static_mapping_when_resolved
687 (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -0700688 addr_only, is_add, tag, twice_nat, out2in_only, identity_nat);
Matus Fabian4772e7a2018-04-04 00:38:02 -0700689
Matus Fabianab395ec2018-09-20 23:18:41 -0700690 /* DHCP resolution required? */
691 if (first_int_addr == 0)
692 {
693 return 0;
694 }
695 else
696 {
697 e_addr.as_u32 = first_int_addr->as_u32;
698 /* Identity mapping? */
699 if (l_addr.as_u32 == 0)
700 l_addr.as_u32 = e_addr.as_u32;
701 }
702 }
Matus Fabianea2600a2018-03-28 04:06:26 -0700703 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700704 {
705 if (!rp_match)
706 return VNET_API_ERROR_NO_SUCH_ENTRY;
Matus Fabianea2600a2018-03-28 04:06:26 -0700707
Matus Fabianab395ec2018-09-20 23:18:41 -0700708 vec_del1 (sm->to_resolve, i);
Matus Fabianea2600a2018-03-28 04:06:26 -0700709
Matus Fabianab395ec2018-09-20 23:18:41 -0700710 if (first_int_addr)
711 {
712 e_addr.as_u32 = first_int_addr->as_u32;
713 /* Identity mapping? */
714 if (l_addr.as_u32 == 0)
715 l_addr.as_u32 = e_addr.as_u32;
716 }
717 else
718 return 0;
719 }
Dave Barach8b275372017-01-16 10:54:02 -0500720 }
721
Matus Fabiandb649882016-08-26 05:45:27 -0700722 m_key.addr = e_addr;
723 m_key.port = addr_only ? 0 : e_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800724 m_key.protocol = addr_only ? 0 : proto;
Matus Fabian8008d7c2018-07-09 01:34:20 -0700725 m_key.fib_index = 0;
Matus Fabiandb649882016-08-26 05:45:27 -0700726 kv.key = m_key.as_u64;
727 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
728 m = 0;
729 else
730 m = pool_elt_at_index (sm->static_mappings, value.value);
731
732 if (is_add)
733 {
734 if (m)
Matus Fabianab395ec2018-09-20 23:18:41 -0700735 return VNET_API_ERROR_VALUE_EXIST;
Matus Fabiandb649882016-08-26 05:45:27 -0700736
Matus Fabianb932d262017-12-18 05:38:24 -0800737 if (twice_nat && addr_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700738 return VNET_API_ERROR_UNSUPPORTED;
Matus Fabianb932d262017-12-18 05:38:24 -0800739
Matus Fabiandb649882016-08-26 05:45:27 -0700740 /* Convert VRF id to FIB index */
741 if (vrf_id != ~0)
Matus Fabianab395ec2018-09-20 23:18:41 -0700742 {
743 p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
744 if (!p)
745 return VNET_API_ERROR_NO_SUCH_FIB;
746 fib_index = p[0];
747 }
Matus Fabiandb649882016-08-26 05:45:27 -0700748 /* If not specified use inside VRF id from SNAT plugin startup config */
749 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700750 {
751 fib_index = sm->inside_fib_index;
752 vrf_id = sm->inside_vrf_id;
753 }
Matus Fabiandb649882016-08-26 05:45:27 -0700754
Matus Fabian36a62702018-04-04 03:27:43 -0700755 if (!out2in_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700756 {
757 m_key.addr = l_addr;
758 m_key.port = addr_only ? 0 : l_port;
759 m_key.protocol = addr_only ? 0 : proto;
760 m_key.fib_index = fib_index;
761 kv.key = m_key.as_u64;
762 if (!clib_bihash_search_8_8
763 (&sm->static_mapping_by_local, &kv, &value))
764 return VNET_API_ERROR_VALUE_EXIST;
765 }
Matus Fabian36a62702018-04-04 03:27:43 -0700766
Matus Fabiandb649882016-08-26 05:45:27 -0700767 /* Find external address in allocated addresses and reserve port for
768 address and port pair mapping when dynamic translations enabled */
Matus Fabiane82488f2018-01-18 03:38:45 -0800769 if (!(addr_only || sm->static_mapping_only || out2in_only))
Matus Fabianab395ec2018-09-20 23:18:41 -0700770 {
771 for (i = 0; i < vec_len (sm->addresses); i++)
772 {
773 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
774 {
775 a = sm->addresses + i;
776 /* External port must be unused */
777 switch (proto)
778 {
Matus Fabian09d96f42017-02-02 01:43:00 -0800779#define _(N, j, n, s) \
780 case SNAT_PROTOCOL_##N: \
781 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
782 return VNET_API_ERROR_INVALID_VALUE; \
783 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
784 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700785 { \
786 a->busy_##n##_ports++; \
dongjuan58f50f12018-09-04 17:40:53 +0800787 a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \
Matus Fabian624b8d92017-09-12 04:15:30 -0700788 } \
Matus Fabian09d96f42017-02-02 01:43:00 -0800789 break;
Matus Fabianab395ec2018-09-20 23:18:41 -0700790 foreach_snat_protocol
Matus Fabian09d96f42017-02-02 01:43:00 -0800791#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -0700792 default:
793 nat_log_info ("unknown protocol");
794 return VNET_API_ERROR_INVALID_VALUE_2;
795 }
796 break;
797 }
798 }
799 /* External address must be allocated */
800 if (!a && (l_addr.as_u32 != e_addr.as_u32))
801 {
802 if (sw_if_index != ~0)
803 {
804 for (i = 0; i < vec_len (sm->to_resolve); i++)
805 {
806 rp = sm->to_resolve + i;
807 if (rp->addr_only)
808 continue;
809 if (rp->sw_if_index != sw_if_index &&
810 rp->l_addr.as_u32 != l_addr.as_u32 &&
811 rp->vrf_id != vrf_id && rp->l_port != l_port &&
812 rp->e_port != e_port && rp->proto != proto)
813 continue;
Matus Fabianf13a8782018-04-06 02:54:40 -0700814
Matus Fabianab395ec2018-09-20 23:18:41 -0700815 vec_del1 (sm->to_resolve, i);
816 break;
817 }
818 }
819 return VNET_API_ERROR_NO_SUCH_ENTRY;
820 }
821 }
Matus Fabiandb649882016-08-26 05:45:27 -0700822
823 pool_get (sm->static_mappings, m);
824 memset (m, 0, sizeof (*m));
Matus Fabian5f224992018-01-25 21:59:16 -0800825 m->tag = vec_dup (tag);
Matus Fabiandb649882016-08-26 05:45:27 -0700826 m->local_addr = l_addr;
827 m->external_addr = e_addr;
Matus Fabiandb649882016-08-26 05:45:27 -0700828 m->vrf_id = vrf_id;
829 m->fib_index = fib_index;
Matus Fabianb932d262017-12-18 05:38:24 -0800830 m->twice_nat = twice_nat;
Matus Fabiane2f4e2f2018-10-07 21:28:23 -0700831 if (out2in_only)
832 m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY;
833 if (addr_only)
834 m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY;
835 if (identity_nat)
836 m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT;
Matus Fabiandb649882016-08-26 05:45:27 -0700837 if (!addr_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700838 {
839 m->local_port = l_port;
840 m->external_port = e_port;
841 m->proto = proto;
842 }
Matus Fabiandb649882016-08-26 05:45:27 -0700843
Matus Fabiana6110b62018-06-13 05:39:07 -0700844 if (sm->num_workers > 1)
Matus Fabianab395ec2018-09-20 23:18:41 -0700845 {
846 ip4_header_t ip = {
847 .src_address = m->local_addr,
848 };
849 vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index));
850 tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
851 }
Matus Fabianb932d262017-12-18 05:38:24 -0800852 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700853 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
Matus Fabianb932d262017-12-18 05:38:24 -0800854
Matus Fabiandb649882016-08-26 05:45:27 -0700855 m_key.addr = m->local_addr;
856 m_key.port = m->local_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800857 m_key.protocol = m->proto;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700858 m_key.fib_index = m->fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700859 kv.key = m_key.as_u64;
860 kv.value = m - sm->static_mappings;
Matus Fabiane82488f2018-01-18 03:38:45 -0800861 if (!out2in_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700862 clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
Matus Fabiandb649882016-08-26 05:45:27 -0700863
864 m_key.addr = m->external_addr;
865 m_key.port = m->external_port;
Matus Fabian8008d7c2018-07-09 01:34:20 -0700866 m_key.fib_index = 0;
Matus Fabiandb649882016-08-26 05:45:27 -0700867 kv.key = m_key.as_u64;
868 kv.value = m - sm->static_mappings;
Matus Fabianab395ec2018-09-20 23:18:41 -0700869 clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1);
Matus Fabianb932d262017-12-18 05:38:24 -0800870
Matus Fabianb793d092018-01-31 05:50:21 -0800871 /* Delete dynamic sessions matching local address (+ local port) */
872 if (!(sm->static_mapping_only))
Matus Fabianab395ec2018-09-20 23:18:41 -0700873 {
874 u_key.addr = m->local_addr;
875 u_key.fib_index = m->fib_index;
876 kv.key = u_key.as_u64;
877 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
878 {
879 user_index = value.value;
880 u = pool_elt_at_index (tsm->users, user_index);
881 if (u->nsessions)
882 {
883 head_index = u->sessions_per_user_list_head_index;
884 head = pool_elt_at_index (tsm->list_pool, head_index);
885 elt_index = head->next;
886 elt = pool_elt_at_index (tsm->list_pool, elt_index);
887 ses_index = elt->value;
888 while (ses_index != ~0)
889 {
890 s = pool_elt_at_index (tsm->sessions, ses_index);
891 elt = pool_elt_at_index (tsm->list_pool, elt->next);
892 ses_index = elt->value;
Matus Fabianb793d092018-01-31 05:50:21 -0800893
Matus Fabianab395ec2018-09-20 23:18:41 -0700894 if (snat_is_session_static (s))
895 continue;
Matus Fabianb793d092018-01-31 05:50:21 -0800896
Matus Fabianab395ec2018-09-20 23:18:41 -0700897 if (!addr_only
898 && (clib_net_to_host_u16 (s->in2out.port) !=
899 m->local_port))
900 continue;
Matus Fabianb793d092018-01-31 05:50:21 -0800901
Matus Fabianab395ec2018-09-20 23:18:41 -0700902 nat_free_session_data (sm, s,
903 tsm - sm->per_thread_data);
904 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
Matus Fabianb793d092018-01-31 05:50:21 -0800905
Matus Fabianab395ec2018-09-20 23:18:41 -0700906 if (!addr_only && !sm->endpoint_dependent)
907 break;
908 }
909 }
910 }
911 }
Matus Fabiandb649882016-08-26 05:45:27 -0700912 }
913 else
914 {
915 if (!m)
Matus Fabianab395ec2018-09-20 23:18:41 -0700916 {
917 if (sw_if_index != ~0)
918 return 0;
919 else
920 return VNET_API_ERROR_NO_SUCH_ENTRY;
921 }
Matus Fabiandb649882016-08-26 05:45:27 -0700922
923 /* Free external address port */
Matus Fabiane82488f2018-01-18 03:38:45 -0800924 if (!(addr_only || sm->static_mapping_only || out2in_only))
Matus Fabianab395ec2018-09-20 23:18:41 -0700925 {
926 for (i = 0; i < vec_len (sm->addresses); i++)
927 {
928 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
929 {
930 a = sm->addresses + i;
931 switch (proto)
932 {
Matus Fabian09d96f42017-02-02 01:43:00 -0800933#define _(N, j, n, s) \
934 case SNAT_PROTOCOL_##N: \
935 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
936 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700937 { \
938 a->busy_##n##_ports--; \
dongjuan58f50f12018-09-04 17:40:53 +0800939 a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \
Matus Fabian624b8d92017-09-12 04:15:30 -0700940 } \
Matus Fabian09d96f42017-02-02 01:43:00 -0800941 break;
Matus Fabianab395ec2018-09-20 23:18:41 -0700942 foreach_snat_protocol
Matus Fabian09d96f42017-02-02 01:43:00 -0800943#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -0700944 default:
945 nat_log_info ("unknown protocol");
946 return VNET_API_ERROR_INVALID_VALUE_2;
947 }
948 break;
949 }
950 }
951 }
Matus Fabiandb649882016-08-26 05:45:27 -0700952
Matus Fabianb932d262017-12-18 05:38:24 -0800953 if (sm->num_workers > 1)
Matus Fabianab395ec2018-09-20 23:18:41 -0700954 tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
Matus Fabianb932d262017-12-18 05:38:24 -0800955 else
Matus Fabianab395ec2018-09-20 23:18:41 -0700956 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
Matus Fabianb932d262017-12-18 05:38:24 -0800957
Matus Fabiandb649882016-08-26 05:45:27 -0700958 m_key.addr = m->local_addr;
959 m_key.port = m->local_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800960 m_key.protocol = m->proto;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700961 m_key.fib_index = m->fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700962 kv.key = m_key.as_u64;
Matus Fabiane82488f2018-01-18 03:38:45 -0800963 if (!out2in_only)
Matus Fabianab395ec2018-09-20 23:18:41 -0700964 clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0);
Matus Fabiandb649882016-08-26 05:45:27 -0700965
966 m_key.addr = m->external_addr;
967 m_key.port = m->external_port;
Matus Fabian8008d7c2018-07-09 01:34:20 -0700968 m_key.fib_index = 0;
Matus Fabiandb649882016-08-26 05:45:27 -0700969 kv.key = m_key.as_u64;
Matus Fabianab395ec2018-09-20 23:18:41 -0700970 clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0);
Matus Fabiandb649882016-08-26 05:45:27 -0700971
972 /* Delete session(s) for static mapping if exist */
973 if (!(sm->static_mapping_only) ||
Matus Fabianab395ec2018-09-20 23:18:41 -0700974 (sm->static_mapping_only && sm->static_mapping_connection_tracking))
975 {
976 u_key.addr = m->local_addr;
977 u_key.fib_index = m->fib_index;
978 kv.key = u_key.as_u64;
979 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
980 {
981 user_index = value.value;
982 u = pool_elt_at_index (tsm->users, user_index);
983 if (u->nstaticsessions)
984 {
985 head_index = u->sessions_per_user_list_head_index;
986 head = pool_elt_at_index (tsm->list_pool, head_index);
987 elt_index = head->next;
988 elt = pool_elt_at_index (tsm->list_pool, elt_index);
989 ses_index = elt->value;
990 while (ses_index != ~0)
991 {
992 s = pool_elt_at_index (tsm->sessions, ses_index);
993 elt = pool_elt_at_index (tsm->list_pool, elt->next);
994 ses_index = elt->value;
Matus Fabiandb649882016-08-26 05:45:27 -0700995
Matus Fabianab395ec2018-09-20 23:18:41 -0700996 if (!addr_only)
997 {
998 if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
999 (clib_net_to_host_u16 (s->out2in.port) !=
1000 e_port))
1001 continue;
1002 }
Matus Fabian13c08182018-04-11 00:36:57 -07001003
Matus Fabianab395ec2018-09-20 23:18:41 -07001004 if (is_lb_session (s))
1005 continue;
Matus Fabian475f0552016-10-19 06:17:52 -07001006
Matus Fabianab395ec2018-09-20 23:18:41 -07001007 if (!snat_is_session_static (s))
1008 continue;
Matus Fabian70a26ac2018-05-14 06:20:28 -07001009
Matus Fabianab395ec2018-09-20 23:18:41 -07001010 nat_free_session_data (sm, s,
1011 tsm - sm->per_thread_data);
1012 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
Matus Fabiandb649882016-08-26 05:45:27 -07001013
Matus Fabianab395ec2018-09-20 23:18:41 -07001014 if (!addr_only && !sm->endpoint_dependent)
1015 break;
1016 }
1017 }
1018 }
1019 }
Matus Fabiandb649882016-08-26 05:45:27 -07001020
Matus Fabian5f224992018-01-25 21:59:16 -08001021 vec_free (m->tag);
Matus Fabiana6110b62018-06-13 05:39:07 -07001022 vec_free (m->workers);
Matus Fabiandb649882016-08-26 05:45:27 -07001023 /* Delete static mapping from pool */
1024 pool_put (sm->static_mappings, m);
1025 }
1026
Matus Fabianab7a8052017-11-28 04:29:41 -08001027 if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001028 return 0;
1029
1030 /* Add/delete external address to FIB */
Matus Fabianab395ec2018-09-20 23:18:41 -07001031 /* *INDENT-OFF* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001032 pool_foreach (interface, sm->interfaces,
1033 ({
Juraj Slobodacba69362017-12-19 02:09:32 +01001034 if (nat_interface_is_inside(interface) || sm->out2in_dpo)
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001035 continue;
1036
Matus Fabian066f0342017-02-10 03:48:01 -08001037 snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
Matus Fabiandccbee32017-01-31 22:20:30 -08001038 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001039 }));
Matus Fabian93d84c92017-07-19 08:06:01 -07001040 pool_foreach (interface, sm->output_feature_interfaces,
1041 ({
Juraj Slobodacba69362017-12-19 02:09:32 +01001042 if (nat_interface_is_inside(interface) || sm->out2in_dpo)
Matus Fabian93d84c92017-07-19 08:06:01 -07001043 continue;
1044
1045 snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
1046 break;
1047 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001048 /* *INDENT-ON* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001049
Matus Fabiandb649882016-08-26 05:45:27 -07001050 return 0;
1051}
1052
Matus Fabianab395ec2018-09-20 23:18:41 -07001053int
1054nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
1055 snat_protocol_t proto,
1056 nat44_lb_addr_port_t * locals, u8 is_add,
1057 twice_nat_type_t twice_nat, u8 out2in_only,
1058 u8 * tag, u32 affinity)
Matus Fabian704018c2017-09-04 02:17:18 -07001059{
Matus Fabianab395ec2018-09-20 23:18:41 -07001060 snat_main_t *sm = &snat_main;
Matus Fabian704018c2017-09-04 02:17:18 -07001061 snat_static_mapping_t *m;
1062 snat_session_key_t m_key;
1063 clib_bihash_kv_8_8_t kv, value;
Matus Fabian704018c2017-09-04 02:17:18 -07001064 snat_address_t *a = 0;
1065 int i;
1066 nat44_lb_addr_port_t *local;
Matus Fabiana6110b62018-06-13 05:39:07 -07001067 u32 elt_index, head_index, ses_index;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001068 snat_main_per_thread_data_t *tsm;
Matus Fabianb932d262017-12-18 05:38:24 -08001069 snat_user_key_t u_key;
1070 snat_user_t *u;
Matus Fabianab395ec2018-09-20 23:18:41 -07001071 snat_session_t *s;
1072 dlist_elt_t *head, *elt;
Matus Fabiana6110b62018-06-13 05:39:07 -07001073 uword *bitmap = 0;
1074
1075 if (!sm->endpoint_dependent)
1076 return VNET_API_ERROR_FEATURE_DISABLED;
Matus Fabian704018c2017-09-04 02:17:18 -07001077
1078 m_key.addr = e_addr;
1079 m_key.port = e_port;
1080 m_key.protocol = proto;
Matus Fabian8008d7c2018-07-09 01:34:20 -07001081 m_key.fib_index = 0;
Matus Fabian704018c2017-09-04 02:17:18 -07001082 kv.key = m_key.as_u64;
1083 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
1084 m = 0;
1085 else
1086 m = pool_elt_at_index (sm->static_mappings, value.value);
1087
1088 if (is_add)
1089 {
1090 if (m)
Matus Fabianab395ec2018-09-20 23:18:41 -07001091 return VNET_API_ERROR_VALUE_EXIST;
Matus Fabian704018c2017-09-04 02:17:18 -07001092
1093 if (vec_len (locals) < 2)
Matus Fabianab395ec2018-09-20 23:18:41 -07001094 return VNET_API_ERROR_INVALID_VALUE;
Matus Fabian704018c2017-09-04 02:17:18 -07001095
Matus Fabian704018c2017-09-04 02:17:18 -07001096 /* Find external address in allocated addresses and reserve port for
1097 address and port pair mapping when dynamic translations enabled */
Matus Fabian240b5ef2018-01-11 04:09:17 -08001098 if (!(sm->static_mapping_only || out2in_only))
Matus Fabianab395ec2018-09-20 23:18:41 -07001099 {
1100 for (i = 0; i < vec_len (sm->addresses); i++)
1101 {
1102 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1103 {
1104 a = sm->addresses + i;
1105 /* External port must be unused */
1106 switch (proto)
1107 {
Matus Fabian704018c2017-09-04 02:17:18 -07001108#define _(N, j, n, s) \
1109 case SNAT_PROTOCOL_##N: \
1110 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
1111 return VNET_API_ERROR_INVALID_VALUE; \
1112 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
1113 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -07001114 { \
1115 a->busy_##n##_ports++; \
dongjuan58f50f12018-09-04 17:40:53 +08001116 a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001117 } \
Matus Fabian704018c2017-09-04 02:17:18 -07001118 break;
Matus Fabianab395ec2018-09-20 23:18:41 -07001119 foreach_snat_protocol
Matus Fabian704018c2017-09-04 02:17:18 -07001120#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -07001121 default:
1122 nat_log_info ("unknown protocol");
1123 return VNET_API_ERROR_INVALID_VALUE_2;
1124 }
1125 break;
1126 }
1127 }
1128 /* External address must be allocated */
1129 if (!a)
1130 return VNET_API_ERROR_NO_SUCH_ENTRY;
1131 }
Matus Fabian704018c2017-09-04 02:17:18 -07001132
1133 pool_get (sm->static_mappings, m);
1134 memset (m, 0, sizeof (*m));
Matus Fabian5f224992018-01-25 21:59:16 -08001135 m->tag = vec_dup (tag);
Matus Fabian704018c2017-09-04 02:17:18 -07001136 m->external_addr = e_addr;
Matus Fabian704018c2017-09-04 02:17:18 -07001137 m->external_port = e_port;
1138 m->proto = proto;
Matus Fabianb932d262017-12-18 05:38:24 -08001139 m->twice_nat = twice_nat;
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07001140 if (out2in_only)
1141 m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY;
Matus Fabianea5b5be2018-09-03 05:02:23 -07001142 m->affinity = affinity;
1143
1144 if (affinity)
Matus Fabianab395ec2018-09-20 23:18:41 -07001145 m->affinity_per_service_list_head_index =
1146 nat_affinity_get_per_service_list_head_index ();
Matus Fabianea5b5be2018-09-03 05:02:23 -07001147 else
Matus Fabianab395ec2018-09-20 23:18:41 -07001148 m->affinity_per_service_list_head_index = ~0;
Matus Fabian704018c2017-09-04 02:17:18 -07001149
1150 m_key.addr = m->external_addr;
1151 m_key.port = m->external_port;
1152 m_key.protocol = m->proto;
Matus Fabian8008d7c2018-07-09 01:34:20 -07001153 m_key.fib_index = 0;
Matus Fabian704018c2017-09-04 02:17:18 -07001154 kv.key = m_key.as_u64;
1155 kv.value = m - sm->static_mappings;
Matus Fabianab395ec2018-09-20 23:18:41 -07001156 if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1))
1157 {
1158 nat_log_err ("static_mapping_by_external key add failed");
1159 return VNET_API_ERROR_UNSPECIFIED;
1160 }
Matus Fabian704018c2017-09-04 02:17:18 -07001161
Matus Fabian092b3cd2017-09-19 05:42:38 -07001162 m_key.fib_index = m->fib_index;
Matus Fabian704018c2017-09-04 02:17:18 -07001163 for (i = 0; i < vec_len (locals); i++)
Matus Fabianab395ec2018-09-20 23:18:41 -07001164 {
1165 locals[i].fib_index =
1166 fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
1167 locals[i].vrf_id,
1168 FIB_SOURCE_PLUGIN_LOW);
1169 m_key.addr = locals[i].addr;
1170 m_key.fib_index = locals[i].fib_index;
1171 if (!out2in_only)
1172 {
1173 m_key.port = locals[i].port;
1174 kv.key = m_key.as_u64;
1175 kv.value = m - sm->static_mappings;
1176 clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
1177 }
1178 locals[i].prefix = (i == 0) ? locals[i].probability :
1179 (locals[i - 1].prefix + locals[i].probability);
1180 vec_add1 (m->locals, locals[i]);
1181 if (sm->num_workers > 1)
1182 {
1183 ip4_header_t ip = {
1184 .src_address = locals[i].addr,
1185 };
1186 bitmap =
1187 clib_bitmap_set (bitmap,
1188 sm->worker_in2out_cb (&ip, m->fib_index), 1);
1189 }
1190 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001191
1192 /* Assign workers */
1193 if (sm->num_workers > 1)
Matus Fabianab395ec2018-09-20 23:18:41 -07001194 {
1195 /* *INDENT-OFF* */
Matus Fabiana6110b62018-06-13 05:39:07 -07001196 clib_bitmap_foreach (i, bitmap,
1197 ({
1198 vec_add1(m->workers, i);
1199 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001200 /* *INDENT-ON* */
1201 }
Matus Fabian704018c2017-09-04 02:17:18 -07001202 }
1203 else
1204 {
1205 if (!m)
Matus Fabianab395ec2018-09-20 23:18:41 -07001206 return VNET_API_ERROR_NO_SUCH_ENTRY;
Matus Fabian704018c2017-09-04 02:17:18 -07001207
Matus Fabian704018c2017-09-04 02:17:18 -07001208 /* Free external address port */
Matus Fabian240b5ef2018-01-11 04:09:17 -08001209 if (!(sm->static_mapping_only || out2in_only))
Matus Fabianab395ec2018-09-20 23:18:41 -07001210 {
1211 for (i = 0; i < vec_len (sm->addresses); i++)
1212 {
1213 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1214 {
1215 a = sm->addresses + i;
1216 switch (proto)
1217 {
Matus Fabian704018c2017-09-04 02:17:18 -07001218#define _(N, j, n, s) \
1219 case SNAT_PROTOCOL_##N: \
1220 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
1221 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -07001222 { \
1223 a->busy_##n##_ports--; \
dongjuan58f50f12018-09-04 17:40:53 +08001224 a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001225 } \
Matus Fabian704018c2017-09-04 02:17:18 -07001226 break;
Matus Fabianab395ec2018-09-20 23:18:41 -07001227 foreach_snat_protocol
Matus Fabian704018c2017-09-04 02:17:18 -07001228#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -07001229 default:
1230 nat_log_info ("unknown protocol");
1231 return VNET_API_ERROR_INVALID_VALUE_2;
1232 }
1233 break;
1234 }
1235 }
1236 }
Matus Fabian704018c2017-09-04 02:17:18 -07001237
1238 m_key.addr = m->external_addr;
1239 m_key.port = m->external_port;
1240 m_key.protocol = m->proto;
Matus Fabian8008d7c2018-07-09 01:34:20 -07001241 m_key.fib_index = 0;
Matus Fabian704018c2017-09-04 02:17:18 -07001242 kv.key = m_key.as_u64;
Matus Fabianab395ec2018-09-20 23:18:41 -07001243 if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0))
1244 {
1245 nat_log_err ("static_mapping_by_external key del failed");
1246 return VNET_API_ERROR_UNSPECIFIED;
1247 }
Matus Fabian7865b5c2017-09-26 01:23:01 -07001248
Matus Fabianab395ec2018-09-20 23:18:41 -07001249 /* *INDENT-OFF* */
Matus Fabian704018c2017-09-04 02:17:18 -07001250 vec_foreach (local, m->locals)
1251 {
Matus Fabianc6c0d2a2018-07-19 22:45:25 -07001252 fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4,
1253 FIB_SOURCE_PLUGIN_LOW);
Matus Fabian704018c2017-09-04 02:17:18 -07001254 m_key.addr = local->addr;
Matus Fabian240b5ef2018-01-11 04:09:17 -08001255 if (!out2in_only)
Matus Fabian704018c2017-09-04 02:17:18 -07001256 {
Matus Fabian240b5ef2018-01-11 04:09:17 -08001257 m_key.port = local->port;
Matus Fabianc6c0d2a2018-07-19 22:45:25 -07001258 m_key.fib_index = local->fib_index;
Matus Fabian240b5ef2018-01-11 04:09:17 -08001259 kv.key = m_key.as_u64;
1260 if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
1261 {
Matus Fabian229c1aa2018-05-28 04:09:52 -07001262 nat_log_err ("static_mapping_by_local key del failed");
Matus Fabian240b5ef2018-01-11 04:09:17 -08001263 return VNET_API_ERROR_UNSPECIFIED;
1264 }
Matus Fabian704018c2017-09-04 02:17:18 -07001265 }
Matus Fabian7865b5c2017-09-26 01:23:01 -07001266
Matus Fabiana6110b62018-06-13 05:39:07 -07001267 if (sm->num_workers > 1)
Matus Fabian704018c2017-09-04 02:17:18 -07001268 {
Matus Fabiana6110b62018-06-13 05:39:07 -07001269 ip4_header_t ip = {
1270 .src_address = local->addr,
1271 };
1272 tsm = vec_elt_at_index (sm->per_thread_data,
1273 sm->worker_in2out_cb (&ip, m->fib_index));
Matus Fabian704018c2017-09-04 02:17:18 -07001274 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001275 else
1276 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
1277
Matus Fabianb932d262017-12-18 05:38:24 -08001278 /* Delete sessions */
1279 u_key.addr = local->addr;
1280 u_key.fib_index = m->fib_index;
1281 kv.key = u_key.as_u64;
1282 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
1283 {
1284 u = pool_elt_at_index (tsm->users, value.value);
1285 if (u->nstaticsessions)
1286 {
1287 head_index = u->sessions_per_user_list_head_index;
1288 head = pool_elt_at_index (tsm->list_pool, head_index);
1289 elt_index = head->next;
1290 elt = pool_elt_at_index (tsm->list_pool, elt_index);
1291 ses_index = elt->value;
1292 while (ses_index != ~0)
1293 {
1294 s = pool_elt_at_index (tsm->sessions, ses_index);
1295 elt = pool_elt_at_index (tsm->list_pool, elt->next);
1296 ses_index = elt->value;
1297
Matus Fabian13c08182018-04-11 00:36:57 -07001298 if (!(is_lb_session (s)))
1299 continue;
1300
shubing guo060c3a72018-08-10 13:59:50 +08001301 if ((s->in2out.addr.as_u32 != local->addr.as_u32) ||
Matus Fabianb932d262017-12-18 05:38:24 -08001302 (clib_net_to_host_u16 (s->in2out.port) != local->port))
1303 continue;
1304
1305 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
Matus Fabian229c1aa2018-05-28 04:09:52 -07001306 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
Matus Fabianb932d262017-12-18 05:38:24 -08001307 }
1308 }
1309 }
Matus Fabian704018c2017-09-04 02:17:18 -07001310 }
Matus Fabianab395ec2018-09-20 23:18:41 -07001311 /* *INDENT-ON* */
Matus Fabianea5b5be2018-09-03 05:02:23 -07001312 if (m->affinity)
Matus Fabianab395ec2018-09-20 23:18:41 -07001313 nat_affinity_flush_service (m->affinity_per_service_list_head_index);
1314 vec_free (m->locals);
1315 vec_free (m->tag);
1316 vec_free (m->workers);
Matus Fabian704018c2017-09-04 02:17:18 -07001317
1318 pool_put (sm->static_mappings, m);
1319 }
1320
1321 return 0;
1322}
1323
Matus Fabianb932d262017-12-18 05:38:24 -08001324int
Matus Fabianab395ec2018-09-20 23:18:41 -07001325snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm,
1326 u8 twice_nat)
Matus Fabian36532bd2017-01-23 23:42:28 -08001327{
1328 snat_address_t *a = 0;
1329 snat_session_t *ses;
1330 u32 *ses_to_be_removed = 0, *ses_index;
Matus Fabian36532bd2017-01-23 23:42:28 -08001331 snat_main_per_thread_data_t *tsm;
1332 snat_static_mapping_t *m;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001333 snat_interface_t *interface;
Matus Fabian36532bd2017-01-23 23:42:28 -08001334 int i;
Matus Fabianab395ec2018-09-20 23:18:41 -07001335 snat_address_t *addresses =
1336 twice_nat ? sm->twice_nat_addresses : sm->addresses;
Matus Fabian36532bd2017-01-23 23:42:28 -08001337
1338 /* Find SNAT address */
Matus Fabianab395ec2018-09-20 23:18:41 -07001339 for (i = 0; i < vec_len (addresses); i++)
Matus Fabian36532bd2017-01-23 23:42:28 -08001340 {
Matus Fabianb932d262017-12-18 05:38:24 -08001341 if (addresses[i].addr.as_u32 == addr.as_u32)
Matus Fabianab395ec2018-09-20 23:18:41 -07001342 {
1343 a = addresses + i;
1344 break;
1345 }
Matus Fabian36532bd2017-01-23 23:42:28 -08001346 }
1347 if (!a)
1348 return VNET_API_ERROR_NO_SUCH_ENTRY;
1349
1350 if (delete_sm)
1351 {
Matus Fabianab395ec2018-09-20 23:18:41 -07001352 /* *INDENT-OFF* */
Matus Fabian36532bd2017-01-23 23:42:28 -08001353 pool_foreach (m, sm->static_mappings,
1354 ({
1355 if (m->external_addr.as_u32 == addr.as_u32)
1356 (void) snat_add_static_mapping (m->local_addr, m->external_addr,
1357 m->local_port, m->external_port,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07001358 m->vrf_id, is_addr_only_static_mapping(m), ~0,
Matus Fabiane82488f2018-01-18 03:38:45 -08001359 m->proto, 0, m->twice_nat,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07001360 is_out2in_only_static_mapping(m), m->tag, is_identity_static_mapping(m));
Matus Fabian36532bd2017-01-23 23:42:28 -08001361 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001362 /* *INDENT-ON* */
Matus Fabian36532bd2017-01-23 23:42:28 -08001363 }
1364 else
1365 {
1366 /* Check if address is used in some static mapping */
Matus Fabianab395ec2018-09-20 23:18:41 -07001367 if (is_snat_address_used_in_static_mapping (sm, addr))
1368 {
1369 nat_log_notice ("address used in static mapping");
1370 return VNET_API_ERROR_UNSPECIFIED;
1371 }
Matus Fabian36532bd2017-01-23 23:42:28 -08001372 }
1373
Matus Fabianf8d84902017-07-23 23:41:03 -07001374 if (a->fib_index != ~0)
Matus Fabianab395ec2018-09-20 23:18:41 -07001375 fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW);
Matus Fabianf8d84902017-07-23 23:41:03 -07001376
Matus Fabian36532bd2017-01-23 23:42:28 -08001377 /* Delete sessions using address */
Matus Fabian09d96f42017-02-02 01:43:00 -08001378 if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
Matus Fabian36532bd2017-01-23 23:42:28 -08001379 {
Matus Fabianab395ec2018-09-20 23:18:41 -07001380 /* *INDENT-OFF* */
Matus Fabian36532bd2017-01-23 23:42:28 -08001381 vec_foreach (tsm, sm->per_thread_data)
1382 {
1383 pool_foreach (ses, tsm->sessions, ({
1384 if (ses->out2in.addr.as_u32 == addr.as_u32)
1385 {
Matus Fabianb932d262017-12-18 05:38:24 -08001386 nat_free_session_data (sm, ses, tsm - sm->per_thread_data);
Matus Fabianb932d262017-12-18 05:38:24 -08001387 vec_add1 (ses_to_be_removed, ses - tsm->sessions);
Matus Fabian36532bd2017-01-23 23:42:28 -08001388 }
1389 }));
1390
1391 vec_foreach (ses_index, ses_to_be_removed)
Matus Fabian229c1aa2018-05-28 04:09:52 -07001392 {
1393 ses = pool_elt_at_index (tsm->sessions, ses_index[0]);
1394 nat44_delete_session (sm, ses, tsm - sm->per_thread_data);
1395 }
Matus Fabian36532bd2017-01-23 23:42:28 -08001396
1397 vec_free (ses_to_be_removed);
Matus Fabianab395ec2018-09-20 23:18:41 -07001398 }
1399 /* *INDENT-ON* */
Matus Fabian36532bd2017-01-23 23:42:28 -08001400 }
1401
dongjuandf865202018-09-11 11:20:30 +00001402#define _(N, i, n, s) \
1403 clib_bitmap_free (a->busy_##n##_port_bitmap); \
1404 vec_free (a->busy_##n##_ports_per_thread);
1405 foreach_snat_protocol
1406#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -07001407 if (twice_nat)
Matus Fabianb932d262017-12-18 05:38:24 -08001408 {
1409 vec_del1 (sm->twice_nat_addresses, i);
1410 return 0;
1411 }
1412 else
1413 vec_del1 (sm->addresses, i);
Matus Fabian36532bd2017-01-23 23:42:28 -08001414
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001415 /* Delete external address from FIB */
Matus Fabianab395ec2018-09-20 23:18:41 -07001416 /* *INDENT-OFF* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001417 pool_foreach (interface, sm->interfaces,
1418 ({
Juraj Slobodacba69362017-12-19 02:09:32 +01001419 if (nat_interface_is_inside(interface) || sm->out2in_dpo)
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001420 continue;
1421
Matus Fabian066f0342017-02-10 03:48:01 -08001422 snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
Matus Fabiandccbee32017-01-31 22:20:30 -08001423 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001424 }));
Matus Fabian93d84c92017-07-19 08:06:01 -07001425 pool_foreach (interface, sm->output_feature_interfaces,
1426 ({
Juraj Slobodacba69362017-12-19 02:09:32 +01001427 if (nat_interface_is_inside(interface) || sm->out2in_dpo)
Matus Fabian93d84c92017-07-19 08:06:01 -07001428 continue;
1429
1430 snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
1431 break;
1432 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001433 /* *INDENT-ON* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001434
Matus Fabian36532bd2017-01-23 23:42:28 -08001435 return 0;
1436}
1437
Matus Fabianab395ec2018-09-20 23:18:41 -07001438int
1439snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
Matus Fabian588144a2016-10-24 03:30:00 -07001440{
1441 snat_main_t *sm = &snat_main;
1442 snat_interface_t *i;
Matus Fabianab395ec2018-09-20 23:18:41 -07001443 const char *feature_name, *del_feature_name;
1444 snat_address_t *ap;
1445 snat_static_mapping_t *m;
1446 snat_det_map_t *dm;
Matus Fabian8008d7c2018-07-09 01:34:20 -07001447 nat_outside_fib_t *outside_fib;
1448 u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
Matus Fabianab395ec2018-09-20 23:18:41 -07001449 sw_if_index);
Matus Fabian588144a2016-10-24 03:30:00 -07001450
Juraj Slobodacba69362017-12-19 02:09:32 +01001451 if (sm->out2in_dpo && !is_inside)
1452 return VNET_API_ERROR_UNSUPPORTED;
1453
Matus Fabianab395ec2018-09-20 23:18:41 -07001454 /* *INDENT-OFF* */
Matus Fabiane4e34c22018-03-07 03:17:57 -08001455 pool_foreach (i, sm->output_feature_interfaces,
1456 ({
1457 if (i->sw_if_index == sw_if_index)
1458 return VNET_API_ERROR_VALUE_EXIST;
1459 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001460 /* *INDENT-ON* */
Matus Fabiane4e34c22018-03-07 03:17:57 -08001461
Matus Fabian588144a2016-10-24 03:30:00 -07001462 if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
Matus Fabianab395ec2018-09-20 23:18:41 -07001463 feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast";
Matus Fabian588144a2016-10-24 03:30:00 -07001464 else
Matus Fabian475f0552016-10-19 06:17:52 -07001465 {
Matus Fabian066f0342017-02-10 03:48:01 -08001466 if (sm->num_workers > 1 && !sm->deterministic)
Matus Fabianab395ec2018-09-20 23:18:41 -07001467 feature_name =
1468 is_inside ? "nat44-in2out-worker-handoff" :
1469 "nat44-out2in-worker-handoff";
Matus Fabian066f0342017-02-10 03:48:01 -08001470 else if (sm->deterministic)
Matus Fabianab395ec2018-09-20 23:18:41 -07001471 feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in";
Matus Fabiana6110b62018-06-13 05:39:07 -07001472 else if (sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07001473 feature_name = is_inside ? "nat44-ed-in2out" : "nat44-ed-out2in";
Matus Fabian475f0552016-10-19 06:17:52 -07001474 else
Matus Fabianab395ec2018-09-20 23:18:41 -07001475 feature_name = is_inside ? "nat44-in2out" : "nat44-out2in";
Matus Fabian475f0552016-10-19 06:17:52 -07001476 }
Matus Fabian588144a2016-10-24 03:30:00 -07001477
Matus Fabian066f0342017-02-10 03:48:01 -08001478 if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1)
Matus Fabian229c1aa2018-05-28 04:09:52 -07001479 sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index,
Matus Fabianab395ec2018-09-20 23:18:41 -07001480 NAT_FQ_NELTS);
Matus Fabian475f0552016-10-19 06:17:52 -07001481
Matus Fabian066f0342017-02-10 03:48:01 -08001482 if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1)
Matus Fabian229c1aa2018-05-28 04:09:52 -07001483 sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index,
Matus Fabianab395ec2018-09-20 23:18:41 -07001484 NAT_FQ_NELTS);
Matus Fabian475f0552016-10-19 06:17:52 -07001485
Matus Fabian8008d7c2018-07-09 01:34:20 -07001486 if (!is_inside)
1487 {
Matus Fabianab395ec2018-09-20 23:18:41 -07001488 /* *INDENT-OFF* */
Matus Fabian8008d7c2018-07-09 01:34:20 -07001489 vec_foreach (outside_fib, sm->outside_fibs)
1490 {
1491 if (outside_fib->fib_index == fib_index)
1492 {
1493 if (is_del)
1494 {
1495 outside_fib->refcount--;
1496 if (!outside_fib->refcount)
1497 vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
1498 }
1499 else
1500 outside_fib->refcount++;
1501 goto feature_set;
1502 }
1503 }
Matus Fabianab395ec2018-09-20 23:18:41 -07001504 /* *INDENT-ON* */
Matus Fabian8008d7c2018-07-09 01:34:20 -07001505 if (!is_del)
Matus Fabianab395ec2018-09-20 23:18:41 -07001506 {
1507 vec_add2 (sm->outside_fibs, outside_fib, 1);
1508 outside_fib->refcount = 1;
1509 outside_fib->fib_index = fib_index;
1510 }
Matus Fabian8008d7c2018-07-09 01:34:20 -07001511 }
1512feature_set:
Matus Fabianab395ec2018-09-20 23:18:41 -07001513 /* *INDENT-OFF* */
Matus Fabian588144a2016-10-24 03:30:00 -07001514 pool_foreach (i, sm->interfaces,
1515 ({
1516 if (i->sw_if_index == sw_if_index)
1517 {
1518 if (is_del)
Matus Fabian36ea2d62017-10-24 04:13:49 -07001519 {
1520 if (nat_interface_is_inside(i) && nat_interface_is_outside(i))
1521 {
1522 if (is_inside)
1523 i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE;
1524 else
1525 i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE;
1526
1527 if (sm->num_workers > 1 && !sm->deterministic)
Milan Lenco2aa22902018-01-22 14:05:14 +01001528 {
1529 del_feature_name = "nat44-handoff-classify";
1530 feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
1531 "nat44-out2in-worker-handoff";
1532 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001533 else if (sm->deterministic)
Milan Lenco2aa22902018-01-22 14:05:14 +01001534 {
1535 del_feature_name = "nat44-det-classify";
1536 feature_name = !is_inside ? "nat44-det-in2out" :
1537 "nat44-det-out2in";
1538 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001539 else if (sm->endpoint_dependent)
1540 {
1541 del_feature_name = "nat44-ed-classify";
1542 feature_name = !is_inside ? "nat44-ed-in2out" :
1543 "nat44-ed-out2in";
1544 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001545 else
Milan Lenco2aa22902018-01-22 14:05:14 +01001546 {
1547 del_feature_name = "nat44-classify";
1548 feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in";
1549 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001550
1551 vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
1552 sw_if_index, 0, 0, 0);
1553 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1554 sw_if_index, 1, 0, 0);
Matus Fabian35dfedc2018-04-26 02:12:20 -07001555 if (!is_inside)
Matus Fabiana6110b62018-06-13 05:39:07 -07001556 {
1557 if (sm->endpoint_dependent)
1558 vnet_feature_enable_disable ("ip4-local",
1559 "nat44-ed-hairpinning",
1560 sw_if_index, 1, 0, 0);
Matus Fabian38bc3082018-08-09 05:15:19 -07001561 else if (!sm->deterministic)
Matus Fabiana6110b62018-06-13 05:39:07 -07001562 vnet_feature_enable_disable ("ip4-local",
1563 "nat44-hairpinning",
1564 sw_if_index, 1, 0, 0);
1565 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001566 }
1567 else
1568 {
1569 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1570 sw_if_index, 0, 0, 0);
1571 pool_put (sm->interfaces, i);
Matus Fabian35dfedc2018-04-26 02:12:20 -07001572 if (is_inside)
Matus Fabiana6110b62018-06-13 05:39:07 -07001573 {
1574 if (sm->endpoint_dependent)
1575 vnet_feature_enable_disable ("ip4-local",
1576 "nat44-ed-hairpinning",
1577 sw_if_index, 0, 0, 0);
Matus Fabian38bc3082018-08-09 05:15:19 -07001578 else if (!sm->deterministic)
Matus Fabiana6110b62018-06-13 05:39:07 -07001579 vnet_feature_enable_disable ("ip4-local",
1580 "nat44-hairpinning",
1581 sw_if_index, 0, 0, 0);
1582 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001583 }
1584 }
Matus Fabian588144a2016-10-24 03:30:00 -07001585 else
Matus Fabian36ea2d62017-10-24 04:13:49 -07001586 {
1587 if ((nat_interface_is_inside(i) && is_inside) ||
1588 (nat_interface_is_outside(i) && !is_inside))
1589 return 0;
1590
1591 if (sm->num_workers > 1 && !sm->deterministic)
1592 {
1593 del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
1594 "nat44-out2in-worker-handoff";
1595 feature_name = "nat44-handoff-classify";
1596 }
1597 else if (sm->deterministic)
1598 {
1599 del_feature_name = !is_inside ? "nat44-det-in2out" :
1600 "nat44-det-out2in";
1601 feature_name = "nat44-det-classify";
1602 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001603 else if (sm->endpoint_dependent)
1604 {
1605 del_feature_name = !is_inside ? "nat44-ed-in2out" :
1606 "nat44-ed-out2in";
1607 feature_name = "nat44-ed-classify";
1608 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001609 else
1610 {
1611 del_feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in";
1612 feature_name = "nat44-classify";
1613 }
1614
1615 vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
1616 sw_if_index, 0, 0, 0);
1617 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1618 sw_if_index, 1, 0, 0);
Matus Fabian35dfedc2018-04-26 02:12:20 -07001619 if (!is_inside)
Matus Fabiana6110b62018-06-13 05:39:07 -07001620 {
1621 if (sm->endpoint_dependent)
1622 vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning",
1623 sw_if_index, 0, 0, 0);
Matus Fabian38bc3082018-08-09 05:15:19 -07001624 else if (!sm->deterministic)
Matus Fabiana6110b62018-06-13 05:39:07 -07001625 vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
1626 sw_if_index, 0, 0, 0);
1627 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001628 goto set_flags;
1629 }
Matus Fabian588144a2016-10-24 03:30:00 -07001630
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001631 goto fib;
Matus Fabian588144a2016-10-24 03:30:00 -07001632 }
1633 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001634 /* *INDENT-ON* */
Matus Fabian588144a2016-10-24 03:30:00 -07001635
1636 if (is_del)
1637 return VNET_API_ERROR_NO_SUCH_ENTRY;
1638
1639 pool_get (sm->interfaces, i);
1640 i->sw_if_index = sw_if_index;
Matus Fabian36ea2d62017-10-24 04:13:49 -07001641 i->flags = 0;
Matus Fabianab395ec2018-09-20 23:18:41 -07001642 vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0,
1643 0);
Matus Fabian36ea2d62017-10-24 04:13:49 -07001644
Matus Fabian35dfedc2018-04-26 02:12:20 -07001645 if (is_inside && !sm->out2in_dpo)
Matus Fabiana6110b62018-06-13 05:39:07 -07001646 {
1647 if (sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07001648 vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning",
1649 sw_if_index, 1, 0, 0);
Matus Fabian38bc3082018-08-09 05:15:19 -07001650 else if (!sm->deterministic)
Matus Fabianab395ec2018-09-20 23:18:41 -07001651 vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
1652 sw_if_index, 1, 0, 0);
Matus Fabiana6110b62018-06-13 05:39:07 -07001653 }
Matus Fabian35dfedc2018-04-26 02:12:20 -07001654
Matus Fabian36ea2d62017-10-24 04:13:49 -07001655set_flags:
1656 if (is_inside)
Matus Fabian35dfedc2018-04-26 02:12:20 -07001657 {
1658 i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
1659 return 0;
1660 }
Matus Fabian36ea2d62017-10-24 04:13:49 -07001661 else
1662 i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
Matus Fabian588144a2016-10-24 03:30:00 -07001663
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001664 /* Add/delete external addresses to FIB */
1665fib:
Matus Fabianab395ec2018-09-20 23:18:41 -07001666 /* *INDENT-OFF* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001667 vec_foreach (ap, sm->addresses)
Matus Fabian066f0342017-02-10 03:48:01 -08001668 snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001669
1670 pool_foreach (m, sm->static_mappings,
1671 ({
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07001672 if (!(is_addr_only_static_mapping(m)) || (m->local_addr.as_u32 == m->external_addr.as_u32))
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001673 continue;
1674
Matus Fabian066f0342017-02-10 03:48:01 -08001675 snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
1676 }));
1677
1678 pool_foreach (dm, sm->det_maps,
1679 ({
1680 snat_add_del_addr_to_fib(&dm->out_addr, dm->out_plen, sw_if_index, !is_del);
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001681 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001682 /* *INDENT-ON* */
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001683
Matus Fabian588144a2016-10-24 03:30:00 -07001684 return 0;
1685}
1686
Matus Fabianab395ec2018-09-20 23:18:41 -07001687int
1688snat_interface_add_del_output_feature (u32 sw_if_index,
1689 u8 is_inside, int is_del)
Matus Fabian93d84c92017-07-19 08:06:01 -07001690{
1691 snat_main_t *sm = &snat_main;
1692 snat_interface_t *i;
Matus Fabianab395ec2018-09-20 23:18:41 -07001693 snat_address_t *ap;
1694 snat_static_mapping_t *m;
Matus Fabian93d84c92017-07-19 08:06:01 -07001695
1696 if (sm->deterministic ||
1697 (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)))
1698 return VNET_API_ERROR_UNSUPPORTED;
1699
Matus Fabianab395ec2018-09-20 23:18:41 -07001700 /* *INDENT-OFF* */
Matus Fabiane4e34c22018-03-07 03:17:57 -08001701 pool_foreach (i, sm->interfaces,
1702 ({
1703 if (i->sw_if_index == sw_if_index)
1704 return VNET_API_ERROR_VALUE_EXIST;
1705 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001706 /* *INDENT-ON* */
Matus Fabiane4e34c22018-03-07 03:17:57 -08001707
Matus Fabian93d84c92017-07-19 08:06:01 -07001708 if (is_inside)
Matus Fabian161c59c2017-07-21 03:46:03 -07001709 {
Matus Fabiana6110b62018-06-13 05:39:07 -07001710 if (sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07001711 {
1712 vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-hairpin-dst",
1713 sw_if_index, !is_del, 0, 0);
1714 vnet_feature_enable_disable ("ip4-output", "nat44-ed-hairpin-src",
1715 sw_if_index, !is_del, 0, 0);
1716 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001717 else
Matus Fabianab395ec2018-09-20 23:18:41 -07001718 {
1719 vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst",
1720 sw_if_index, !is_del, 0, 0);
1721 vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src",
1722 sw_if_index, !is_del, 0, 0);
1723 }
Matus Fabian161c59c2017-07-21 03:46:03 -07001724 goto fq;
1725 }
Matus Fabian93d84c92017-07-19 08:06:01 -07001726
1727 if (sm->num_workers > 1)
1728 {
Matus Fabiana6110b62018-06-13 05:39:07 -07001729 vnet_feature_enable_disable ("ip4-unicast",
Matus Fabianab395ec2018-09-20 23:18:41 -07001730 "nat44-out2in-worker-handoff",
1731 sw_if_index, !is_del, 0, 0);
Matus Fabian93d84c92017-07-19 08:06:01 -07001732 vnet_feature_enable_disable ("ip4-output",
Matus Fabianab395ec2018-09-20 23:18:41 -07001733 "nat44-in2out-output-worker-handoff",
1734 sw_if_index, !is_del, 0, 0);
Matus Fabian93d84c92017-07-19 08:06:01 -07001735 }
1736 else
1737 {
Matus Fabiana6110b62018-06-13 05:39:07 -07001738 if (sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07001739 {
1740 vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-out2in",
1741 sw_if_index, !is_del, 0, 0);
1742 vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output",
1743 sw_if_index, !is_del, 0, 0);
1744 }
Matus Fabiana6110b62018-06-13 05:39:07 -07001745 else
Matus Fabianab395ec2018-09-20 23:18:41 -07001746 {
1747 vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in",
1748 sw_if_index, !is_del, 0, 0);
1749 vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output",
1750 sw_if_index, !is_del, 0, 0);
1751 }
Matus Fabian93d84c92017-07-19 08:06:01 -07001752 }
1753
Matus Fabian161c59c2017-07-21 03:46:03 -07001754fq:
Matus Fabian93d84c92017-07-19 08:06:01 -07001755 if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
1756 sm->fq_in2out_output_index =
1757 vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
1758
1759 if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
Matus Fabianab395ec2018-09-20 23:18:41 -07001760 sm->fq_out2in_index =
1761 vlib_frame_queue_main_init (sm->out2in_node_index, 0);
Matus Fabian93d84c92017-07-19 08:06:01 -07001762
Matus Fabianab395ec2018-09-20 23:18:41 -07001763 /* *INDENT-OFF* */
Matus Fabian93d84c92017-07-19 08:06:01 -07001764 pool_foreach (i, sm->output_feature_interfaces,
1765 ({
1766 if (i->sw_if_index == sw_if_index)
1767 {
1768 if (is_del)
1769 pool_put (sm->output_feature_interfaces, i);
1770 else
1771 return VNET_API_ERROR_VALUE_EXIST;
1772
1773 goto fib;
1774 }
1775 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001776 /* *INDENT-ON* */
Matus Fabian93d84c92017-07-19 08:06:01 -07001777
1778 if (is_del)
1779 return VNET_API_ERROR_NO_SUCH_ENTRY;
1780
1781 pool_get (sm->output_feature_interfaces, i);
1782 i->sw_if_index = sw_if_index;
Matus Fabian36ea2d62017-10-24 04:13:49 -07001783 i->flags = 0;
1784 if (is_inside)
1785 i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
1786 else
1787 i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
Matus Fabian93d84c92017-07-19 08:06:01 -07001788
1789 /* Add/delete external addresses to FIB */
1790fib:
1791 if (is_inside)
1792 return 0;
1793
Matus Fabianab395ec2018-09-20 23:18:41 -07001794 /* *INDENT-OFF* */
Matus Fabian93d84c92017-07-19 08:06:01 -07001795 vec_foreach (ap, sm->addresses)
1796 snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
1797
1798 pool_foreach (m, sm->static_mappings,
1799 ({
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07001800 if (!((is_addr_only_static_mapping(m))) || (m->local_addr.as_u32 == m->external_addr.as_u32))
Matus Fabian93d84c92017-07-19 08:06:01 -07001801 continue;
1802
1803 snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
1804 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001805 /* *INDENT-ON* */
Matus Fabian93d84c92017-07-19 08:06:01 -07001806
1807 return 0;
1808}
1809
Matus Fabianab395ec2018-09-20 23:18:41 -07001810int
1811snat_set_workers (uword * bitmap)
Matus Fabian475f0552016-10-19 06:17:52 -07001812{
1813 snat_main_t *sm = &snat_main;
Matus Fabian7801ca22017-08-03 00:58:05 -07001814 int i, j = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07001815
1816 if (sm->num_workers < 2)
1817 return VNET_API_ERROR_FEATURE_DISABLED;
1818
1819 if (clib_bitmap_last_set (bitmap) >= sm->num_workers)
1820 return VNET_API_ERROR_INVALID_WORKER;
1821
1822 vec_free (sm->workers);
Matus Fabianab395ec2018-09-20 23:18:41 -07001823 /* *INDENT-OFF* */
Matus Fabian475f0552016-10-19 06:17:52 -07001824 clib_bitmap_foreach (i, bitmap,
1825 ({
1826 vec_add1(sm->workers, i);
Matus Fabian10491392018-01-05 05:03:35 -08001827 sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j;
Matus Fabian7801ca22017-08-03 00:58:05 -07001828 j++;
Matus Fabian475f0552016-10-19 06:17:52 -07001829 }));
Matus Fabianab395ec2018-09-20 23:18:41 -07001830 /* *INDENT-ON* */
Matus Fabian475f0552016-10-19 06:17:52 -07001831
Matus Fabian7801ca22017-08-03 00:58:05 -07001832 sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers);
1833 sm->num_snat_thread = _vec_len (sm->workers);
1834
Matus Fabian475f0552016-10-19 06:17:52 -07001835 return 0;
1836}
1837
Dave Barach8b275372017-01-16 10:54:02 -05001838
Dave Barachcab65ec2017-01-11 13:01:14 -05001839static void
1840snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
Matus Fabianab395ec2018-09-20 23:18:41 -07001841 uword opaque,
1842 u32 sw_if_index,
1843 ip4_address_t * address,
1844 u32 address_length,
1845 u32 if_address_index, u32 is_delete);
Dave Barachcab65ec2017-01-11 13:01:14 -05001846
Matus Fabian4772e7a2018-04-04 00:38:02 -07001847static void
1848nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im,
Matus Fabianab395ec2018-09-20 23:18:41 -07001849 uword opaque,
1850 u32 sw_if_index,
1851 ip4_address_t * address,
1852 u32 address_length,
1853 u32 if_address_index, u32 is_delete);
Matus Fabian4772e7a2018-04-04 00:38:02 -07001854
Matus Fabian27697102017-11-09 01:43:47 -08001855static int
1856nat_alloc_addr_and_port_default (snat_address_t * addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -07001857 u32 fib_index,
1858 u32 thread_index,
1859 snat_session_key_t * k,
1860 u16 port_per_thread, u32 snat_thread_index);
Matus Fabian27697102017-11-09 01:43:47 -08001861
Matus Fabianab395ec2018-09-20 23:18:41 -07001862static clib_error_t *
1863snat_init (vlib_main_t * vm)
Dave Barach20c02cb2016-06-26 10:42:08 -04001864{
Matus Fabianab395ec2018-09-20 23:18:41 -07001865 snat_main_t *sm = &snat_main;
1866 clib_error_t *error = 0;
1867 ip4_main_t *im = &ip4_main;
1868 ip_lookup_main_t *lm = &im->lookup_main;
Matus Fabian475f0552016-10-19 06:17:52 -07001869 uword *p;
1870 vlib_thread_registration_t *tr;
1871 vlib_thread_main_t *tm = vlib_get_thread_main ();
1872 uword *bitmap = 0;
1873 u32 i;
Dave Barachcab65ec2017-01-11 13:01:14 -05001874 ip4_add_del_interface_address_callback_t cb4;
Matus Fabianab395ec2018-09-20 23:18:41 -07001875 vlib_node_t *error_drop_node;
Dave Barach20c02cb2016-06-26 10:42:08 -04001876
Dave Barach20c02cb2016-06-26 10:42:08 -04001877 sm->vlib_main = vm;
Matus Fabianab395ec2018-09-20 23:18:41 -07001878 sm->vnet_main = vnet_get_main ();
Dave Barach20c02cb2016-06-26 10:42:08 -04001879 sm->ip4_main = im;
1880 sm->ip4_lookup_main = lm;
1881 sm->api_main = &api_main;
Matus Fabian475f0552016-10-19 06:17:52 -07001882 sm->first_worker_index = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07001883 sm->num_workers = 0;
Matus Fabian7801ca22017-08-03 00:58:05 -07001884 sm->num_snat_thread = 1;
Matus Fabian475f0552016-10-19 06:17:52 -07001885 sm->workers = 0;
Matus Fabian7801ca22017-08-03 00:58:05 -07001886 sm->port_per_thread = 0xffff - 1024;
Matus Fabian475f0552016-10-19 06:17:52 -07001887 sm->fq_in2out_index = ~0;
1888 sm->fq_out2in_index = ~0;
Matus Fabian6a0946f2017-04-12 03:36:13 -07001889 sm->udp_timeout = SNAT_UDP_TIMEOUT;
1890 sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
1891 sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
1892 sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
Matus Fabian27697102017-11-09 01:43:47 -08001893 sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
Matus Fabian5d28c7a2018-09-04 03:55:45 -07001894 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
Juraj Sloboda7b929792017-11-23 13:20:48 +01001895 sm->forwarding_enabled = 0;
Matus Fabian229c1aa2018-05-28 04:09:52 -07001896 sm->log_class = vlib_log_register_class ("nat", 0);
1897 error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
1898 sm->error_node_index = error_drop_node->index;
Matus Fabianbb4e0222018-09-13 02:36:25 -07001899 sm->mss_clamping = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07001900
1901 p = hash_get_mem (tm->thread_registrations_by_name, "workers");
1902 if (p)
1903 {
1904 tr = (vlib_thread_registration_t *) p[0];
1905 if (tr)
Matus Fabianab395ec2018-09-20 23:18:41 -07001906 {
1907 sm->num_workers = tr->count;
1908 sm->first_worker_index = tr->first_index;
1909 }
Matus Fabian475f0552016-10-19 06:17:52 -07001910 }
1911
Matus Fabian7801ca22017-08-03 00:58:05 -07001912 vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1);
1913
Matus Fabian475f0552016-10-19 06:17:52 -07001914 /* Use all available workers by default */
1915 if (sm->num_workers > 1)
1916 {
Matus Fabianab395ec2018-09-20 23:18:41 -07001917 for (i = 0; i < sm->num_workers; i++)
1918 bitmap = clib_bitmap_set (bitmap, i, 1);
1919 snat_set_workers (bitmap);
Matus Fabian475f0552016-10-19 06:17:52 -07001920 clib_bitmap_free (bitmap);
1921 }
Matus Fabian7801ca22017-08-03 00:58:05 -07001922 else
1923 {
1924 sm->per_thread_data[0].snat_thread_index = 0;
1925 }
Dave Barach20c02cb2016-06-26 10:42:08 -04001926
Matus Fabianab395ec2018-09-20 23:18:41 -07001927 error = snat_api_init (vm, sm);
Matus Fabian08ce4322017-06-19 05:28:27 -07001928 if (error)
1929 return error;
Dave Barach20c02cb2016-06-26 10:42:08 -04001930
Dave Barachcab65ec2017-01-11 13:01:14 -05001931 /* Set up the interface address add/del callback */
1932 cb4.function = snat_ip4_add_del_interface_address_cb;
1933 cb4.function_opaque = 0;
1934
1935 vec_add1 (im->add_del_interface_address_callbacks, cb4);
1936
Matus Fabian4772e7a2018-04-04 00:38:02 -07001937 cb4.function = nat_ip4_add_del_addr_only_sm_cb;
1938 cb4.function_opaque = 0;
1939
1940 vec_add1 (im->add_del_interface_address_callbacks, cb4);
1941
Juraj Slobodacba69362017-12-19 02:09:32 +01001942 nat_dpo_module_init ();
1943
Matus Fabianeea28d72017-01-13 04:15:54 -08001944 /* Init IPFIX logging */
Matus Fabianab395ec2018-09-20 23:18:41 -07001945 snat_ipfix_logging_init (vm);
Matus Fabianeea28d72017-01-13 04:15:54 -08001946
Matus Fabianefcd1e92017-08-15 06:59:19 -07001947 /* Init NAT64 */
Matus Fabianab395ec2018-09-20 23:18:41 -07001948 error = nat64_init (vm);
Matus Fabianefcd1e92017-08-15 06:59:19 -07001949 if (error)
1950 return error;
Matus Fabian06596c52017-06-06 04:53:28 -07001951
Matus Fabianab395ec2018-09-20 23:18:41 -07001952 dslite_init (vm);
Matus Fabian8ebe6252017-11-06 05:04:53 -08001953
Matus Fabianab395ec2018-09-20 23:18:41 -07001954 nat66_init ();
Matus Fabianf2a23cc2018-01-22 03:41:53 -08001955
Matus Fabianefcd1e92017-08-15 06:59:19 -07001956 /* Init virtual fragmenentation reassembly */
Matus Fabianab395ec2018-09-20 23:18:41 -07001957 return nat_reass_init (vm);
Dave Barach20c02cb2016-06-26 10:42:08 -04001958}
1959
1960VLIB_INIT_FUNCTION (snat_init);
1961
Matus Fabianab395ec2018-09-20 23:18:41 -07001962void
1963snat_free_outside_address_and_port (snat_address_t * addresses,
1964 u32 thread_index, snat_session_key_t * k)
Dave Barach20c02cb2016-06-26 10:42:08 -04001965{
1966 snat_address_t *a;
shubing guo762a4932018-08-13 17:16:46 +08001967 u32 address_index;
Dave Barach20c02cb2016-06-26 10:42:08 -04001968 u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
Matus Fabian2ba92e32017-08-21 07:05:03 -07001969
Matus Fabianab395ec2018-09-20 23:18:41 -07001970 for (address_index = 0; address_index < vec_len (addresses);
1971 address_index++)
shubing guo762a4932018-08-13 17:16:46 +08001972 {
1973 if (addresses[address_index].addr.as_u32 == k->addr.as_u32)
Matus Fabianab395ec2018-09-20 23:18:41 -07001974 break;
shubing guo762a4932018-08-13 17:16:46 +08001975 }
1976
Matus Fabian8ebe6252017-11-06 05:04:53 -08001977 ASSERT (address_index < vec_len (addresses));
Dave Barach20c02cb2016-06-26 10:42:08 -04001978
Matus Fabian8ebe6252017-11-06 05:04:53 -08001979 a = addresses + address_index;
Dave Barach20c02cb2016-06-26 10:42:08 -04001980
Matus Fabian09d96f42017-02-02 01:43:00 -08001981 switch (k->protocol)
1982 {
1983#define _(N, i, n, s) \
1984 case SNAT_PROTOCOL_##N: \
1985 ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
1986 port_host_byte_order) == 1); \
1987 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
1988 port_host_byte_order, 0); \
1989 a->busy_##n##_ports--; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001990 a->busy_##n##_ports_per_thread[thread_index]--; \
Matus Fabian09d96f42017-02-02 01:43:00 -08001991 break;
1992 foreach_snat_protocol
1993#undef _
1994 default:
Matus Fabian229c1aa2018-05-28 04:09:52 -07001995 nat_log_info ("unknown protocol");
Matus Fabian09d96f42017-02-02 01:43:00 -08001996 return;
1997 }
Matus Fabian2ba92e32017-08-21 07:05:03 -07001998}
Dave Barach20c02cb2016-06-26 10:42:08 -04001999
Matus Fabianab395ec2018-09-20 23:18:41 -07002000int
2001snat_static_mapping_match (snat_main_t * sm,
2002 snat_session_key_t match,
2003 snat_session_key_t * mapping,
2004 u8 by_external,
2005 u8 * is_addr_only,
2006 twice_nat_type_t * twice_nat,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002007 lb_nat_type_t * lb, ip4_address_t * ext_host_addr,
2008 u8 * is_identity_nat)
Matus Fabiandb649882016-08-26 05:45:27 -07002009{
2010 clib_bihash_kv_8_8_t kv, value;
2011 snat_static_mapping_t *m;
Matus Fabian09d96f42017-02-02 01:43:00 -08002012 snat_session_key_t m_key;
Matus Fabiandb649882016-08-26 05:45:27 -07002013 clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
Matus Fabian704018c2017-09-04 02:17:18 -07002014 u32 rand, lo = 0, hi, mid;
Matus Fabianea5b5be2018-09-03 05:02:23 -07002015 u8 backend_index;
Matus Fabiandb649882016-08-26 05:45:27 -07002016
Matus Fabian8008d7c2018-07-09 01:34:20 -07002017 m_key.fib_index = match.fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -07002018 if (by_external)
Matus Fabian8008d7c2018-07-09 01:34:20 -07002019 {
2020 mapping_hash = &sm->static_mapping_by_external;
2021 m_key.fib_index = 0;
2022 }
Matus Fabiandb649882016-08-26 05:45:27 -07002023
2024 m_key.addr = match.addr;
2025 m_key.port = clib_net_to_host_u16 (match.port);
Matus Fabian09d96f42017-02-02 01:43:00 -08002026 m_key.protocol = match.protocol;
Matus Fabiandb649882016-08-26 05:45:27 -07002027
2028 kv.key = m_key.as_u64;
2029
2030 if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
2031 {
2032 /* Try address only mapping */
2033 m_key.port = 0;
Matus Fabian09d96f42017-02-02 01:43:00 -08002034 m_key.protocol = 0;
Matus Fabiandb649882016-08-26 05:45:27 -07002035 kv.key = m_key.as_u64;
2036 if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
Matus Fabianab395ec2018-09-20 23:18:41 -07002037 return 1;
Matus Fabiandb649882016-08-26 05:45:27 -07002038 }
2039
2040 m = pool_elt_at_index (sm->static_mappings, value.value);
2041
2042 if (by_external)
2043 {
Matus Fabian704018c2017-09-04 02:17:18 -07002044 if (vec_len (m->locals))
Matus Fabianab395ec2018-09-20 23:18:41 -07002045 {
2046 if (PREDICT_FALSE (lb != 0))
2047 *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT;
2048 if (m->affinity)
2049 {
2050 if (nat_affinity_find_and_lock (ext_host_addr[0], match.addr,
2051 match.protocol, match.port,
2052 &backend_index))
2053 goto get_local;
Matus Fabianea5b5be2018-09-03 05:02:23 -07002054
Matus Fabianab395ec2018-09-20 23:18:41 -07002055 mapping->addr = m->locals[backend_index].addr;
2056 mapping->port =
2057 clib_host_to_net_u16 (m->locals[backend_index].port);
2058 mapping->fib_index = m->locals[backend_index].fib_index;
2059 goto end;
2060 }
2061 get_local:
2062 hi = vec_len (m->locals) - 1;
2063 rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix);
2064 while (lo < hi)
2065 {
2066 mid = ((hi - lo) >> 1) + lo;
2067 (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid);
2068 }
2069 if (!(m->locals[lo].prefix >= rand))
2070 return 1;
2071 if (PREDICT_FALSE (sm->num_workers > 1))
2072 {
2073 ip4_header_t ip = {
2074 .src_address = m->locals[lo].addr,
2075 };
2076 if (sm->worker_in2out_cb (&ip, m->fib_index) !=
2077 vlib_get_thread_index ())
2078 goto get_local;
2079 }
2080 mapping->addr = m->locals[lo].addr;
2081 mapping->port = clib_host_to_net_u16 (m->locals[lo].port);
2082 mapping->fib_index = m->locals[lo].fib_index;
2083 if (m->affinity)
2084 {
2085 if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr,
2086 match.protocol, match.port,
2087 lo, m->affinity,
2088 m->affinity_per_service_list_head_index))
2089 nat_log_info ("create affinity record failed");
2090 }
2091 }
Matus Fabian704018c2017-09-04 02:17:18 -07002092 else
Matus Fabianab395ec2018-09-20 23:18:41 -07002093 {
2094 if (PREDICT_FALSE (lb != 0))
2095 *lb = NO_LB_NAT;
2096 mapping->fib_index = m->fib_index;
2097 mapping->addr = m->local_addr;
2098 /* Address only mapping doesn't change port */
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002099 mapping->port = is_addr_only_static_mapping (m) ? match.port
Matus Fabianab395ec2018-09-20 23:18:41 -07002100 : clib_host_to_net_u16 (m->local_port);
2101 }
Matus Fabian704018c2017-09-04 02:17:18 -07002102 mapping->protocol = m->proto;
Matus Fabiandb649882016-08-26 05:45:27 -07002103 }
2104 else
2105 {
2106 mapping->addr = m->external_addr;
2107 /* Address only mapping doesn't change port */
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002108 mapping->port = is_addr_only_static_mapping (m) ? match.port
Matus Fabianab395ec2018-09-20 23:18:41 -07002109 : clib_host_to_net_u16 (m->external_port);
Matus Fabiandb649882016-08-26 05:45:27 -07002110 mapping->fib_index = sm->outside_fib_index;
2111 }
2112
Matus Fabianea5b5be2018-09-03 05:02:23 -07002113end:
Matus Fabianab395ec2018-09-20 23:18:41 -07002114 if (PREDICT_FALSE (is_addr_only != 0))
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002115 *is_addr_only = is_addr_only_static_mapping (m);
Juraj Slobodad3677682017-04-14 03:24:45 +02002116
Matus Fabianab395ec2018-09-20 23:18:41 -07002117 if (PREDICT_FALSE (twice_nat != 0))
Matus Fabianb932d262017-12-18 05:38:24 -08002118 *twice_nat = m->twice_nat;
2119
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002120 if (PREDICT_FALSE (is_identity_nat != 0))
2121 *is_identity_nat = is_identity_static_mapping (m);
2122
Matus Fabiandb649882016-08-26 05:45:27 -07002123 return 0;
2124}
2125
Matus Fabian7801ca22017-08-03 00:58:05 -07002126static_always_inline u16
Matus Fabian8ebe6252017-11-06 05:04:53 -08002127snat_random_port (u16 min, u16 max)
Matus Fabian7801ca22017-08-03 00:58:05 -07002128{
Matus Fabian8ebe6252017-11-06 05:04:53 -08002129 snat_main_t *sm = &snat_main;
Matus Fabian7801ca22017-08-03 00:58:05 -07002130 return min + random_u32 (&sm->random_seed) /
Matus Fabianab395ec2018-09-20 23:18:41 -07002131 (random_u32_max () / (max - min + 1) + 1);
Matus Fabian7801ca22017-08-03 00:58:05 -07002132}
2133
Matus Fabian27697102017-11-09 01:43:47 -08002134int
2135snat_alloc_outside_address_and_port (snat_address_t * addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -07002136 u32 fib_index,
2137 u32 thread_index,
2138 snat_session_key_t * k,
2139 u16 port_per_thread,
2140 u32 snat_thread_index)
Matus Fabian27697102017-11-09 01:43:47 -08002141{
2142 snat_main_t *sm = &snat_main;
2143
Matus Fabianab395ec2018-09-20 23:18:41 -07002144 return sm->alloc_addr_and_port (addresses, fib_index, thread_index, k,
2145 port_per_thread, snat_thread_index);
Matus Fabian27697102017-11-09 01:43:47 -08002146}
2147
2148static int
2149nat_alloc_addr_and_port_default (snat_address_t * addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -07002150 u32 fib_index,
2151 u32 thread_index,
2152 snat_session_key_t * k,
2153 u16 port_per_thread, u32 snat_thread_index)
Dave Barach20c02cb2016-06-26 10:42:08 -04002154{
Matus Fabianab395ec2018-09-20 23:18:41 -07002155 int i;
Matus Fabian51e759f2017-12-07 23:22:51 -08002156 snat_address_t *a, *ga = 0;
Dave Barach20c02cb2016-06-26 10:42:08 -04002157 u32 portnum;
2158
Matus Fabian8ebe6252017-11-06 05:04:53 -08002159 for (i = 0; i < vec_len (addresses); i++)
Dave Barach20c02cb2016-06-26 10:42:08 -04002160 {
Matus Fabian8ebe6252017-11-06 05:04:53 -08002161 a = addresses + i;
Matus Fabian09d96f42017-02-02 01:43:00 -08002162 switch (k->protocol)
Matus Fabianab395ec2018-09-20 23:18:41 -07002163 {
Matus Fabian09d96f42017-02-02 01:43:00 -08002164#define _(N, j, n, s) \
2165 case SNAT_PROTOCOL_##N: \
Matus Fabian8ebe6252017-11-06 05:04:53 -08002166 if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
Matus Fabian09d96f42017-02-02 01:43:00 -08002167 { \
Matus Fabian51e759f2017-12-07 23:22:51 -08002168 if (a->fib_index == fib_index) \
Matus Fabian09d96f42017-02-02 01:43:00 -08002169 { \
Matus Fabian51e759f2017-12-07 23:22:51 -08002170 while (1) \
2171 { \
2172 portnum = (port_per_thread * \
2173 snat_thread_index) + \
2174 snat_random_port(1, port_per_thread) + 1024; \
2175 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
2176 continue; \
2177 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
2178 a->busy_##n##_ports_per_thread[thread_index]++; \
2179 a->busy_##n##_ports++; \
2180 k->addr = a->addr; \
2181 k->port = clib_host_to_net_u16(portnum); \
Matus Fabian51e759f2017-12-07 23:22:51 -08002182 return 0; \
2183 } \
2184 } \
2185 else if (a->fib_index == ~0) \
2186 { \
2187 ga = a; \
Matus Fabian09d96f42017-02-02 01:43:00 -08002188 } \
2189 } \
2190 break;
Matus Fabianab395ec2018-09-20 23:18:41 -07002191 foreach_snat_protocol
Matus Fabian09d96f42017-02-02 01:43:00 -08002192#undef _
Matus Fabianab395ec2018-09-20 23:18:41 -07002193 default:
2194 nat_log_info ("unknown protocol");
2195 return 1;
2196 }
Matus Fabian09d96f42017-02-02 01:43:00 -08002197
Dave Barach20c02cb2016-06-26 10:42:08 -04002198 }
Matus Fabian51e759f2017-12-07 23:22:51 -08002199
2200 if (ga)
2201 {
2202 a = ga;
2203 switch (k->protocol)
2204 {
2205#define _(N, j, n, s) \
2206 case SNAT_PROTOCOL_##N: \
2207 while (1) \
2208 { \
2209 portnum = (port_per_thread * \
2210 snat_thread_index) + \
2211 snat_random_port(1, port_per_thread) + 1024; \
2212 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
2213 continue; \
2214 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
2215 a->busy_##n##_ports_per_thread[thread_index]++; \
2216 a->busy_##n##_ports++; \
2217 k->addr = a->addr; \
2218 k->port = clib_host_to_net_u16(portnum); \
Matus Fabian51e759f2017-12-07 23:22:51 -08002219 return 0; \
2220 }
2221 break;
2222 foreach_snat_protocol
2223#undef _
2224 default:
Matus Fabian229c1aa2018-05-28 04:09:52 -07002225 nat_log_info ("unknown protocol");
Matus Fabian51e759f2017-12-07 23:22:51 -08002226 return 1;
2227 }
2228 }
2229
Dave Barach20c02cb2016-06-26 10:42:08 -04002230 /* Totally out of translations to use... */
Matus Fabianab395ec2018-09-20 23:18:41 -07002231 snat_ipfix_logging_addresses_exhausted (0);
Dave Barach20c02cb2016-06-26 10:42:08 -04002232 return 1;
2233}
2234
Matus Fabian27697102017-11-09 01:43:47 -08002235static int
2236nat_alloc_addr_and_port_mape (snat_address_t * addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -07002237 u32 fib_index,
2238 u32 thread_index,
2239 snat_session_key_t * k,
2240 u16 port_per_thread, u32 snat_thread_index)
Matus Fabian27697102017-11-09 01:43:47 -08002241{
2242 snat_main_t *sm = &snat_main;
2243 snat_address_t *a = addresses;
2244 u16 m, ports, portnum, A, j;
2245 m = 16 - (sm->psid_offset + sm->psid_length);
2246 ports = (1 << (16 - sm->psid_length)) - (1 << m);
2247
2248 if (!vec_len (addresses))
2249 goto exhausted;
2250
2251 switch (k->protocol)
2252 {
2253#define _(N, i, n, s) \
2254 case SNAT_PROTOCOL_##N: \
2255 if (a->busy_##n##_ports < ports) \
2256 { \
2257 while (1) \
2258 { \
2259 A = snat_random_port(1, pow2_mask(sm->psid_offset)); \
2260 j = snat_random_port(0, pow2_mask(m)); \
2261 portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \
2262 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
2263 continue; \
2264 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
2265 a->busy_##n##_ports++; \
2266 k->addr = a->addr; \
2267 k->port = clib_host_to_net_u16 (portnum); \
Matus Fabian27697102017-11-09 01:43:47 -08002268 return 0; \
2269 } \
2270 } \
2271 break;
2272 foreach_snat_protocol
2273#undef _
2274 default:
Matus Fabian229c1aa2018-05-28 04:09:52 -07002275 nat_log_info ("unknown protocol");
Matus Fabian27697102017-11-09 01:43:47 -08002276 return 1;
2277 }
2278
2279exhausted:
2280 /* Totally out of translations to use... */
Matus Fabianab395ec2018-09-20 23:18:41 -07002281 snat_ipfix_logging_addresses_exhausted (0);
Matus Fabian27697102017-11-09 01:43:47 -08002282 return 1;
2283}
Dave Barach20c02cb2016-06-26 10:42:08 -04002284
Matus Fabian5d28c7a2018-09-04 03:55:45 -07002285static int
2286nat_alloc_addr_and_port_range (snat_address_t * addresses,
Matus Fabianab395ec2018-09-20 23:18:41 -07002287 u32 fib_index,
2288 u32 thread_index,
2289 snat_session_key_t * k,
2290 u16 port_per_thread, u32 snat_thread_index)
Matus Fabian5d28c7a2018-09-04 03:55:45 -07002291{
2292 snat_main_t *sm = &snat_main;
2293 snat_address_t *a = addresses;
2294 u16 portnum, ports;
2295
2296 ports = sm->end_port - sm->start_port + 1;
2297
2298 if (!vec_len (addresses))
2299 goto exhausted;
2300
2301 switch (k->protocol)
2302 {
2303#define _(N, i, n, s) \
2304 case SNAT_PROTOCOL_##N: \
2305 if (a->busy_##n##_ports < ports) \
2306 { \
2307 while (1) \
2308 { \
2309 portnum = snat_random_port(sm->start_port, sm->end_port); \
2310 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
2311 continue; \
2312 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
2313 a->busy_##n##_ports++; \
2314 k->addr = a->addr; \
2315 k->port = clib_host_to_net_u16 (portnum); \
Matus Fabian5d28c7a2018-09-04 03:55:45 -07002316 return 0; \
2317 } \
2318 } \
2319 break;
2320 foreach_snat_protocol
2321#undef _
2322 default:
2323 nat_log_info ("unknown protocol");
2324 return 1;
2325 }
2326
2327exhausted:
2328 /* Totally out of translations to use... */
Matus Fabianab395ec2018-09-20 23:18:41 -07002329 snat_ipfix_logging_addresses_exhausted (0);
Matus Fabian5d28c7a2018-09-04 03:55:45 -07002330 return 1;
2331}
2332
Juraj Slobodacba69362017-12-19 02:09:32 +01002333void
2334nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add)
2335{
2336 dpo_id_t dpo_v4 = DPO_INVALID;
2337 fib_prefix_t pfx = {
2338 .fp_proto = FIB_PROTOCOL_IP4,
2339 .fp_len = 32,
2340 .fp_addr.ip4.as_u32 = addr.as_u32,
2341 };
2342
2343 if (is_add)
2344 {
2345 nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4);
2346 fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI,
Matus Fabianab395ec2018-09-20 23:18:41 -07002347 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
Juraj Slobodacba69362017-12-19 02:09:32 +01002348 dpo_reset (&dpo_v4);
2349 }
2350 else
2351 {
2352 fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_PLUGIN_HI);
2353 }
2354}
2355
Matus Fabian229c1aa2018-05-28 04:09:52 -07002356u8 *
2357format_session_kvp (u8 * s, va_list * args)
2358{
2359 clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
2360 snat_session_key_t k;
2361
2362 k.as_u64 = v->key;
2363
2364 s = format (s, "%U session-index %llu", format_snat_key, &k, v->value);
2365
2366 return s;
2367}
2368
2369u8 *
2370format_static_mapping_kvp (u8 * s, va_list * args)
2371{
2372 clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
2373 snat_session_key_t k;
2374
2375 k.as_u64 = v->key;
2376
Matus Fabian878c6462018-08-23 00:33:35 -07002377 s = format (s, "%U static-mapping-index %llu",
Matus Fabianab395ec2018-09-20 23:18:41 -07002378 format_static_mapping_key, &k, v->value);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002379
2380 return s;
2381}
2382
2383u8 *
2384format_user_kvp (u8 * s, va_list * args)
2385{
2386 clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
2387 snat_user_key_t k;
2388
2389 k.as_u64 = v->key;
2390
2391 s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr,
Matus Fabianab395ec2018-09-20 23:18:41 -07002392 k.fib_index, v->value);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002393
2394 return s;
2395}
2396
2397u8 *
2398format_ed_session_kvp (u8 * s, va_list * args)
2399{
2400 clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *);
2401 nat_ed_ses_key_t k;
2402
2403 k.as_u64[0] = v->key[0];
2404 k.as_u64[1] = v->key[1];
2405
Matus Fabianab395ec2018-09-20 23:18:41 -07002406 s =
2407 format (s, "local %U:%d remote %U:%d proto %U fib %d session-index %llu",
2408 format_ip4_address, &k.l_addr, clib_net_to_host_u16 (k.l_port),
2409 format_ip4_address, &k.r_addr, clib_net_to_host_u16 (k.r_port),
2410 format_ip_protocol, k.proto, k.fib_index, v->value);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002411
2412 return s;
2413}
2414
Matus Fabian066f0342017-02-10 03:48:01 -08002415static u32
2416snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0)
2417{
2418 snat_main_t *sm = &snat_main;
Matus Fabian066f0342017-02-10 03:48:01 -08002419 u32 next_worker_index = 0;
Matus Fabian7865b5c2017-09-26 01:23:01 -07002420 u32 hash;
Matus Fabian066f0342017-02-10 03:48:01 -08002421
Matus Fabian7865b5c2017-09-26 01:23:01 -07002422 next_worker_index = sm->first_worker_index;
2423 hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
Matus Fabianab395ec2018-09-20 23:18:41 -07002424 (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
Matus Fabian066f0342017-02-10 03:48:01 -08002425
Matus Fabian7865b5c2017-09-26 01:23:01 -07002426 if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
2427 next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
Matus Fabian066f0342017-02-10 03:48:01 -08002428 else
Matus Fabian7865b5c2017-09-26 01:23:01 -07002429 next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
Matus Fabian066f0342017-02-10 03:48:01 -08002430
2431 return next_worker_index;
2432}
2433
2434static u32
2435snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
2436{
2437 snat_main_t *sm = &snat_main;
Matus Fabianed3c1602017-09-21 05:07:12 -07002438 udp_header_t *udp;
2439 u16 port;
2440 snat_session_key_t m_key;
2441 clib_bihash_kv_8_8_t kv, value;
2442 snat_static_mapping_t *m;
Matus Fabianed3c1602017-09-21 05:07:12 -07002443 u32 proto;
Matus Fabian10491392018-01-05 05:03:35 -08002444 u32 next_worker_index = 0;
Matus Fabian066f0342017-02-10 03:48:01 -08002445
Matus Fabianed3c1602017-09-21 05:07:12 -07002446 /* first try static mappings without port */
2447 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
Matus Fabian066f0342017-02-10 03:48:01 -08002448 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002449 m_key.addr = ip0->dst_address;
2450 m_key.port = 0;
2451 m_key.protocol = 0;
2452 m_key.fib_index = rx_fib_index0;
2453 kv.key = m_key.as_u64;
Matus Fabianab395ec2018-09-20 23:18:41 -07002454 if (!clib_bihash_search_8_8
2455 (&sm->static_mapping_by_external, &kv, &value))
2456 {
2457 m = pool_elt_at_index (sm->static_mappings, value.value);
2458 return m->workers[0];
2459 }
Matus Fabian066f0342017-02-10 03:48:01 -08002460 }
2461
Matus Fabianed3c1602017-09-21 05:07:12 -07002462 proto = ip_proto_to_snat_proto (ip0->protocol);
2463 udp = ip4_next_header (ip0);
2464 port = udp->dst_port;
Matus Fabian066f0342017-02-10 03:48:01 -08002465
Matus Fabian51e759f2017-12-07 23:22:51 -08002466 if (PREDICT_FALSE (ip4_is_fragment (ip0)))
2467 {
2468 if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
2469 return vlib_get_thread_index ();
2470
2471 if (PREDICT_TRUE (!ip4_is_first_fragment (ip0)))
2472 {
2473 nat_reass_ip4_t *reass;
2474
2475 reass = nat_ip4_reass_find (ip0->src_address, ip0->dst_address,
2476 ip0->fragment_id, ip0->protocol);
2477
2478 if (reass && (reass->thread_index != (u32) ~ 0))
Matus Fabianab395ec2018-09-20 23:18:41 -07002479 return reass->thread_index;
Matus Fabian51e759f2017-12-07 23:22:51 -08002480 else
2481 return vlib_get_thread_index ();
2482 }
2483 }
2484
Matus Fabianed3c1602017-09-21 05:07:12 -07002485 /* unknown protocol */
2486 if (PREDICT_FALSE (proto == ~0))
Matus Fabian066f0342017-02-10 03:48:01 -08002487 {
Matus Fabiana6110b62018-06-13 05:39:07 -07002488 /* use current thread */
Matus Fabianed3c1602017-09-21 05:07:12 -07002489 return vlib_get_thread_index ();
2490 }
2491
2492 if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
2493 {
Matus Fabianab395ec2018-09-20 23:18:41 -07002494 icmp46_header_t *icmp = (icmp46_header_t *) udp;
2495 icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
Matus Fabianed3c1602017-09-21 05:07:12 -07002496 if (!icmp_is_error_message (icmp))
Matus Fabianab395ec2018-09-20 23:18:41 -07002497 port = echo->identifier;
Matus Fabian066f0342017-02-10 03:48:01 -08002498 else
Matus Fabianab395ec2018-09-20 23:18:41 -07002499 {
2500 ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
2501 proto = ip_proto_to_snat_proto (inner_ip->protocol);
2502 void *l4_header = ip4_next_header (inner_ip);
2503 switch (proto)
2504 {
2505 case SNAT_PROTOCOL_ICMP:
2506 icmp = (icmp46_header_t *) l4_header;
2507 echo = (icmp_echo_header_t *) (icmp + 1);
2508 port = echo->identifier;
2509 break;
2510 case SNAT_PROTOCOL_UDP:
2511 case SNAT_PROTOCOL_TCP:
2512 port = ((tcp_udp_header_t *) l4_header)->src_port;
2513 break;
2514 default:
2515 return vlib_get_thread_index ();
2516 }
2517 }
Matus Fabian066f0342017-02-10 03:48:01 -08002518 }
Matus Fabian066f0342017-02-10 03:48:01 -08002519
Matus Fabianed3c1602017-09-21 05:07:12 -07002520 /* try static mappings with port */
2521 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
2522 {
2523 m_key.addr = ip0->dst_address;
2524 m_key.port = clib_net_to_host_u16 (port);
2525 m_key.protocol = proto;
2526 m_key.fib_index = rx_fib_index0;
2527 kv.key = m_key.as_u64;
Matus Fabianab395ec2018-09-20 23:18:41 -07002528 if (!clib_bihash_search_8_8
2529 (&sm->static_mapping_by_external, &kv, &value))
2530 {
2531 m = pool_elt_at_index (sm->static_mappings, value.value);
2532 return m->workers[0];
2533 }
Matus Fabianed3c1602017-09-21 05:07:12 -07002534 }
2535
2536 /* worker by outside port */
Matus Fabian10491392018-01-05 05:03:35 -08002537 next_worker_index = sm->first_worker_index;
2538 next_worker_index +=
2539 sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
2540 return next_worker_index;
Matus Fabian066f0342017-02-10 03:48:01 -08002541}
2542
Matus Fabiana6110b62018-06-13 05:39:07 -07002543static u32
2544nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
2545{
2546 snat_main_t *sm = &snat_main;
2547 clib_bihash_kv_8_8_t kv, value;
2548 u32 proto, next_worker_index = 0;
2549 udp_header_t *udp;
2550 u16 port;
2551 snat_static_mapping_t *m;
2552 u32 hash;
2553
2554 /* first try static mappings without port */
2555 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
2556 {
2557 make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0);
Matus Fabianab395ec2018-09-20 23:18:41 -07002558 if (!clib_bihash_search_8_8
2559 (&sm->static_mapping_by_external, &kv, &value))
2560 {
2561 m = pool_elt_at_index (sm->static_mappings, value.value);
2562 return m->workers[0];
2563 }
Matus Fabiana6110b62018-06-13 05:39:07 -07002564 }
2565
2566 proto = ip_proto_to_snat_proto (ip->protocol);
2567
2568 /* unknown protocol */
2569 if (PREDICT_FALSE (proto == ~0))
2570 {
2571 /* use current thread */
2572 return vlib_get_thread_index ();
2573 }
2574
2575 udp = ip4_next_header (ip);
2576 port = udp->dst_port;
2577
2578 if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP))
2579 {
Matus Fabianab395ec2018-09-20 23:18:41 -07002580 icmp46_header_t *icmp = (icmp46_header_t *) udp;
2581 icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
Matus Fabiana6110b62018-06-13 05:39:07 -07002582 if (!icmp_is_error_message (icmp))
Matus Fabianab395ec2018-09-20 23:18:41 -07002583 port = echo->identifier;
Matus Fabiana6110b62018-06-13 05:39:07 -07002584 else
Matus Fabianab395ec2018-09-20 23:18:41 -07002585 {
2586 ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
2587 proto = ip_proto_to_snat_proto (inner_ip->protocol);
2588 void *l4_header = ip4_next_header (inner_ip);
2589 switch (proto)
2590 {
2591 case SNAT_PROTOCOL_ICMP:
2592 icmp = (icmp46_header_t *) l4_header;
2593 echo = (icmp_echo_header_t *) (icmp + 1);
2594 port = echo->identifier;
2595 break;
2596 case SNAT_PROTOCOL_UDP:
2597 case SNAT_PROTOCOL_TCP:
2598 port = ((tcp_udp_header_t *) l4_header)->src_port;
2599 break;
2600 default:
2601 return vlib_get_thread_index ();
2602 }
2603 }
Matus Fabiana6110b62018-06-13 05:39:07 -07002604 }
2605
2606 /* try static mappings with port */
2607 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
2608 {
2609 make_sm_kv (&kv, &ip->dst_address, proto, rx_fib_index,
Matus Fabianab395ec2018-09-20 23:18:41 -07002610 clib_net_to_host_u16 (port));
2611 if (!clib_bihash_search_8_8
2612 (&sm->static_mapping_by_external, &kv, &value))
2613 {
2614 m = pool_elt_at_index (sm->static_mappings, value.value);
2615 if (!vec_len (m->locals))
2616 return m->workers[0];
Matus Fabiana6110b62018-06-13 05:39:07 -07002617
Matus Fabianab395ec2018-09-20 23:18:41 -07002618 hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
2619 (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
Matus Fabiana6110b62018-06-13 05:39:07 -07002620
Matus Fabianab395ec2018-09-20 23:18:41 -07002621 if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers))))
2622 return m->workers[hash & (_vec_len (m->workers) - 1)];
2623 else
2624 return m->workers[hash % _vec_len (m->workers)];
2625 }
Matus Fabiana6110b62018-06-13 05:39:07 -07002626 }
2627
2628 /* worker by outside port */
2629 next_worker_index = sm->first_worker_index;
2630 next_worker_index +=
2631 sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
2632
2633 return next_worker_index;
2634}
2635
Matus Fabianeea28d72017-01-13 04:15:54 -08002636static clib_error_t *
Dave Barach20c02cb2016-06-26 10:42:08 -04002637snat_config (vlib_main_t * vm, unformat_input_t * input)
2638{
Matus Fabianab395ec2018-09-20 23:18:41 -07002639 snat_main_t *sm = &snat_main;
2640 nat66_main_t *nm = &nat66_main;
Dave Barach20c02cb2016-06-26 10:42:08 -04002641 u32 translation_buckets = 1024;
Matus Fabianab395ec2018-09-20 23:18:41 -07002642 u32 translation_memory_size = 128 << 20;
Dave Barach20c02cb2016-06-26 10:42:08 -04002643 u32 user_buckets = 128;
Matus Fabianab395ec2018-09-20 23:18:41 -07002644 u32 user_memory_size = 64 << 20;
Dave Barach20c02cb2016-06-26 10:42:08 -04002645 u32 max_translations_per_user = 100;
2646 u32 outside_vrf_id = 0;
Juraj Sloboda9341e342018-04-13 12:00:46 +02002647 u32 outside_ip6_vrf_id = 0;
Matus Fabiandb649882016-08-26 05:45:27 -07002648 u32 inside_vrf_id = 0;
2649 u32 static_mapping_buckets = 1024;
Matus Fabianab395ec2018-09-20 23:18:41 -07002650 u32 static_mapping_memory_size = 64 << 20;
Matus Fabian51e759f2017-12-07 23:22:51 -08002651 u32 nat64_bib_buckets = 1024;
2652 u32 nat64_bib_memory_size = 128 << 20;
2653 u32 nat64_st_buckets = 2048;
2654 u32 nat64_st_memory_size = 256 << 20;
Matus Fabiandb649882016-08-26 05:45:27 -07002655 u8 static_mapping_only = 0;
2656 u8 static_mapping_connection_tracking = 0;
Matus Fabian092b3cd2017-09-19 05:42:38 -07002657 snat_main_per_thread_data_t *tsm;
Matus Fabianab395ec2018-09-20 23:18:41 -07002658 dslite_main_t *dm = &dslite_main;
Dave Barach20c02cb2016-06-26 10:42:08 -04002659
Matus Fabian066f0342017-02-10 03:48:01 -08002660 sm->deterministic = 0;
Juraj Slobodacba69362017-12-19 02:09:32 +01002661 sm->out2in_dpo = 0;
Matus Fabiana6110b62018-06-13 05:39:07 -07002662 sm->endpoint_dependent = 0;
Matus Fabian066f0342017-02-10 03:48:01 -08002663
Dave Barach20c02cb2016-06-26 10:42:08 -04002664 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2665 {
Matus Fabianab395ec2018-09-20 23:18:41 -07002666 if (unformat
2667 (input, "translation hash buckets %d", &translation_buckets))
2668 ;
Dave Barach20c02cb2016-06-26 10:42:08 -04002669 else if (unformat (input, "translation hash memory %d",
Matus Fabianab395ec2018-09-20 23:18:41 -07002670 &translation_memory_size));
Dave Barach20c02cb2016-06-26 10:42:08 -04002671 else if (unformat (input, "user hash buckets %d", &user_buckets))
Matus Fabianab395ec2018-09-20 23:18:41 -07002672 ;
2673 else if (unformat (input, "user hash memory %d", &user_memory_size))
2674 ;
Dave Barach20c02cb2016-06-26 10:42:08 -04002675 else if (unformat (input, "max translations per user %d",
Matus Fabianab395ec2018-09-20 23:18:41 -07002676 &max_translations_per_user))
2677 ;
2678 else if (unformat (input, "outside VRF id %d", &outside_vrf_id))
2679 ;
2680 else if (unformat (input, "outside ip6 VRF id %d", &outside_ip6_vrf_id))
2681 ;
2682 else if (unformat (input, "inside VRF id %d", &inside_vrf_id))
2683 ;
Matus Fabiandb649882016-08-26 05:45:27 -07002684 else if (unformat (input, "static mapping only"))
Matus Fabianab395ec2018-09-20 23:18:41 -07002685 {
2686 static_mapping_only = 1;
2687 if (unformat (input, "connection tracking"))
2688 static_mapping_connection_tracking = 1;
2689 }
Matus Fabian066f0342017-02-10 03:48:01 -08002690 else if (unformat (input, "deterministic"))
Matus Fabianab395ec2018-09-20 23:18:41 -07002691 sm->deterministic = 1;
Matus Fabian51e759f2017-12-07 23:22:51 -08002692 else if (unformat (input, "nat64 bib hash buckets %d",
Matus Fabianab395ec2018-09-20 23:18:41 -07002693 &nat64_bib_buckets))
2694 ;
Matus Fabian51e759f2017-12-07 23:22:51 -08002695 else if (unformat (input, "nat64 bib hash memory %d",
Matus Fabianab395ec2018-09-20 23:18:41 -07002696 &nat64_bib_memory_size))
2697 ;
2698 else
2699 if (unformat (input, "nat64 st hash buckets %d", &nat64_st_buckets))
2700 ;
Matus Fabian51e759f2017-12-07 23:22:51 -08002701 else if (unformat (input, "nat64 st hash memory %d",
Matus Fabianab395ec2018-09-20 23:18:41 -07002702 &nat64_st_memory_size))
2703 ;
Juraj Slobodacba69362017-12-19 02:09:32 +01002704 else if (unformat (input, "out2in dpo"))
Matus Fabianab395ec2018-09-20 23:18:41 -07002705 sm->out2in_dpo = 1;
Juraj Slobodac5c6a332018-01-09 16:08:32 +01002706 else if (unformat (input, "dslite ce"))
Matus Fabianab395ec2018-09-20 23:18:41 -07002707 dslite_set_ce (dm, 1);
Matus Fabiana6110b62018-06-13 05:39:07 -07002708 else if (unformat (input, "endpoint-dependent"))
Matus Fabianab395ec2018-09-20 23:18:41 -07002709 sm->endpoint_dependent = 1;
Matus Fabian066f0342017-02-10 03:48:01 -08002710 else
Matus Fabiandb649882016-08-26 05:45:27 -07002711 return clib_error_return (0, "unknown input '%U'",
Dave Barach20c02cb2016-06-26 10:42:08 -04002712 format_unformat_error, input);
2713 }
2714
Matus Fabian69ce30d2018-08-22 01:27:10 -07002715 if (sm->deterministic && sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07002716 return clib_error_return (0,
2717 "deterministic and endpoint-dependent modes are mutually exclusive");
Matus Fabian69ce30d2018-08-22 01:27:10 -07002718
2719 if (static_mapping_only && (sm->deterministic || sm->endpoint_dependent))
Matus Fabianab395ec2018-09-20 23:18:41 -07002720 return clib_error_return (0,
2721 "static mapping only mode available only for simple nat");
Matus Fabian69ce30d2018-08-22 01:27:10 -07002722
2723 if (sm->out2in_dpo && (sm->deterministic || sm->endpoint_dependent))
Matus Fabianab395ec2018-09-20 23:18:41 -07002724 return clib_error_return (0,
2725 "out2in dpo mode available only for simple nat");
Matus Fabian69ce30d2018-08-22 01:27:10 -07002726
Dave Barach20c02cb2016-06-26 10:42:08 -04002727 /* for show commands, etc. */
2728 sm->translation_buckets = translation_buckets;
2729 sm->translation_memory_size = translation_memory_size;
Matus Fabian41fef502017-09-22 02:43:05 -07002730 /* do not exceed load factor 10 */
2731 sm->max_translations = 10 * translation_buckets;
Dave Barach20c02cb2016-06-26 10:42:08 -04002732 sm->user_buckets = user_buckets;
2733 sm->user_memory_size = user_memory_size;
2734 sm->max_translations_per_user = max_translations_per_user;
2735 sm->outside_vrf_id = outside_vrf_id;
Matus Fabian31c31aa2017-02-05 22:45:57 -08002736 sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
Matus Fabianab395ec2018-09-20 23:18:41 -07002737 outside_vrf_id,
2738 FIB_SOURCE_PLUGIN_HI);
Juraj Sloboda9341e342018-04-13 12:00:46 +02002739 nm->outside_vrf_id = outside_ip6_vrf_id;
2740 nm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
Matus Fabianab395ec2018-09-20 23:18:41 -07002741 outside_ip6_vrf_id,
2742 FIB_SOURCE_PLUGIN_HI);
Matus Fabiandb649882016-08-26 05:45:27 -07002743 sm->inside_vrf_id = inside_vrf_id;
Matus Fabian31c31aa2017-02-05 22:45:57 -08002744 sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
Matus Fabianab395ec2018-09-20 23:18:41 -07002745 inside_vrf_id,
2746 FIB_SOURCE_PLUGIN_HI);
Matus Fabiandb649882016-08-26 05:45:27 -07002747 sm->static_mapping_only = static_mapping_only;
2748 sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
Dave Barach20c02cb2016-06-26 10:42:08 -04002749
Matus Fabianab395ec2018-09-20 23:18:41 -07002750 nat64_set_hash (nat64_bib_buckets, nat64_bib_memory_size, nat64_st_buckets,
2751 nat64_st_memory_size);
Matus Fabian51e759f2017-12-07 23:22:51 -08002752
Matus Fabian066f0342017-02-10 03:48:01 -08002753 if (sm->deterministic)
Matus Fabiandb649882016-08-26 05:45:27 -07002754 {
Matus Fabian066f0342017-02-10 03:48:01 -08002755 sm->in2out_node_index = snat_det_in2out_node.index;
Matus Fabian93d84c92017-07-19 08:06:01 -07002756 sm->in2out_output_node_index = ~0;
Matus Fabian066f0342017-02-10 03:48:01 -08002757 sm->out2in_node_index = snat_det_out2in_node.index;
Juraj Sloboda7a1bde02017-04-03 08:43:58 +02002758 sm->icmp_match_in2out_cb = icmp_match_in2out_det;
2759 sm->icmp_match_out2in_cb = icmp_match_out2in_det;
Matus Fabiandb649882016-08-26 05:45:27 -07002760 }
Matus Fabian066f0342017-02-10 03:48:01 -08002761 else
2762 {
Matus Fabiana6110b62018-06-13 05:39:07 -07002763 if (sm->endpoint_dependent)
Matus Fabianab395ec2018-09-20 23:18:41 -07002764 {
2765 sm->worker_in2out_cb = snat_get_worker_in2out_cb;
2766 sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb;
2767 sm->in2out_node_index = nat44_ed_in2out_node.index;
2768 sm->in2out_output_node_index = nat44_ed_in2out_output_node.index;
2769 sm->out2in_node_index = nat44_ed_out2in_node.index;
2770 sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
2771 sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
2772 nat_affinity_init (vm);
2773 }
Matus Fabiana6110b62018-06-13 05:39:07 -07002774 else
Matus Fabianab395ec2018-09-20 23:18:41 -07002775 {
2776 sm->worker_in2out_cb = snat_get_worker_in2out_cb;
2777 sm->worker_out2in_cb = snat_get_worker_out2in_cb;
2778 sm->in2out_node_index = snat_in2out_node.index;
2779 sm->in2out_output_node_index = snat_in2out_output_node.index;
2780 sm->out2in_node_index = snat_out2in_node.index;
2781 sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
2782 sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
2783 }
Matus Fabian066f0342017-02-10 03:48:01 -08002784 if (!static_mapping_only ||
Matus Fabianab395ec2018-09-20 23:18:41 -07002785 (static_mapping_only && static_mapping_connection_tracking))
2786 {
2787 /* *INDENT-OFF* */
Matus Fabian092b3cd2017-09-19 05:42:38 -07002788 vec_foreach (tsm, sm->per_thread_data)
2789 {
Matus Fabiana6110b62018-06-13 05:39:07 -07002790 if (sm->endpoint_dependent)
2791 {
2792 clib_bihash_init_16_8 (&tsm->in2out_ed, "in2out-ed",
2793 translation_buckets,
2794 translation_memory_size);
2795 clib_bihash_set_kvp_format_fn_16_8 (&tsm->in2out_ed,
2796 format_ed_session_kvp);
Matus Fabian092b3cd2017-09-19 05:42:38 -07002797
Matus Fabiana6110b62018-06-13 05:39:07 -07002798 clib_bihash_init_16_8 (&tsm->out2in_ed, "out2in-ed",
2799 translation_buckets,
2800 translation_memory_size);
2801 clib_bihash_set_kvp_format_fn_16_8 (&tsm->out2in_ed,
2802 format_ed_session_kvp);
2803 }
2804 else
2805 {
2806 clib_bihash_init_8_8 (&tsm->in2out, "in2out",
2807 translation_buckets,
2808 translation_memory_size);
2809 clib_bihash_set_kvp_format_fn_8_8 (&tsm->in2out,
2810 format_session_kvp);
2811
2812 clib_bihash_init_8_8 (&tsm->out2in, "out2in",
2813 translation_buckets,
2814 translation_memory_size);
2815 clib_bihash_set_kvp_format_fn_8_8 (&tsm->out2in,
2816 format_session_kvp);
2817 }
Matus Fabian092b3cd2017-09-19 05:42:38 -07002818
2819 clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets,
2820 user_memory_size);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002821 clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash,
2822 format_user_kvp);
Matus Fabian092b3cd2017-09-19 05:42:38 -07002823 }
Matus Fabianab395ec2018-09-20 23:18:41 -07002824 /* *INDENT-ON* */
Matus Fabian092b3cd2017-09-19 05:42:38 -07002825
Matus Fabianab395ec2018-09-20 23:18:41 -07002826 }
Juraj Sloboda557a71c2017-02-22 05:16:06 -08002827 else
Matus Fabianab395ec2018-09-20 23:18:41 -07002828 {
2829 sm->icmp_match_in2out_cb = icmp_match_in2out_fast;
2830 sm->icmp_match_out2in_cb = icmp_match_out2in_fast;
2831 }
Matus Fabian066f0342017-02-10 03:48:01 -08002832 clib_bihash_init_8_8 (&sm->static_mapping_by_local,
Matus Fabianab395ec2018-09-20 23:18:41 -07002833 "static_mapping_by_local", static_mapping_buckets,
2834 static_mapping_memory_size);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002835 clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local,
Matus Fabianab395ec2018-09-20 23:18:41 -07002836 format_static_mapping_kvp);
Matus Fabian066f0342017-02-10 03:48:01 -08002837
2838 clib_bihash_init_8_8 (&sm->static_mapping_by_external,
Matus Fabianab395ec2018-09-20 23:18:41 -07002839 "static_mapping_by_external",
2840 static_mapping_buckets,
2841 static_mapping_memory_size);
Matus Fabian229c1aa2018-05-28 04:09:52 -07002842 clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external,
Matus Fabianab395ec2018-09-20 23:18:41 -07002843 format_static_mapping_kvp);
Matus Fabian066f0342017-02-10 03:48:01 -08002844 }
2845
Dave Barach20c02cb2016-06-26 10:42:08 -04002846 return 0;
2847}
2848
Matus Fabian2ba92e32017-08-21 07:05:03 -07002849VLIB_CONFIG_FUNCTION (snat_config, "nat");
Dave Barach20c02cb2016-06-26 10:42:08 -04002850
Dave Barachcab65ec2017-01-11 13:01:14 -05002851static void
Matus Fabian4772e7a2018-04-04 00:38:02 -07002852nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im,
Matus Fabianab395ec2018-09-20 23:18:41 -07002853 uword opaque,
2854 u32 sw_if_index,
2855 ip4_address_t * address,
2856 u32 address_length,
2857 u32 if_address_index, u32 is_delete)
Matus Fabian4772e7a2018-04-04 00:38:02 -07002858{
2859 snat_main_t *sm = &snat_main;
2860 snat_static_map_resolve_t *rp;
2861 snat_static_mapping_t *m;
2862 snat_session_key_t m_key;
2863 clib_bihash_kv_8_8_t kv, value;
2864 int i, rv;
2865 ip4_address_t l_addr;
2866
2867 for (i = 0; i < vec_len (sm->to_resolve); i++)
2868 {
2869 rp = sm->to_resolve + i;
2870 if (rp->addr_only == 0)
Matus Fabianab395ec2018-09-20 23:18:41 -07002871 continue;
Matus Fabian4772e7a2018-04-04 00:38:02 -07002872 if (rp->sw_if_index == sw_if_index)
Matus Fabianab395ec2018-09-20 23:18:41 -07002873 goto match;
Matus Fabian4772e7a2018-04-04 00:38:02 -07002874 }
2875
2876 return;
2877
2878match:
2879 m_key.addr.as_u32 = address->as_u32;
2880 m_key.port = rp->addr_only ? 0 : rp->e_port;
2881 m_key.protocol = rp->addr_only ? 0 : rp->proto;
2882 m_key.fib_index = sm->outside_fib_index;
2883 kv.key = m_key.as_u64;
2884 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
2885 m = 0;
2886 else
2887 m = pool_elt_at_index (sm->static_mappings, value.value);
2888
2889 if (!is_delete)
2890 {
2891 /* Don't trip over lease renewal, static config */
2892 if (m)
Matus Fabianab395ec2018-09-20 23:18:41 -07002893 return;
Matus Fabian4772e7a2018-04-04 00:38:02 -07002894 }
2895 else
2896 {
2897 if (!m)
Matus Fabianab395ec2018-09-20 23:18:41 -07002898 return;
Matus Fabian4772e7a2018-04-04 00:38:02 -07002899 }
2900
2901 /* Indetity mapping? */
2902 if (rp->l_addr.as_u32 == 0)
2903 l_addr.as_u32 = address[0].as_u32;
2904 else
2905 l_addr.as_u32 = rp->l_addr.as_u32;
2906 /* Add the static mapping */
2907 rv = snat_add_static_mapping (l_addr,
Matus Fabianab395ec2018-09-20 23:18:41 -07002908 address[0],
2909 rp->l_port,
2910 rp->e_port,
2911 rp->vrf_id,
2912 rp->addr_only, ~0 /* sw_if_index */ ,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002913 rp->proto, !is_delete, rp->twice_nat,
2914 rp->out2in_only, rp->tag, rp->identity_nat);
Matus Fabian4772e7a2018-04-04 00:38:02 -07002915 if (rv)
Matus Fabian229c1aa2018-05-28 04:09:52 -07002916 nat_log_notice ("snat_add_static_mapping returned %d", rv);
Matus Fabian4772e7a2018-04-04 00:38:02 -07002917}
2918
2919static void
Dave Barachcab65ec2017-01-11 13:01:14 -05002920snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
Matus Fabianab395ec2018-09-20 23:18:41 -07002921 uword opaque,
2922 u32 sw_if_index,
2923 ip4_address_t * address,
2924 u32 address_length,
2925 u32 if_address_index, u32 is_delete)
Dave Barachcab65ec2017-01-11 13:01:14 -05002926{
2927 snat_main_t *sm = &snat_main;
Dave Barach8b275372017-01-16 10:54:02 -05002928 snat_static_map_resolve_t *rp;
Matus Fabianab7a8052017-11-28 04:29:41 -08002929 ip4_address_t l_addr;
Dave Barachcab65ec2017-01-11 13:01:14 -05002930 int i, j;
Dave Barach8b275372017-01-16 10:54:02 -05002931 int rv;
Matus Fabianb932d262017-12-18 05:38:24 -08002932 u8 twice_nat = 0;
2933 snat_address_t *addresses = sm->addresses;
Dave Barachcab65ec2017-01-11 13:01:14 -05002934
Matus Fabianab395ec2018-09-20 23:18:41 -07002935 for (i = 0; i < vec_len (sm->auto_add_sw_if_indices); i++)
Dave Barachcab65ec2017-01-11 13:01:14 -05002936 {
2937 if (sw_if_index == sm->auto_add_sw_if_indices[i])
Matus Fabianab395ec2018-09-20 23:18:41 -07002938 goto match;
Matus Fabianb932d262017-12-18 05:38:24 -08002939 }
Dave Barachcab65ec2017-01-11 13:01:14 -05002940
Matus Fabianab395ec2018-09-20 23:18:41 -07002941 for (i = 0; i < vec_len (sm->auto_add_sw_if_indices_twice_nat); i++)
Matus Fabianb932d262017-12-18 05:38:24 -08002942 {
2943 twice_nat = 1;
2944 addresses = sm->twice_nat_addresses;
2945 if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i])
Matus Fabianab395ec2018-09-20 23:18:41 -07002946 goto match;
Matus Fabianb932d262017-12-18 05:38:24 -08002947 }
2948
2949 return;
2950
2951match:
2952 if (!is_delete)
2953 {
2954 /* Don't trip over lease renewal, static config */
Matus Fabianab395ec2018-09-20 23:18:41 -07002955 for (j = 0; j < vec_len (addresses); j++)
2956 if (addresses[j].addr.as_u32 == address->as_u32)
2957 return;
Matus Fabianb932d262017-12-18 05:38:24 -08002958
Matus Fabiana6110b62018-06-13 05:39:07 -07002959 (void) snat_add_address (sm, address, ~0, twice_nat);
Matus Fabianb932d262017-12-18 05:38:24 -08002960 /* Scan static map resolution vector */
2961 for (j = 0; j < vec_len (sm->to_resolve); j++)
Matus Fabianab395ec2018-09-20 23:18:41 -07002962 {
2963 rp = sm->to_resolve + j;
2964 if (rp->addr_only)
2965 continue;
2966 /* On this interface? */
2967 if (rp->sw_if_index == sw_if_index)
2968 {
2969 /* Indetity mapping? */
2970 if (rp->l_addr.as_u32 == 0)
2971 l_addr.as_u32 = address[0].as_u32;
2972 else
2973 l_addr.as_u32 = rp->l_addr.as_u32;
2974 /* Add the static mapping */
2975 rv = snat_add_static_mapping (l_addr,
2976 address[0],
2977 rp->l_port,
2978 rp->e_port,
2979 rp->vrf_id,
2980 rp->addr_only,
2981 ~0 /* sw_if_index */ ,
2982 rp->proto,
Matus Fabiane2f4e2f2018-10-07 21:28:23 -07002983 rp->is_add, rp->twice_nat,
2984 rp->out2in_only, rp->tag,
2985 rp->identity_nat);
Matus Fabianab395ec2018-09-20 23:18:41 -07002986 if (rv)
2987 nat_log_notice ("snat_add_static_mapping returned %d", rv);
2988 }
2989 }
Matus Fabianb932d262017-12-18 05:38:24 -08002990 return;
2991 }
2992 else
2993 {
Matus Fabianab395ec2018-09-20 23:18:41 -07002994 (void) snat_del_address (sm, address[0], 1, twice_nat);
Matus Fabianb932d262017-12-18 05:38:24 -08002995 return;
Dave Barachcab65ec2017-01-11 13:01:14 -05002996 }
2997}
2998
2999
Matus Fabianab395ec2018-09-20 23:18:41 -07003000int
3001snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del,
3002 u8 twice_nat)
Dave Barachcab65ec2017-01-11 13:01:14 -05003003{
Matus Fabianab395ec2018-09-20 23:18:41 -07003004 ip4_main_t *ip4_main = sm->ip4_main;
3005 ip4_address_t *first_int_addr;
Matus Fabian36532bd2017-01-23 23:42:28 -08003006 snat_static_map_resolve_t *rp;
3007 u32 *indices_to_delete = 0;
3008 int i, j;
Matus Fabianb932d262017-12-18 05:38:24 -08003009 u32 *auto_add_sw_if_indices =
Matus Fabianab395ec2018-09-20 23:18:41 -07003010 twice_nat ? sm->
3011 auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices;
Dave Barachcab65ec2017-01-11 13:01:14 -05003012
Matus Fabianab395ec2018-09-20 23:18:41 -07003013 first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address */
3014 );
Dave Barachcab65ec2017-01-11 13:01:14 -05003015
Matus Fabianab395ec2018-09-20 23:18:41 -07003016 for (i = 0; i < vec_len (auto_add_sw_if_indices); i++)
Dave Barachcab65ec2017-01-11 13:01:14 -05003017 {
Matus Fabianb932d262017-12-18 05:38:24 -08003018 if (auto_add_sw_if_indices[i] == sw_if_index)
Matus Fabianab395ec2018-09-20 23:18:41 -07003019 {
3020 if (is_del)
3021 {
3022 /* if have address remove it */
3023 if (first_int_addr)
3024 (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat);
3025 else
3026 {
3027 for (j = 0; j < vec_len (sm->to_resolve); j++)
3028 {
3029 rp = sm->to_resolve + j;
3030 if (rp->sw_if_index == sw_if_index)
3031 vec_add1 (indices_to_delete, j);
3032 }
3033 if (vec_len (indices_to_delete))
3034 {
3035 for (j = vec_len (indices_to_delete) - 1; j >= 0; j--)
3036 vec_del1 (sm->to_resolve, j);
3037 vec_free (indices_to_delete);
3038 }
3039 }
3040 if (twice_nat)
3041 vec_del1 (sm->auto_add_sw_if_indices_twice_nat, i);
3042 else
3043 vec_del1 (sm->auto_add_sw_if_indices, i);
3044 }
3045 else
3046 return VNET_API_ERROR_VALUE_EXIST;
Matus Fabian8bf68e82017-01-12 04:24:35 -08003047
Matus Fabianab395ec2018-09-20 23:18:41 -07003048 return 0;
3049 }
Dave Barachcab65ec2017-01-11 13:01:14 -05003050 }
Matus Fabian2ba92e32017-08-21 07:05:03 -07003051
Matus Fabian8bf68e82017-01-12 04:24:35 -08003052 if (is_del)
3053 return VNET_API_ERROR_NO_SUCH_ENTRY;
3054
Dave Barachcab65ec2017-01-11 13:01:14 -05003055 /* add to the auto-address list */
Matus Fabianb932d262017-12-18 05:38:24 -08003056 if (twice_nat)
Matus Fabianab395ec2018-09-20 23:18:41 -07003057 vec_add1 (sm->auto_add_sw_if_indices_twice_nat, sw_if_index);
Matus Fabianb932d262017-12-18 05:38:24 -08003058 else
Matus Fabianab395ec2018-09-20 23:18:41 -07003059 vec_add1 (sm->auto_add_sw_if_indices, sw_if_index);
Dave Barachcab65ec2017-01-11 13:01:14 -05003060
3061 /* If the address is already bound - or static - add it now */
3062 if (first_int_addr)
Matus Fabianab395ec2018-09-20 23:18:41 -07003063 (void) snat_add_address (sm, first_int_addr, ~0, twice_nat);
Dave Barachcab65ec2017-01-11 13:01:14 -05003064
3065 return 0;
3066}
3067
Matus Fabian5ba86f72017-10-26 03:37:38 -07003068int
Matus Fabianab395ec2018-09-20 23:18:41 -07003069nat44_del_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
3070 snat_protocol_t proto, u32 vrf_id, int is_in)
Matus Fabian5ba86f72017-10-26 03:37:38 -07003071{
3072 snat_main_per_thread_data_t *tsm;
3073 clib_bihash_kv_8_8_t kv, value;
3074 ip4_header_t ip;
3075 u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
3076 snat_session_key_t key;
3077 snat_session_t *s;
3078 clib_bihash_8_8_t *t;
Matus Fabian5ba86f72017-10-26 03:37:38 -07003079
Matus Fabiana6110b62018-06-13 05:39:07 -07003080 if (sm->endpoint_dependent)
3081 return VNET_API_ERROR_UNSUPPORTED;
3082
Matus Fabian5ba86f72017-10-26 03:37:38 -07003083 ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
Matus Fabian4888b502018-03-27 01:07:25 -07003084 if (sm->num_workers > 1)
Matus Fabian5ba86f72017-10-26 03:37:38 -07003085 tsm =
3086 vec_elt_at_index (sm->per_thread_data,
3087 sm->worker_in2out_cb (&ip, fib_index));
3088 else
3089 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
3090
3091 key.addr.as_u32 = addr->as_u32;
3092 key.port = clib_host_to_net_u16 (port);
3093 key.protocol = proto;
3094 key.fib_index = fib_index;
3095 kv.key = key.as_u64;
3096 t = is_in ? &tsm->in2out : &tsm->out2in;
3097 if (!clib_bihash_search_8_8 (t, &kv, &value))
3098 {
Matus Fabian70a26ac2018-05-14 06:20:28 -07003099 if (pool_is_free_index (tsm->sessions, value.value))
Matus Fabianab395ec2018-09-20 23:18:41 -07003100 return VNET_API_ERROR_UNSPECIFIED;
Matus Fabian70a26ac2018-05-14 06:20:28 -07003101
Matus Fabian5ba86f72017-10-26 03:37:38 -07003102 s = pool_elt_at_index (tsm->sessions, value.value);
Matus Fabian229c1aa2018-05-28 04:09:52 -07003103 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
3104 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
Matus Fabian5ba86f72017-10-26 03:37:38 -07003105 return 0;
3106 }
3107
3108 return VNET_API_ERROR_NO_SUCH_ENTRY;
3109}
3110
Matus Fabian70a26ac2018-05-14 06:20:28 -07003111int
Matus Fabianab395ec2018-09-20 23:18:41 -07003112nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
3113 ip4_address_t * eh_addr, u16 eh_port, u8 proto,
3114 u32 vrf_id, int is_in)
Matus Fabian70a26ac2018-05-14 06:20:28 -07003115{
3116 ip4_header_t ip;
3117 clib_bihash_16_8_t *t;
3118 nat_ed_ses_key_t key;
3119 clib_bihash_kv_16_8_t kv, value;
Matus Fabian70a26ac2018-05-14 06:20:28 -07003120 u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
3121 snat_session_t *s;
Matus Fabiana6110b62018-06-13 05:39:07 -07003122 snat_main_per_thread_data_t *tsm;
3123
3124 if (!sm->endpoint_dependent)
3125 return VNET_API_ERROR_FEATURE_DISABLED;
Matus Fabian70a26ac2018-05-14 06:20:28 -07003126
3127 ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
3128 if (sm->num_workers > 1)
Matus Fabiana6110b62018-06-13 05:39:07 -07003129 tsm =
3130 vec_elt_at_index (sm->per_thread_data,
3131 sm->worker_in2out_cb (&ip, fib_index));
Matus Fabian70a26ac2018-05-14 06:20:28 -07003132 else
Matus Fabiana6110b62018-06-13 05:39:07 -07003133 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
Matus Fabian70a26ac2018-05-14 06:20:28 -07003134
Matus Fabiana6110b62018-06-13 05:39:07 -07003135 t = is_in ? &tsm->in2out_ed : &tsm->out2in_ed;
Matus Fabian70a26ac2018-05-14 06:20:28 -07003136 key.l_addr.as_u32 = addr->as_u32;
3137 key.r_addr.as_u32 = eh_addr->as_u32;
3138 key.l_port = clib_host_to_net_u16 (port);
3139 key.r_port = clib_host_to_net_u16 (eh_port);
3140 key.proto = proto;
Matus Fabianab395ec2018-09-20 23:18:41 -07003141 key.fib_index = fib_index;
Matus Fabian70a26ac2018-05-14 06:20:28 -07003142 kv.key[0] = key.as_u64[0];
3143 kv.key[1] = key.as_u64[1];
3144 if (clib_bihash_search_16_8 (t, &kv, &value))
3145 return VNET_API_ERROR_NO_SUCH_ENTRY;
3146
Matus Fabiana6110b62018-06-13 05:39:07 -07003147 if (pool_is_free_index (tsm->sessions, value.value))
Matus Fabian70a26ac2018-05-14 06:20:28 -07003148 return VNET_API_ERROR_UNSPECIFIED;
Matus Fabiana6110b62018-06-13 05:39:07 -07003149 s = pool_elt_at_index (tsm->sessions, value.value);
3150 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
3151 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
Matus Fabian70a26ac2018-05-14 06:20:28 -07003152 return 0;
3153}
3154
Matus Fabian82119542018-01-25 01:13:22 -08003155void
3156nat_set_alloc_addr_and_port_mape (u16 psid, u16 psid_offset, u16 psid_length)
Matus Fabian5ba86f72017-10-26 03:37:38 -07003157{
3158 snat_main_t *sm = &snat_main;
Matus Fabian5ba86f72017-10-26 03:37:38 -07003159
Matus Fabian5d28c7a2018-09-04 03:55:45 -07003160 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
Matus Fabian82119542018-01-25 01:13:22 -08003161 sm->alloc_addr_and_port = nat_alloc_addr_and_port_mape;
3162 sm->psid = psid;
3163 sm->psid_offset = psid_offset;
3164 sm->psid_length = psid_length;
Matus Fabian5ba86f72017-10-26 03:37:38 -07003165}
3166
Matus Fabian82119542018-01-25 01:13:22 -08003167void
Matus Fabian5d28c7a2018-09-04 03:55:45 -07003168nat_set_alloc_addr_and_port_range (u16 start_port, u16 end_port)
3169{
3170 snat_main_t *sm = &snat_main;
3171
3172 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
3173 sm->alloc_addr_and_port = nat_alloc_addr_and_port_range;
3174 sm->start_port = start_port;
3175 sm->end_port = end_port;
3176}
3177
3178void
Matus Fabian82119542018-01-25 01:13:22 -08003179nat_set_alloc_addr_and_port_default (void)
Matus Fabian27697102017-11-09 01:43:47 -08003180{
3181 snat_main_t *sm = &snat_main;
Matus Fabian27697102017-11-09 01:43:47 -08003182
Matus Fabian5d28c7a2018-09-04 03:55:45 -07003183 sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
Matus Fabian82119542018-01-25 01:13:22 -08003184 sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
Matus Fabian066f0342017-02-10 03:48:01 -08003185}
3186
Matus Fabianab395ec2018-09-20 23:18:41 -07003187/*
3188 * fd.io coding-style-patch-verification: ON
3189 *
3190 * Local Variables:
3191 * eval: (c-set-style "gnu")
3192 * End:
3193 */