aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/nat/nat.c')
-rw-r--r--src/plugins/nat/nat.c602
1 files changed, 425 insertions, 177 deletions
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 81af143ab2c..245689db45d 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -37,6 +37,7 @@
#include <nat/nat44-ei/nat44_ei.h>
#include <vpp/app/version.h>
+#include <nat/lib/nat_inlines.h>
snat_main_t snat_main;
@@ -152,11 +153,6 @@ VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
.node_name = "nat44-hairpin-dst",
.runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
};
-VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_dst, static) = {
- .arc_name = "ip4-unicast",
- .node_name = "nat44-ed-hairpin-dst",
- .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
-};
/* Hook up output features */
VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
@@ -186,12 +182,6 @@ VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = {
.runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
.runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
};
-VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_src, static) = {
- .arc_name = "ip4-output",
- .node_name = "nat44-ed-hairpin-src",
- .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
- .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
-};
/* Hook up ip4-local features */
VNET_FEATURE_INIT (ip4_nat_hairpinning, static) =
@@ -200,13 +190,6 @@ VNET_FEATURE_INIT (ip4_nat_hairpinning, static) =
.node_name = "nat44-hairpinning",
.runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
};
-VNET_FEATURE_INIT (ip4_nat44_ed_hairpinning, static) =
-{
- .arc_name = "ip4-local",
- .node_name = "nat44-ed-hairpinning",
- .runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
-};
-
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
@@ -275,13 +258,13 @@ format_ed_session_kvp (u8 * s, va_list * args)
u32 fib_index;
split_ed_kv (v, &l_addr, &r_addr, &proto, &fib_index, &l_port, &r_port);
- s =
- format (s,
- "local %U:%d remote %U:%d proto %U fib %d thread-index %u session-index %u",
- format_ip4_address, &l_addr, clib_net_to_host_u16 (l_port),
- format_ip4_address, &r_addr, clib_net_to_host_u16 (r_port),
- format_ip_protocol, proto, fib_index,
- ed_value_get_session_index (v), ed_value_get_thread_index (v));
+ s = format (s,
+ "local %U:%d remote %U:%d proto %U fib %d thread-index %u "
+ "session-index %u",
+ format_ip4_address, &l_addr, clib_net_to_host_u16 (l_port),
+ format_ip4_address, &r_addr, clib_net_to_host_u16 (r_port),
+ format_ip_protocol, proto, fib_index,
+ ed_value_get_thread_index (v), ed_value_get_session_index (v));
return s;
}
@@ -291,39 +274,22 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index,
u8 is_ha)
{
clib_bihash_kv_8_8_t kv;
- u8 proto;
- u16 r_port, l_port;
- ip4_address_t *l_addr, *r_addr;
- u32 fib_index = 0;
- clib_bihash_kv_16_8_t ed_kv;
snat_main_per_thread_data_t *tsm =
vec_elt_at_index (sm->per_thread_data, thread_index);
if (is_ed_session (s))
{
per_vrf_sessions_unregister_session (s, thread_index);
+
+ if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 0))
+ nat_elog_warn ("flow hash del failed");
+
+ if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0))
+ nat_elog_warn ("flow hash del failed");
}
if (is_fwd_bypass_session (s))
{
- if (snat_is_unk_proto_session (s))
- {
- init_ed_k (&ed_kv, s->in2out.addr, 0, s->ext_host_addr, 0, 0,
- s->in2out.port);
- }
- else
- {
- l_port = s->in2out.port;
- r_port = s->ext_host_port;
- l_addr = &s->in2out.addr;
- r_addr = &s->ext_host_addr;
- proto = nat_proto_to_ip_proto (s->nat_proto);
- fib_index = s->in2out.fib_index;
- init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index,
- proto);
- }
- if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
- nat_elog_warn ("in2out_ed key del failed");
return;
}
@@ -333,36 +299,6 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index,
if (is_affinity_sessions (s))
nat_affinity_unlock (s->ext_host_addr, s->out2in.addr,
s->nat_proto, s->out2in.port);
- l_addr = &s->out2in.addr;
- r_addr = &s->ext_host_addr;
- fib_index = s->out2in.fib_index;
- if (snat_is_unk_proto_session (s))
- {
- proto = s->in2out.port;
- r_port = 0;
- l_port = 0;
- }
- else
- {
- proto = nat_proto_to_ip_proto (s->nat_proto);
- l_port = s->out2in.port;
- r_port = s->ext_host_port;
- }
- init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
- if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0))
- nat_elog_warn ("out2in_ed key del failed");
- l_addr = &s->in2out.addr;
- fib_index = s->in2out.fib_index;
- if (!snat_is_unk_proto_session (s))
- l_port = s->in2out.port;
- if (is_twice_nat_session (s))
- {
- r_addr = &s->ext_host_nat_addr;
- r_port = s->ext_host_nat_port;
- }
- init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
- if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
- nat_elog_warn ("in2out_ed key del failed");
if (!is_ha)
nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
@@ -1985,18 +1921,14 @@ feature_set:
sw_if_index, 1, 0, 0);
if (!is_inside)
{
- if (sm->endpoint_dependent)
- vnet_feature_enable_disable ("ip4-local",
- "nat44-ed-hairpinning",
- sw_if_index, 1, 0, 0);
- else
- vnet_feature_enable_disable ("ip4-local",
- "nat44-hairpinning",
- sw_if_index, 1, 0, 0);
- }
- }
- else
- {
+ if (!sm->endpoint_dependent)
+ vnet_feature_enable_disable ("ip4-local",
+ "nat44-hairpinning",
+ sw_if_index, 1, 0, 0);
+ }
+ }
+ else
+ {
int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
if (rv)
return rv;
@@ -2005,41 +1937,38 @@ feature_set:
pool_put (sm->interfaces, i);
if (is_inside)
{
- if (sm->endpoint_dependent)
- vnet_feature_enable_disable ("ip4-local",
- "nat44-ed-hairpinning",
- sw_if_index, 0, 0, 0);
- else
- vnet_feature_enable_disable ("ip4-local",
- "nat44-hairpinning",
- sw_if_index, 0, 0, 0);
- }
- }
- }
- else
- {
- if ((nat_interface_is_inside(i) && is_inside) ||
- (nat_interface_is_outside(i) && !is_inside))
- return 0;
+ if (!sm->endpoint_dependent)
+ vnet_feature_enable_disable ("ip4-local",
+ "nat44-hairpinning",
+ sw_if_index, 0, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ if ((nat_interface_is_inside (i) && is_inside) ||
+ (nat_interface_is_outside (i) && !is_inside))
+ return 0;
- if (sm->num_workers > 1)
- {
- del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
- "nat44-out2in-worker-handoff";
- feature_name = "nat44-handoff-classify";
- }
- else if (sm->endpoint_dependent)
- {
- del_feature_name = !is_inside ? "nat-pre-in2out" :
- "nat-pre-out2in";
+ if (sm->num_workers > 1)
+ {
+ del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
+ "nat44-out2in-worker-handoff";
+ feature_name = "nat44-handoff-classify";
+ }
+ else if (sm->endpoint_dependent)
+ {
+ del_feature_name =
+ !is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
- feature_name = "nat44-ed-classify";
- }
- else
- {
- del_feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in";
- feature_name = "nat44-classify";
- }
+ feature_name = "nat44-ed-classify";
+ }
+ else
+ {
+ del_feature_name =
+ !is_inside ? "nat44-in2out" : "nat44-out2in";
+ feature_name = "nat44-classify";
+ }
int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
if (rv)
@@ -2050,17 +1979,14 @@ feature_set:
sw_if_index, 1, 0, 0);
if (!is_inside)
{
- if (sm->endpoint_dependent)
- vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning",
- sw_if_index, 0, 0, 0);
- else
- vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
- sw_if_index, 0, 0, 0);
- }
- goto set_flags;
- }
+ if (!sm->endpoint_dependent)
+ vnet_feature_enable_disable (
+ "ip4-local", "nat44-hairpinning", sw_if_index, 0, 0, 0);
+ }
+ goto set_flags;
+ }
- goto fib;
+ goto fib;
}
}
/* *INDENT-ON* */
@@ -2085,10 +2011,7 @@ feature_set:
if (is_inside && !sm->out2in_dpo)
{
- if (sm->endpoint_dependent)
- vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning",
- sw_if_index, 1, 0, 0);
- else
+ if (!sm->endpoint_dependent)
vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
sw_if_index, 1, 0, 0);
}
@@ -2199,10 +2122,6 @@ feature_set:
!is_del);
if (rv)
return rv;
- vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-hairpin-dst",
- sw_if_index, !is_del, 0, 0);
- vnet_feature_enable_disable ("ip4-output", "nat44-ed-hairpin-src",
- sw_if_index, !is_del, 0, 0);
}
else
{
@@ -2575,12 +2494,6 @@ nat44_set_node_indexes (snat_main_t * sm, vlib_main_t * vm)
sm->hairpin_dst_node_index = node->index;
node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-src");
sm->hairpin_src_node_index = node->index;
- node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpinning");
- sm->ed_hairpinning_node_index = node->index;
- node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-dst");
- sm->ed_hairpin_dst_node_index = node->index;
- node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-src");
- sm->ed_hairpin_src_node_index = node->index;
}
#define nat_init_simple_counter(c, n, sn) \
@@ -2778,8 +2691,8 @@ nat44_ed_plugin_enable (nat44_config_t c)
if (sm->pat)
{
- sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
- sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
+ sm->icmp_match_in2out_cb = NULL;
+ sm->icmp_match_out2in_cb = NULL;
}
else
{
@@ -2907,8 +2820,6 @@ nat44_ed_plugin_disable ()
vec_free (sm->max_translations_per_fib);
- nat_affinity_disable ();
-
nat44_ed_db_free ();
nat44_addresses_free (&sm->addresses);
@@ -3258,8 +3169,8 @@ nat44_ed_get_worker_in2out_cb (ip4_header_t *ip, u32 rx_fib_index,
init_ed_k (&kv16, ip->src_address, udp->src_port, ip->dst_address,
udp->dst_port, fib_index, ip->protocol);
- if (PREDICT_TRUE (!clib_bihash_search_16_8 (&sm->out2in_ed,
- &kv16, &value16)))
+ if (PREDICT_TRUE (
+ !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)))
{
tsm =
vec_elt_at_index (sm->per_thread_data,
@@ -3327,13 +3238,13 @@ nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip,
init_ed_k (&kv16, ip->dst_address, udp->dst_port, ip->src_address,
udp->src_port, rx_fib_index, ip->protocol);
- if (PREDICT_TRUE (!clib_bihash_search_16_8 (&sm->out2in_ed,
- &kv16, &value16)))
+ if (PREDICT_TRUE (
+ !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)))
{
tsm =
vec_elt_at_index (sm->per_thread_data,
ed_value_get_thread_index (&value16));
- vnet_buffer2 (b)->nat.ed_out2in_nat_session_index =
+ vnet_buffer2 (b)->nat.cached_session_index =
ed_value_get_session_index (&value16);
next_worker_index = sm->first_worker_index + tsm->thread_index;
nat_elog_debug_handoff ("HANDOFF OUT2IN (session)",
@@ -3347,10 +3258,17 @@ nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip,
}
else if (proto == NAT_PROTOCOL_ICMP)
{
- if (!get_icmp_o2i_ed_key (b, ip, rx_fib_index, ~0, ~0, 0, 0, 0, &kv16))
+ ip4_address_t lookup_saddr, lookup_daddr;
+ u16 lookup_sport, lookup_dport;
+ u8 lookup_protocol;
+ if (!nat_get_icmp_session_lookup_values (
+ b, ip, &lookup_saddr, &lookup_sport, &lookup_daddr, &lookup_dport,
+ &lookup_protocol))
{
- if (PREDICT_TRUE (!clib_bihash_search_16_8 (&sm->out2in_ed,
- &kv16, &value16)))
+ init_ed_k (&kv16, lookup_saddr, lookup_sport, lookup_daddr,
+ lookup_dport, rx_fib_index, lookup_protocol);
+ if (PREDICT_TRUE (
+ !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)))
{
tsm =
vec_elt_at_index (sm->per_thread_data,
@@ -3558,9 +3476,6 @@ nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
tsm->unk_proto_lru_head_index = head - tsm->lru_pool;
clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index);
- clib_bihash_init_16_8 (&tsm->in2out_ed, "in2out-ed", translation_buckets, 0);
- clib_bihash_set_kvp_format_fn_16_8 (&tsm->in2out_ed, format_ed_session_kvp);
-
// TODO: ED nat is not using these
// before removal large refactor required
pool_alloc (tsm->list_pool, translations);
@@ -3569,6 +3484,17 @@ nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
}
static void
+reinit_ed_flow_hash ()
+{
+ snat_main_t *sm = &snat_main;
+ // we expect 2 flows per session, so multiply translation_buckets by 2
+ clib_bihash_init_16_8 (
+ &sm->flow_hash, "ed-flow-hash",
+ clib_max (1, sm->num_workers) * 2 * sm->translation_buckets, 0);
+ clib_bihash_set_kvp_format_fn_16_8 (&sm->flow_hash, format_ed_session_kvp);
+}
+
+static void
nat44_ed_db_init (u32 translations, u32 translation_buckets, u32 user_buckets)
{
snat_main_t *sm = &snat_main;
@@ -3576,8 +3502,7 @@ nat44_ed_db_init (u32 translations, u32 translation_buckets, u32 user_buckets)
u32 static_mapping_buckets = 1024;
u32 static_mapping_memory_size = 64 << 20;
- clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed", translation_buckets, 0);
- clib_bihash_set_kvp_format_fn_16_8 (&sm->out2in_ed, format_ed_session_kvp);
+ reinit_ed_flow_hash ();
clib_bihash_init_8_8 (&sm->static_mapping_by_local,
"static_mapping_by_local", static_mapping_buckets,
@@ -3607,7 +3532,6 @@ nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm)
pool_free (tsm->sessions);
pool_free (tsm->lru_pool);
- clib_bihash_free_16_8 (&tsm->in2out_ed);
vec_free (tsm->per_vrf_sessions_vec);
// TODO: resolve static mappings (put only to !ED)
@@ -3623,7 +3547,7 @@ nat44_ed_db_free ()
snat_main_per_thread_data_t *tsm;
pool_free (sm->static_mappings);
- clib_bihash_free_16_8 (&sm->out2in_ed);
+ clib_bihash_free_16_8 (&sm->flow_hash);
clib_bihash_free_8_8 (&sm->static_mapping_by_local);
clib_bihash_free_8_8 (&sm->static_mapping_by_external);
@@ -3642,11 +3566,7 @@ nat44_ed_sessions_clear ()
snat_main_t *sm = &snat_main;
snat_main_per_thread_data_t *tsm;
- clib_bihash_free_16_8 (&sm->out2in_ed);
- clib_bihash_init_16_8 (
- &sm->out2in_ed, "out2in-ed",
- clib_max (1, sm->num_workers) * sm->translation_buckets, 0);
- clib_bihash_set_kvp_format_fn_16_8 (&sm->out2in_ed, format_ed_session_kvp);
+ reinit_ed_flow_hash ();
if (sm->pat)
{
@@ -3896,7 +3816,6 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
u32 vrf_id, int is_in)
{
ip4_header_t ip;
- clib_bihash_16_8_t *t;
clib_bihash_kv_16_8_t kv, value;
u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
snat_session_t *s;
@@ -3913,16 +3832,15 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
else
tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
- t = is_in ? &tsm->in2out_ed : &sm->out2in_ed;
init_ed_k (&kv, *addr, port, *eh_addr, eh_port, fib_index, proto);
- if (clib_bihash_search_16_8 (t, &kv, &value))
+ if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value))
{
return VNET_API_ERROR_NO_SUCH_ENTRY;
}
- if (pool_is_free_index (tsm->sessions, value.value))
+ if (pool_is_free_index (tsm->sessions, ed_value_get_session_index (&value)))
return VNET_API_ERROR_UNSPECIFIED;
- s = pool_elt_at_index (tsm->sessions, value.value);
+ s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value));
nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
return 0;
@@ -3952,13 +3870,343 @@ VLIB_REGISTER_NODE (nat_default_node) = {
[NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath",
[NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in",
[NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath",
- [NAT_NEXT_OUT2IN_ED_HANDOFF] = "nat44-ed-out2in-handoff",
[NAT_NEXT_IN2OUT_CLASSIFY] = "nat44-in2out-worker-handoff",
[NAT_NEXT_OUT2IN_CLASSIFY] = "nat44-out2in-worker-handoff",
},
};
/* *INDENT-ON* */
+void
+nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f)
+{
+ f->l3_csum_delta = 0;
+ f->l4_csum_delta = 0;
+ if (f->ops & NAT_FLOW_OP_SADDR_REWRITE &&
+ f->rewrite.saddr.as_u32 != f->match.saddr.as_u32)
+ {
+ f->l3_csum_delta =
+ ip_csum_add_even (f->l3_csum_delta, f->rewrite.saddr.as_u32);
+ f->l3_csum_delta =
+ ip_csum_sub_even (f->l3_csum_delta, f->match.saddr.as_u32);
+ }
+ else
+ {
+ f->rewrite.saddr.as_u32 = f->match.saddr.as_u32;
+ }
+ if (f->ops & NAT_FLOW_OP_DADDR_REWRITE &&
+ f->rewrite.daddr.as_u32 != f->match.daddr.as_u32)
+ {
+ f->l3_csum_delta =
+ ip_csum_add_even (f->l3_csum_delta, f->rewrite.daddr.as_u32);
+ f->l3_csum_delta =
+ ip_csum_sub_even (f->l3_csum_delta, f->match.daddr.as_u32);
+ }
+ else
+ {
+ f->rewrite.daddr.as_u32 = f->match.daddr.as_u32;
+ }
+ if (f->ops & NAT_FLOW_OP_SPORT_REWRITE && f->rewrite.sport != f->match.sport)
+ {
+ f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.sport);
+ f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.sport);
+ }
+ else
+ {
+ f->rewrite.sport = f->match.sport;
+ }
+ if (f->ops & NAT_FLOW_OP_DPORT_REWRITE && f->rewrite.dport != f->match.dport)
+ {
+ f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.dport);
+ f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.dport);
+ }
+ else
+ {
+ f->rewrite.dport = f->match.dport;
+ }
+ if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE &&
+ f->rewrite.icmp_id != f->match.icmp_id)
+ {
+ f->l4_csum_delta =
+ ip_csum_add_even (f->l4_csum_delta, f->rewrite.icmp_id);
+ f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.icmp_id);
+ }
+ else
+ {
+ f->rewrite.icmp_id = f->match.icmp_id;
+ }
+ if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
+ {
+ }
+ else
+ {
+ f->rewrite.fib_index = f->match.fib_index;
+ }
+}
+
+static_always_inline int nat_6t_flow_icmp_translate (snat_main_t *sm,
+ vlib_buffer_t *b,
+ ip4_header_t *ip,
+ nat_6t_flow_t *f);
+
+static_always_inline void
+nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
+ nat_6t_flow_t *f, nat_protocol_t proto,
+ int is_icmp_inner_ip4)
+{
+ udp_header_t *udp = ip4_next_header (ip);
+ tcp_header_t *tcp = (tcp_header_t *) udp;
+
+ if ((NAT_PROTOCOL_TCP == proto || NAT_PROTOCOL_UDP == proto) &&
+ !vnet_buffer (b)->ip.reass.is_non_first_fragment)
+ {
+ if (!is_icmp_inner_ip4)
+ { // regular case
+ ip->src_address = f->rewrite.saddr;
+ ip->dst_address = f->rewrite.daddr;
+ udp->src_port = f->rewrite.sport;
+ udp->dst_port = f->rewrite.dport;
+ }
+ else
+ { // icmp inner ip4 - reversed saddr/daddr
+ ip->src_address = f->rewrite.daddr;
+ ip->dst_address = f->rewrite.saddr;
+ udp->src_port = f->rewrite.dport;
+ udp->dst_port = f->rewrite.sport;
+ }
+
+ if (NAT_PROTOCOL_TCP == proto)
+ {
+ ip_csum_t tcp_sum = tcp->checksum;
+ tcp_sum = ip_csum_sub_even (tcp_sum, f->l3_csum_delta);
+ tcp_sum = ip_csum_sub_even (tcp_sum, f->l4_csum_delta);
+ mss_clamping (sm->mss_clamping, tcp, &tcp_sum);
+ tcp->checksum = ip_csum_fold (tcp_sum);
+ }
+ else if (proto == NAT_PROTOCOL_UDP && udp->checksum)
+ {
+ ip_csum_t udp_sum = udp->checksum;
+ udp_sum = ip_csum_sub_even (udp_sum, f->l3_csum_delta);
+ udp_sum = ip_csum_sub_even (udp_sum, f->l4_csum_delta);
+ udp->checksum = ip_csum_fold (udp_sum);
+ }
+ }
+ else
+ {
+ if (!is_icmp_inner_ip4)
+ { // regular case
+ ip->src_address = f->rewrite.saddr;
+ ip->dst_address = f->rewrite.daddr;
+ }
+ else
+ { // icmp inner ip4 - reversed saddr/daddr
+ ip->src_address = f->rewrite.daddr;
+ ip->dst_address = f->rewrite.saddr;
+ }
+ }
+
+ ip_csum_t ip_sum = ip->checksum;
+ ip_sum = ip_csum_sub_even (ip_sum, f->l3_csum_delta);
+ ip->checksum = ip_csum_fold (ip_sum);
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+}
+
+static_always_inline int
+nat_6t_flow_icmp_translate (snat_main_t *sm, vlib_buffer_t *b,
+ ip4_header_t *ip, nat_6t_flow_t *f)
+{
+ if (IP_PROTOCOL_ICMP != ip->protocol)
+ return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
+
+ icmp46_header_t *icmp = ip4_next_header (ip);
+ icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
+
+ if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment))
+ {
+ if (icmp->checksum == 0)
+ icmp->checksum = 0xffff;
+
+ if (!icmp_type_is_error_message (icmp->type))
+ {
+ if ((f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) &&
+ (f->rewrite.icmp_id != echo->identifier))
+ {
+ ip_csum_t sum = icmp->checksum;
+ sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id,
+ icmp_echo_header_t,
+ identifier /* changed member */);
+ echo->identifier = f->rewrite.icmp_id;
+ icmp->checksum = ip_csum_fold (sum);
+ }
+ }
+ else
+ {
+ // errors are not fragmented
+ ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
+
+ if (!ip4_header_checksum_is_valid (inner_ip))
+ {
+ return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
+ }
+
+ nat_protocol_t inner_proto =
+ ip_proto_to_nat_proto (inner_ip->protocol);
+
+ ip_csum_t icmp_sum = icmp->checksum;
+
+ switch (inner_proto)
+ {
+ case NAT_PROTOCOL_UDP:
+ case NAT_PROTOCOL_TCP:
+ nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto,
+ 1 /* is_icmp_inner_ip4 */);
+ icmp_sum = ip_csum_sub_even (icmp_sum, f->l3_csum_delta);
+ icmp->checksum = ip_csum_fold (icmp_sum);
+ break;
+ case NAT_PROTOCOL_ICMP:
+ if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE)
+ {
+ icmp46_header_t *inner_icmp = ip4_next_header (inner_ip);
+ icmp_echo_header_t *inner_echo =
+ (icmp_echo_header_t *) (inner_icmp + 1);
+ if (f->rewrite.icmp_id != inner_echo->identifier)
+ {
+ ip_csum_t sum = icmp->checksum;
+ sum = ip_csum_update (
+ sum, inner_echo->identifier, f->rewrite.icmp_id,
+ icmp_echo_header_t, identifier /* changed member */);
+ icmp->checksum = ip_csum_fold (sum);
+ ip_csum_t inner_sum = inner_icmp->checksum;
+ inner_sum = ip_csum_update (
+ sum, inner_echo->identifier, f->rewrite.icmp_id,
+ icmp_echo_header_t, identifier /* changed member */);
+ inner_icmp->checksum = ip_csum_fold (inner_sum);
+ inner_echo->identifier = f->rewrite.icmp_id;
+ }
+ }
+ break;
+ default:
+ clib_warning ("unexpected NAT protocol value `%d'", inner_proto);
+ return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
+ }
+ }
+ }
+ return NAT_ED_TRNSL_ERR_SUCCESS;
+}
+
+nat_translation_error_e
+nat_6t_flow_buf_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
+ nat_6t_flow_t *f, nat_protocol_t proto,
+ int is_output_feature)
+{
+ if (!is_output_feature && f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
+ {
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = f->rewrite.fib_index;
+ }
+
+ nat_6t_flow_ip4_translate (sm, b, ip, f, proto, 0 /* is_icmp_inner_ip4 */);
+
+ if (NAT_PROTOCOL_ICMP == proto)
+ {
+ return nat_6t_flow_icmp_translate (sm, b, ip, f);
+ }
+
+ return NAT_ED_TRNSL_ERR_SUCCESS;
+}
+
+u8 *
+format_nat_6t (u8 *s, va_list *args)
+{
+ nat_6t_t *t = va_arg (*args, nat_6t_t *);
+
+ s = format (s, "saddr %U sport %u daddr %U dport %u proto %U fib_idx %u",
+ format_ip4_address, t->saddr.as_u8,
+ clib_net_to_host_u16 (t->sport), format_ip4_address,
+ t->daddr.as_u8, clib_net_to_host_u16 (t->dport),
+ format_ip_protocol, t->proto, t->fib_index);
+ return s;
+}
+
+u8 *
+format_nat_ed_translation_error (u8 *s, va_list *args)
+{
+ nat_translation_error_e e = va_arg (*args, nat_translation_error_e);
+
+ switch (e)
+ {
+ case NAT_ED_TRNSL_ERR_SUCCESS:
+ s = format (s, "success");
+ break;
+ case NAT_ED_TRNSL_ERR_TRANSLATION_FAILED:
+ s = format (s, "translation-failed");
+ break;
+ case NAT_ED_TRNSL_ERR_FLOW_MISMATCH:
+ s = format (s, "flow-mismatch");
+ break;
+ }
+ return s;
+}
+
+u8 *
+format_nat_6t_flow (u8 *s, va_list *args)
+{
+ nat_6t_flow_t *f = va_arg (*args, nat_6t_flow_t *);
+
+ s = format (s, "match: %U ", format_nat_6t, &f->match);
+ int r = 0;
+ if (f->ops & NAT_FLOW_OP_SADDR_REWRITE)
+ {
+ s = format (s, "rewrite: saddr %U ", format_ip4_address,
+ f->rewrite.saddr.as_u8);
+ r = 1;
+ }
+ if (f->ops & NAT_FLOW_OP_SPORT_REWRITE)
+ {
+ if (!r)
+ {
+ s = format (s, "rewrite: ");
+ r = 1;
+ }
+ s = format (s, "sport %u ", clib_net_to_host_u16 (f->rewrite.sport));
+ }
+ if (f->ops & NAT_FLOW_OP_DADDR_REWRITE)
+ {
+ if (!r)
+ {
+ s = format (s, "rewrite: ");
+ r = 1;
+ }
+ s = format (s, "daddr %U ", format_ip4_address, f->rewrite.daddr.as_u8);
+ }
+ if (f->ops & NAT_FLOW_OP_DPORT_REWRITE)
+ {
+ if (!r)
+ {
+ s = format (s, "rewrite: ");
+ r = 1;
+ }
+ s = format (s, "dport %u ", clib_net_to_host_u16 (f->rewrite.dport));
+ }
+ if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE)
+ {
+ if (!r)
+ {
+ s = format (s, "rewrite: ");
+ r = 1;
+ }
+ s = format (s, "icmp-id %u ", clib_net_to_host_u16 (f->rewrite.icmp_id));
+ }
+ if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
+ {
+ if (!r)
+ {
+ s = format (s, "rewrite: ");
+ r = 1;
+ }
+ s = format (s, "txfib %u ", f->rewrite.fib_index);
+ }
+ return s;
+}
+
/*
* fd.io coding-style-patch-verification: ON
*