X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=blobdiff_plain;f=src%2Fplugins%2Fnat%2Fnat.c;h=8e20df4e64e41fb83736d76186d2d12b0a8783a8;hp=8dce3b96c505cead4a1929416e46e47a829d828b;hb=61717cc38;hpb=8008d7cdfcf71389c98d2968070b8cda1ed433f9 diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 8dce3b96c50..8e20df4e64e 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -25,129 +25,151 @@ #include #include #include -#include -#include #include +#include +#include +#include +#include #include #include +#include +#include #include snat_main_t snat_main; +fib_source_t nat_fib_src_hi; +fib_source_t nat_fib_src_low; +/* *INDENT-OFF* */ /* Hook up input features */ +VNET_FEATURE_INIT (nat_pre_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat-pre-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (nat_pre_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat-pre-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; +VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), +}; VNET_FEATURE_INIT (ip4_snat_in2out, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_snat_out2in, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat_classify, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat_det_classify, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-ed-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-ed-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-ed-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-in2out-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-out2in-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", - "ip4-dhcp-client-detect"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-handoff-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out-fast", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), }; VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-out2in-fast", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-hairpin-dst", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .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"), + .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) = { .arc_name = "ip4-output", .node_name = "nat44-in2out-output", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), }; VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { .arc_name = "ip4-output", .node_name = "nat44-in2out-output-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), }; VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { .arc_name = "ip4-output", .node_name = "nat44-hairpin-src", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), }; VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = { .arc_name = "ip4-output", .node_name = "nat44-ed-in2out-output", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), + .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 ("acl-plugin-out-ip4-fa"), + .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), + .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), }; /* Hook up ip4-local features */ @@ -165,143 +187,302 @@ VNET_FEATURE_INIT (ip4_nat44_ed_hairpinning, static) = }; -/* *INDENT-OFF* */ VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, - .description = "Network Address Translation", + .description = "Network Address Translation (NAT)", }; /* *INDENT-ON* */ -vlib_node_registration_t nat44_classify_node; -vlib_node_registration_t nat44_ed_classify_node; -vlib_node_registration_t nat44_det_classify_node; -vlib_node_registration_t nat44_handoff_classify_node; - -typedef enum { - NAT44_CLASSIFY_NEXT_IN2OUT, - NAT44_CLASSIFY_NEXT_OUT2IN, - NAT44_CLASSIFY_N_NEXT, -} nat44_classify_next_t; - void -nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) +nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, + u8 is_ha) { snat_session_key_t key; clib_bihash_kv_8_8_t kv; nat_ed_ses_key_t ed_key; clib_bihash_kv_16_8_t ed_kv; - int i; - snat_address_t *a; snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, thread_index); if (is_fwd_bypass_session (s)) { + if (snat_is_unk_proto_session (s)) + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } + else + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->in2out.port; + ed_key.r_port = s->ext_host_port; + } ed_key.l_addr = s->in2out.addr; ed_key.r_addr = s->ext_host_addr; - ed_key.l_port = s->in2out.port; - ed_key.r_port = s->ext_host_port; - ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); ed_key.fib_index = 0; ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) - nat_log_warn ("in2out_ed key del failed"); + nat_elog_warn ("in2out_ed key del failed"); return; } /* session lookup tables */ if (is_ed_session (s)) { + if (is_affinity_sessions (s)) + nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, + s->in2out.protocol, s->out2in.port); ed_key.l_addr = s->out2in.addr; ed_key.r_addr = s->ext_host_addr; ed_key.fib_index = s->out2in.fib_index; if (snat_is_unk_proto_session (s)) - { - ed_key.proto = s->in2out.port; - ed_key.r_port = 0; - ed_key.l_port = 0; - } + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } else - { - ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); - ed_key.l_port = s->out2in.port; - ed_key.r_port = s->ext_host_port; - } + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->out2in.port; + ed_key.r_port = s->ext_host_port; + } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0)) - nat_log_warn ("out2in_ed key del failed"); - + nat_elog_warn ("out2in_ed key del failed"); ed_key.l_addr = s->in2out.addr; ed_key.fib_index = s->in2out.fib_index; if (!snat_is_unk_proto_session (s)) - ed_key.l_port = s->in2out.port; + ed_key.l_port = s->in2out.port; if (is_twice_nat_session (s)) - { - ed_key.r_addr = s->ext_host_nat_addr; - ed_key.r_port = s->ext_host_nat_port; - } + { + ed_key.r_addr = s->ext_host_nat_addr; + ed_key.r_port = s->ext_host_nat_port; + } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) - nat_log_warn ("in2out_ed key del failed"); + nat_elog_warn ("in2out_ed key del failed"); + + if (!is_ha) + nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index, + &s->in2out.addr, s->in2out.port, + &s->ext_host_nat_addr, s->ext_host_nat_port, + &s->out2in.addr, s->out2in.port, + &s->ext_host_addr, s->ext_host_port, + s->in2out.protocol, is_twice_nat_session (s)); } else { kv.key = s->in2out.as_u64; if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0)) - nat_log_warn ("in2out key del failed"); + nat_elog_warn ("in2out key del failed"); kv.key = s->out2in.as_u64; if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0)) - nat_log_warn ("out2in key del failed"); + nat_elog_warn ("out2in key del failed"); + + if (!is_ha) + nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index, + &s->in2out.addr, s->in2out.port, + &s->out2in.addr, s->out2in.port, + s->in2out.protocol); } if (snat_is_unk_proto_session (s)) return; - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); + if (!is_ha) + { + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete (thread_index, + s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, + s->ext_host_port, s->out2in.protocol, s->out2in.fib_index, + thread_index); + } /* Twice NAT address and port for external host */ if (is_twice_nat_session (s)) { - for (i = 0; i < vec_len (sm->twice_nat_addresses); i++) - { - key.protocol = s->in2out.protocol; - key.port = s->ext_host_nat_port; - a = sm->twice_nat_addresses + i; - if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32) - { - snat_free_outside_address_and_port (sm->twice_nat_addresses, - thread_index, &key, i); - break; - } - } + key.protocol = s->in2out.protocol; + key.port = s->ext_host_nat_port; + key.addr.as_u32 = s->ext_host_nat_addr.as_u32; + snat_free_outside_address_and_port (sm->twice_nat_addresses, + thread_index, &key); + } + + if (snat_is_session_static (s)) + return; + + ed_bihash_kv_t bihash_key; + clib_memset (&bihash_key, 0, sizeof (bihash_key)); + bihash_key.k.dst_address = s->ext_host_addr.as_u32; + bihash_key.k.dst_port = s->ext_host_port; + bihash_key.k.src_address = s->out2in.addr.as_u32; + bihash_key.k.src_port = s->out2in.port; + bihash_key.k.protocol = s->out2in.protocol; + clib_bihash_add_del_16_8 (&sm->ed_ext_ports, &bihash_key.kv, + 0 /* is_add */ ); + + snat_free_outside_address_and_port (sm->addresses, thread_index, + &s->out2in); +} + +void +nat44_free_session_data (snat_main_t * sm, snat_session_t * s, + u32 thread_index, u8 is_ha) +{ + snat_session_key_t key; + nat_ed_ses_key_t ed_key; + 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_fwd_bypass_session (s)) + { + if (snat_is_unk_proto_session (s)) + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } + else + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->in2out.port; + ed_key.r_port = s->ext_host_port; + } + + ed_key.l_addr = s->in2out.addr; + ed_key.r_addr = s->ext_host_addr; + ed_key.fib_index = 0; + ed_kv.key[0] = ed_key.as_u64[0]; + ed_kv.key[1] = ed_key.as_u64[1]; + + if (PREDICT_FALSE + (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))) + nat_elog_warn ("in2out_ed key del failed"); + return; + } + + /* session lookup tables */ + if (is_affinity_sessions (s)) + nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, + s->in2out.protocol, s->out2in.port); + ed_key.l_addr = s->out2in.addr; + ed_key.r_addr = s->ext_host_addr; + ed_key.fib_index = s->out2in.fib_index; + if (snat_is_unk_proto_session (s)) + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } + else + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->out2in.port; + ed_key.r_port = s->ext_host_port; + } + ed_kv.key[0] = ed_key.as_u64[0]; + ed_kv.key[1] = ed_key.as_u64[1]; + + if (PREDICT_FALSE (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0))) + nat_elog_warn ("out2in_ed key del failed"); + + ed_key.l_addr = s->in2out.addr; + ed_key.fib_index = s->in2out.fib_index; + + if (!snat_is_unk_proto_session (s)) + ed_key.l_port = s->in2out.port; + + if (is_twice_nat_session (s)) + { + ed_key.r_addr = s->ext_host_nat_addr; + ed_key.r_port = s->ext_host_nat_port; + } + + ed_kv.key[0] = ed_key.as_u64[0]; + ed_kv.key[1] = ed_key.as_u64[1]; + + if (PREDICT_FALSE (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, + &s->in2out.addr, s->in2out.port, + &s->ext_host_nat_addr, s->ext_host_nat_port, + &s->out2in.addr, s->out2in.port, + &s->ext_host_addr, s->ext_host_port, + s->in2out.protocol, is_twice_nat_session (s)); + } + + if (snat_is_unk_proto_session (s)) + return; + + // is this correct ? + if (!is_ha) + { + snat_ipfix_logging_nat44_ses_delete (thread_index, + s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, + s->ext_host_port, s->out2in.protocol, s->out2in.fib_index, + thread_index); + } + + /* Twice NAT address and port for external host */ + if (is_twice_nat_session (s)) + { + key.protocol = s->in2out.protocol; + key.port = s->ext_host_nat_port; + key.addr.as_u32 = s->ext_host_nat_addr.as_u32; + snat_free_outside_address_and_port (sm->twice_nat_addresses, + thread_index, &key); } if (snat_is_session_static (s)) return; - if (s->outside_address_index != ~0) - snat_free_outside_address_and_port (sm->addresses, thread_index, - &s->out2in, s->outside_address_index); + ed_bihash_kv_t bihash_key; + clib_memset (&bihash_key, 0, sizeof (bihash_key)); + bihash_key.k.dst_address = s->ext_host_addr.as_u32; + bihash_key.k.dst_port = s->ext_host_port; + bihash_key.k.src_address = s->out2in.addr.as_u32; + bihash_key.k.src_port = s->out2in.port; + bihash_key.k.protocol = s->out2in.protocol; + clib_bihash_add_del_16_8 (&sm->ed_ext_ports, &bihash_key.kv, + 0 /* is_add */ ); + + // should be called for every dynamic session + snat_free_outside_address_and_port (sm->addresses, thread_index, + &s->out2in); } + snat_user_t * -nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, - u32 thread_index) +nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index, + u32 thread_index) { snat_user_t *u = 0; snat_user_key_t user_key; clib_bihash_kv_8_8_t kv, value; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - dlist_elt_t * per_user_list_head_elt; + dlist_elt_t *per_user_list_head_elt; user_key.addr.as_u32 = addr->as_u32; user_key.fib_index = fib_index; @@ -312,14 +493,17 @@ nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, { /* no, make a new one */ pool_get (tsm->users, u); - memset (u, 0, sizeof (*u)); + clib_memset (u, 0, sizeof (*u)); + + u->min_session_timeout = 0; + u->addr.as_u32 = addr->as_u32; u->fib_index = fib_index; pool_get (tsm->list_pool, per_user_list_head_elt); u->sessions_per_user_list_head_index = per_user_list_head_elt - - tsm->list_pool; + tsm->list_pool; clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index); @@ -327,7 +511,14 @@ nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, /* add user */ if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) - nat_log_warn ("user_hash keay add failed"); + { + nat_elog_warn ("user_hash key add failed"); + nat44_delete_user_with_no_session (sm, u, thread_index); + return NULL; + } + + vlib_set_simple_counter (&sm->total_users, thread_index, 0, + pool_elts (tsm->users)); } else { @@ -338,43 +529,43 @@ nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, } snat_session_t * -nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index) +nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, + u32 thread_index, f64 now) { snat_session_t *s; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; u32 oldest_per_user_translation_list_index, session_index; - dlist_elt_t * oldest_per_user_translation_list_elt; - dlist_elt_t * per_user_translation_list_elt; + dlist_elt_t *oldest_per_user_translation_list_elt; + dlist_elt_t *per_user_translation_list_elt; /* Over quota? Recycle the least recently used translation */ if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user) { oldest_per_user_translation_list_index = - clib_dlist_remove_head (tsm->list_pool, - u->sessions_per_user_list_head_index); + clib_dlist_remove_head (tsm->list_pool, + u->sessions_per_user_list_head_index); ASSERT (oldest_per_user_translation_list_index != ~0); /* Add it back to the end of the LRU list */ clib_dlist_addtail (tsm->list_pool, - u->sessions_per_user_list_head_index, - oldest_per_user_translation_list_index); + u->sessions_per_user_list_head_index, + oldest_per_user_translation_list_index); /* Get the list element */ oldest_per_user_translation_list_elt = - pool_elt_at_index (tsm->list_pool, - oldest_per_user_translation_list_index); + pool_elt_at_index (tsm->list_pool, + oldest_per_user_translation_list_index); /* Get the session index from the list element */ session_index = oldest_per_user_translation_list_elt->value; /* Get the session */ s = pool_elt_at_index (tsm->sessions, session_index); - nat_free_session_data (sm, s, thread_index); - if (snat_is_session_static(s)) - u->nstaticsessions--; + nat_free_session_data (sm, s, thread_index, 0); + if (snat_is_session_static (s)) + u->nstaticsessions--; else - u->nsessions--; - s->outside_address_index = ~0; + u->nsessions--; s->flags = 0; s->total_bytes = 0; s->total_pkts = 0; @@ -387,286 +578,162 @@ nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index) else { pool_get (tsm->sessions, s); - memset (s, 0, sizeof (*s)); - s->outside_address_index = ~0; + clib_memset (s, 0, sizeof (*s)); /* Create list elts */ pool_get (tsm->list_pool, per_user_translation_list_elt); clib_dlist_init (tsm->list_pool, - per_user_translation_list_elt - tsm->list_pool); + per_user_translation_list_elt - tsm->list_pool); per_user_translation_list_elt->value = s - tsm->sessions; s->per_user_index = per_user_translation_list_elt - tsm->list_pool; s->per_user_list_head_index = u->sessions_per_user_list_head_index; clib_dlist_addtail (tsm->list_pool, - s->per_user_list_head_index, - per_user_translation_list_elt - tsm->list_pool); + s->per_user_list_head_index, + per_user_translation_list_elt - tsm->list_pool); + + dlist_elt_t *global_lru_list_elt; + pool_get (tsm->global_lru_pool, global_lru_list_elt); + global_lru_list_elt->value = s - tsm->sessions; + s->global_lru_index = global_lru_list_elt - tsm->global_lru_pool; + clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index, + s->global_lru_index); + s->last_lru_update = now; + + s->user_index = u - tsm->users; + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); } + s->ha_last_refreshed = now; + return s; } -typedef struct { - u8 next_in2out; -} nat44_classify_trace_t; - -static u8 * format_nat44_classify_trace (u8 * s, va_list * args) +snat_session_t * +nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index, + f64 now) { - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); - nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *); - char *next; - - next = t->next_in2out ? "nat44-in2out" : "nat44-out2in"; + snat_session_t *s = NULL; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - s = format (s, "nat44-classify: next %s", next); + dlist_elt_t *oldest_elt; + u64 sess_timeout_time; + u32 oldest_index; - return s; -} + // no sessions + if (PREDICT_FALSE (!(u->nsessions) && !(u->nstaticsessions))) + goto alloc_new; -static inline uword -nat44_classify_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - nat44_classify_next_t next_index; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; + // no free sessions + if (PREDICT_FALSE + ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)) + { + if (nat44_max_translations_per_user_cleanup (u, thread_index, now)) + goto alloc_new; + + nat_elog_addr (SNAT_LOG_WARNING, "[warn] max translations per user", + clib_net_to_host_u32 (u->addr.as_u32)); + snat_ipfix_logging_max_entries_per_user (thread_index, + sm->max_translations_per_user, + u->addr.as_u32); + return 0; + } - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; + /* first try to reuse an expired session from this ip */ + oldest_index = + clib_dlist_remove_head (tsm->list_pool, + u->sessions_per_user_list_head_index); + oldest_elt = pool_elt_at_index (tsm->list_pool, oldest_index); + s = pool_elt_at_index (tsm->sessions, oldest_elt->value); - while (n_left_from > 0) + sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time || + (s->tcp_close_timestamp && now >= s->tcp_close_timestamp)) { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, - to_next, n_left_to_next); + // reuse old session + clib_dlist_addtail (tsm->list_pool, + u->sessions_per_user_list_head_index, oldest_index); + s = nat44_session_reuse_old (sm, u, s, thread_index, now); + s->last_lru_update = now; + } + else + { + clib_dlist_addhead (tsm->list_pool, + u->sessions_per_user_list_head_index, oldest_index); + s = NULL; + } - while (n_left_from > 0 && n_left_to_next > 0) +alloc_new: + /* try to free an expired session from global LRU list */ + if (!s) + { + oldest_index = clib_dlist_remove_head (tsm->global_lru_pool, + tsm->global_lru_head_index); + if (~0 != oldest_index) { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT; - ip4_header_t *ip0; - snat_address_t *ap; - snat_session_key_t m_key0; - clib_bihash_kv_8_8_t kv0, value0; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - - vec_foreach (ap, sm->addresses) - { - if (ip0->dst_address.as_u32 == ap->addr.as_u32) - { - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - goto enqueue0; - } - } - - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - m_key0.addr = ip0->dst_address; - m_key0.port = 0; - m_key0.protocol = 0; - m_key0.fib_index = sm->outside_fib_index; - kv0.key = m_key0.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - goto enqueue0; - } - udp_header_t * udp0 = ip4_next_header (ip0); - m_key0.port = clib_net_to_host_u16 (udp0->dst_port); - m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol); - kv0.key = m_key0.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - } - } - - enqueue0: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_classify_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0; - } - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); + oldest_elt = pool_elt_at_index (tsm->global_lru_pool, oldest_index); + s = pool_elt_at_index (tsm->sessions, oldest_elt->value); + + sess_timeout_time = + s->last_heard + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time + || (s->tcp_close_timestamp && now >= s->tcp_close_timestamp)) + { + nat_free_session_data (sm, s, thread_index, 0); + nat44_ed_delete_session (sm, s, thread_index, 0); + } + else + { + clib_dlist_addhead (tsm->global_lru_pool, + tsm->global_lru_head_index, oldest_index); + } + s = NULL; + } } - - return frame->n_vectors; + if (!s) + { + s = nat44_session_alloc_new (tsm, u, now); + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); + } + return s; } -static uword -nat44_classify_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_classify_node_fn_inline (vm, node, frame); -}; - -VLIB_REGISTER_NODE (nat44_classify_node) = { - .function = nat44_classify_node_fn, - .name = "nat44-classify", - .vector_size = sizeof (u32), - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = NAT44_CLASSIFY_N_NEXT, - .next_nodes = { - [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out", - [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (nat44_classify_node, - nat44_classify_node_fn); -static uword -nat44_ed_classify_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_classify_node_fn_inline (vm, node, frame); -}; - -VLIB_REGISTER_NODE (nat44_ed_classify_node) = { - .function = nat44_ed_classify_node_fn, - .name = "nat44-ed-classify", - .vector_size = sizeof (u32), - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = NAT44_CLASSIFY_N_NEXT, - .next_nodes = { - [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-ed-in2out", - [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-ed-out2in", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_classify_node, - nat44_ed_classify_node_fn); - -static uword -nat44_det_classify_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_classify_node_fn_inline (vm, node, frame); -}; - -VLIB_REGISTER_NODE (nat44_det_classify_node) = { - .function = nat44_det_classify_node_fn, - .name = "nat44-det-classify", - .vector_size = sizeof (u32), - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = NAT44_CLASSIFY_N_NEXT, - .next_nodes = { - [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-det-in2out", - [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-det-out2in", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (nat44_det_classify_node, - nat44_det_classify_node_fn); - -static uword -nat44_handoff_classify_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_classify_node_fn_inline (vm, node, frame); -}; - -VLIB_REGISTER_NODE (nat44_handoff_classify_node) = { - .function = nat44_handoff_classify_node_fn, - .name = "nat44-handoff-classify", - .vector_size = sizeof (u32), - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = NAT44_CLASSIFY_N_NEXT, - .next_nodes = { - [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out-worker-handoff", - [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in-worker-handoff", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (nat44_handoff_classify_node, - nat44_handoff_classify_node_fn); - -/** - * @brief Add/del NAT address to FIB. - * - * Add the external NAT address to the FIB as receive entries. This ensures - * that VPP will reply to ARP for this address and we don't need to enable - * proxy ARP on the outside interface. - * - * @param addr IPv4 address. - * @param plen address prefix length - * @param sw_if_index Interface. - * @param is_add If 0 delete, otherwise add. - */ void snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, - int is_add) + int is_add) { fib_prefix_t prefix = { .fp_len = p_len, .fp_proto = FIB_PROTOCOL_IP4, .fp_addr = { - .ip4.as_u32 = addr->as_u32, - }, + .ip4.as_u32 = addr->as_u32, + }, }; - u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index); + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); if (is_add) - fib_table_entry_update_one_path(fib_index, - &prefix, - FIB_SOURCE_PLUGIN_LOW, - (FIB_ENTRY_FLAG_CONNECTED | - FIB_ENTRY_FLAG_LOCAL | - FIB_ENTRY_FLAG_EXCLUSIVE), - DPO_PROTO_IP4, - NULL, - sw_if_index, - ~0, - 1, - NULL, - FIB_ROUTE_PATH_FLAG_NONE); + fib_table_entry_update_one_path (fib_index, + &prefix, + nat_fib_src_low, + (FIB_ENTRY_FLAG_CONNECTED | + FIB_ENTRY_FLAG_LOCAL | + FIB_ENTRY_FLAG_EXCLUSIVE), + DPO_PROTO_IP4, + NULL, + sw_if_index, + ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); else - fib_table_entry_delete(fib_index, - &prefix, - FIB_SOURCE_PLUGIN_LOW); + fib_table_entry_delete (fib_index, &prefix, nat_fib_src_low); } -int snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, - u8 twice_nat) +int +snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, + u8 twice_nat) { - snat_address_t * ap; + snat_address_t *ap; snat_interface_t *i; vlib_thread_main_t *tm = vlib_get_thread_main (); @@ -674,11 +741,13 @@ int snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, return VNET_API_ERROR_FEATURE_DISABLED; /* Check if address already exists */ + /* *INDENT-OFF* */ vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses) { if (ap->addr.as_u32 == addr->as_u32) return VNET_API_ERROR_VALUE_EXIST; } + /* *INDENT-ON* */ if (twice_nat) vec_add2 (sm->twice_nat_addresses, ap, 1); @@ -689,20 +758,21 @@ int snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, if (vrf_id != ~0) ap->fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, - FIB_SOURCE_PLUGIN_LOW); + nat_fib_src_low); else ap->fib_index = ~0; #define _(N, i, n, s) \ - clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); \ + clib_memset(ap->busy_##n##_port_refcounts, 0, sizeof(ap->busy_##n##_port_refcounts));\ ap->busy_##n##_ports = 0; \ + ap->busy_##n##_ports_per_thread = 0;\ vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0); foreach_snat_protocol #undef _ - - if (twice_nat) + if (twice_nat) return 0; /* Add external address to FIB */ + /* *INDENT-OFF* */ pool_foreach (i, sm->interfaces, ({ if (nat_interface_is_inside(i) || sm->out2in_dpo) @@ -719,42 +789,41 @@ int snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); break; })); + /* *INDENT-ON* */ return 0; } -static int is_snat_address_used_in_static_mapping (snat_main_t *sm, - ip4_address_t addr) +static int +is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr) { snat_static_mapping_t *m; + /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ + if (is_addr_only_static_mapping (m) || + is_out2in_only_static_mapping (m) || + is_identity_static_mapping (m)) + continue; if (m->external_addr.as_u32 == addr.as_u32) return 1; })); + /* *INDENT-ON* */ return 0; } -void increment_v4_address (ip4_address_t * a) -{ - u32 v; - - v = clib_net_to_host_u32(a->as_u32) + 1; - a->as_u32 = clib_host_to_net_u32(v); -} - static void snat_add_static_mapping_when_resolved (snat_main_t * sm, - ip4_address_t l_addr, - u16 l_port, - u32 sw_if_index, - u16 e_port, - u32 vrf_id, - snat_protocol_t proto, - int addr_only, - int is_add, - u8 * tag) + ip4_address_t l_addr, + u16 l_port, + u32 sw_if_index, + u16 e_port, + u32 vrf_id, + snat_protocol_t proto, + int addr_only, int is_add, u8 * tag, + int twice_nat, int out2in_only, + int identity_nat) { snat_static_map_resolve_t *rp; @@ -767,129 +836,126 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm, rp->proto = proto; rp->addr_only = addr_only; rp->is_add = is_add; + rp->twice_nat = twice_nat; + rp->out2in_only = out2in_only; + rp->identity_nat = identity_nat; rp->tag = vec_dup (tag); } -/** - * @brief Add static mapping. - * - * Create static mapping between local addr+port and external addr+port. - * - * @param l_addr Local IPv4 address. - * @param e_addr External IPv4 address. - * @param l_port Local port number. - * @param e_port External port number. - * @param vrf_id VRF ID. - * @param addr_only If 0 address port and pair mapping, otherwise address only. - * @param sw_if_index External port instead of specific IP address. - * @param is_add If 0 delete static mapping, otherwise add. - * @param twice_nat If value is TWICE_NAT then translate external host address - * and port. - * If value is TWICE_NAT_SELF then translate external host - * address and port whenever external host address equals - * local address of internal host. - * @param out2in_only If 1 rule match only out2in direction - * @param tag - opaque string tag - * - * @returns - */ -int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, int addr_only, - u32 sw_if_index, snat_protocol_t proto, int is_add, - twice_nat_type_t twice_nat, u8 out2in_only, - u8 * tag) +static u32 +get_thread_idx_by_port (u16 e_port) +{ + snat_main_t *sm = &snat_main; + u32 thread_idx = sm->num_workers; + if (sm->num_workers > 1) + { + thread_idx = + sm->first_worker_index + + sm->workers[(e_port - 1024) / sm->port_per_thread]; + } + return thread_idx; +} + +int +snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, int addr_only, + u32 sw_if_index, snat_protocol_t proto, int is_add, + twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag, + u8 identity_nat) { - snat_main_t * sm = &snat_main; + snat_main_t *sm = &snat_main; snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_kv_8_8_t kv, value; snat_address_t *a = 0; u32 fib_index = ~0; - uword * p; snat_interface_t *interface; int i; snat_main_per_thread_data_t *tsm; snat_user_key_t u_key; snat_user_t *u; - dlist_elt_t * head, * elt; + dlist_elt_t *head, *elt; u32 elt_index, head_index; u32 ses_index; u64 user_index; - snat_session_t * s; + snat_session_t *s; snat_static_map_resolve_t *rp, *rp_match = 0; + nat44_lb_addr_port_t *local; + u32 find = ~0; if (!sm->endpoint_dependent) { if (twice_nat || out2in_only) - return VNET_API_ERROR_FEATURE_DISABLED; + return VNET_API_ERROR_FEATURE_DISABLED; } /* If the external address is a specific interface address */ if (sw_if_index != ~0) { - ip4_address_t * first_int_addr; + ip4_address_t *first_int_addr; for (i = 0; i < vec_len (sm->to_resolve); i++) - { - rp = sm->to_resolve + i; - if (rp->sw_if_index != sw_if_index || - rp->l_addr.as_u32 != l_addr.as_u32 || - rp->vrf_id != vrf_id || rp->addr_only != addr_only) - continue; - - if (!addr_only) - { - if (rp->l_port != l_port || rp->e_port != e_port || rp->proto != proto) - continue; - } - - rp_match = rp; - break; - } + { + rp = sm->to_resolve + i; + if (rp->sw_if_index != sw_if_index || + rp->l_addr.as_u32 != l_addr.as_u32 || + rp->vrf_id != vrf_id || rp->addr_only != addr_only) + continue; + + if (!addr_only) + { + if ((rp->l_port != l_port && rp->e_port != e_port) + || rp->proto != proto) + continue; + } + + rp_match = rp; + break; + } /* Might be already set... */ first_int_addr = ip4_interface_first_address - (sm->ip4_main, sw_if_index, 0 /* just want the address*/); + (sm->ip4_main, sw_if_index, 0 /* just want the address */ ); if (is_add) - { - if (rp_match) - return VNET_API_ERROR_VALUE_EXIST; - - snat_add_static_mapping_when_resolved - (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, - addr_only, is_add, tag); - - /* DHCP resolution required? */ - if (first_int_addr == 0) - { - return 0; - } - else - { - e_addr.as_u32 = first_int_addr->as_u32; - /* Identity mapping? */ - if (l_addr.as_u32 == 0) - l_addr.as_u32 = e_addr.as_u32; - } - } + { + if (rp_match) + return VNET_API_ERROR_VALUE_EXIST; + + snat_add_static_mapping_when_resolved + (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, + addr_only, is_add, tag, twice_nat, out2in_only, identity_nat); + + /* DHCP resolution required? */ + if (first_int_addr == 0) + { + return 0; + } + else + { + e_addr.as_u32 = first_int_addr->as_u32; + /* Identity mapping? */ + if (l_addr.as_u32 == 0) + l_addr.as_u32 = e_addr.as_u32; + } + } else - { - if (!rp_match) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - vec_del1 (sm->to_resolve, i); - - if (first_int_addr) - { - e_addr.as_u32 = first_int_addr->as_u32; - /* Identity mapping? */ - if (l_addr.as_u32 == 0) - l_addr.as_u32 = e_addr.as_u32; - } - else - return 0; - } + { + if (!rp_match) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + vec_del1 (sm->to_resolve, i); + + if (first_int_addr) + { + e_addr.as_u32 = first_int_addr->as_u32; + /* Identity mapping? */ + if (l_addr.as_u32 == 0) + l_addr.as_u32 = e_addr.as_u32; + } + else + return 0; + } } m_key.addr = e_addr; @@ -905,286 +971,349 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (is_add) { if (m) - return VNET_API_ERROR_VALUE_EXIST; + { + if (is_identity_static_mapping (m)) + { + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + return VNET_API_ERROR_VALUE_EXIST; + })); + /* *INDENT-ON* */ + pool_get (m->locals, local); + local->vrf_id = vrf_id; + local->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + nat_fib_src_low); + m_key.addr = m->local_addr; + m_key.port = m->local_port; + m_key.protocol = m->proto; + m_key.fib_index = local->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + return 0; + } + else + return VNET_API_ERROR_VALUE_EXIST; + } if (twice_nat && addr_only) - return VNET_API_ERROR_UNSUPPORTED; + return VNET_API_ERROR_UNSUPPORTED; /* Convert VRF id to FIB index */ if (vrf_id != ~0) - { - p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id); - if (!p) - return VNET_API_ERROR_NO_SUCH_FIB; - fib_index = p[0]; - } + fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + nat_fib_src_low); /* If not specified use inside VRF id from SNAT plugin startup config */ else - { - fib_index = sm->inside_fib_index; - vrf_id = sm->inside_vrf_id; - } + { + fib_index = sm->inside_fib_index; + vrf_id = sm->inside_vrf_id; + fib_table_lock (fib_index, FIB_PROTOCOL_IP4, nat_fib_src_low); + } - if (!out2in_only) - { - m_key.addr = l_addr; - m_key.port = addr_only ? 0 : l_port; - m_key.protocol = addr_only ? 0 : proto; - m_key.fib_index = fib_index; - kv.key = m_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) - return VNET_API_ERROR_VALUE_EXIST; - } + if (!(out2in_only || identity_nat)) + { + m_key.addr = l_addr; + m_key.port = addr_only ? 0 : l_port; + m_key.protocol = addr_only ? 0 : proto; + m_key.fib_index = fib_index; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_local, &kv, &value)) + return VNET_API_ERROR_VALUE_EXIST; + } /* Find external address in allocated addresses and reserve port for address and port pair mapping when dynamic translations enabled */ if (!(addr_only || sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - /* External port must be unused */ - switch (proto) - { + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + if (a->busy_##n##_port_refcounts[e_port]) \ return VNET_API_ERROR_INVALID_VALUE; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + ++a->busy_##n##_port_refcounts[e_port]; \ if (e_port > 1024) \ { \ a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ } \ break; - foreach_snat_protocol + foreach_snat_protocol #undef _ - default: - nat_log_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - /* External address must be allocated */ - if (!a && (l_addr.as_u32 != e_addr.as_u32)) - { - if (sw_if_index != ~0) - { - for (i = 0; i < vec_len (sm->to_resolve); i++) - { - rp = sm->to_resolve + i; - if (rp->addr_only) - continue; - if (rp->sw_if_index != sw_if_index && - rp->l_addr.as_u32 != l_addr.as_u32 && - rp->vrf_id != vrf_id && rp->l_port != l_port && - rp->e_port != e_port && rp->proto != proto) - continue; - - vec_del1 (sm->to_resolve, i); - break; - } - } - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - } + default: + nat_elog_info ("unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a && (l_addr.as_u32 != e_addr.as_u32)) + { + if (sw_if_index != ~0) + { + for (i = 0; i < vec_len (sm->to_resolve); i++) + { + rp = sm->to_resolve + i; + if (rp->addr_only) + continue; + if (rp->sw_if_index != sw_if_index && + rp->l_addr.as_u32 != l_addr.as_u32 && + rp->vrf_id != vrf_id && rp->l_port != l_port && + rp->e_port != e_port && rp->proto != proto) + continue; + + vec_del1 (sm->to_resolve, i); + break; + } + } + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + } pool_get (sm->static_mappings, m); - memset (m, 0, sizeof (*m)); + clib_memset (m, 0, sizeof (*m)); m->tag = vec_dup (tag); m->local_addr = l_addr; m->external_addr = e_addr; - m->addr_only = addr_only; - m->vrf_id = vrf_id; - m->fib_index = fib_index; m->twice_nat = twice_nat; - m->out2in_only = out2in_only; + if (out2in_only) + m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY; + if (addr_only) + m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY; + if (identity_nat) + { + m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; + pool_get (m->locals, local); + local->vrf_id = vrf_id; + local->fib_index = fib_index; + } + else + { + m->vrf_id = vrf_id; + m->fib_index = fib_index; + } if (!addr_only) - { - m->local_port = l_port; - m->external_port = e_port; - m->proto = proto; - } + { + m->local_port = l_port; + m->external_port = e_port; + m->proto = proto; + } if (sm->num_workers > 1) - { - ip4_header_t ip = { - .src_address = m->local_addr, - }; - vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index)); - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); - } + { + ip4_header_t ip = { + .src_address = m->local_addr, + }; + vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0)); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); + } else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); m_key.addr = m->local_addr; m_key.port = m->local_port; m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; + m_key.fib_index = fib_index; kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; if (!out2in_only) - clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); m_key.addr = m->external_addr; m_key.port = m->external_port; m_key.fib_index = 0; kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; - clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1); /* Delete dynamic sessions matching local address (+ local port) */ if (!(sm->static_mapping_only)) - { - u_key.addr = m->local_addr; - u_key.fib_index = m->fib_index; - kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) - { - user_index = value.value; - u = pool_elt_at_index (tsm->users, user_index); - if (u->nsessions) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - while (ses_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, ses_index); - elt = pool_elt_at_index (tsm->list_pool, elt->next); - ses_index = elt->value; - - if (snat_is_session_static (s)) - continue; - - if (!addr_only && (clib_net_to_host_u16 (s->in2out.port) != m->local_port)) - continue; - - nat_free_session_data (sm, s, tsm - sm->per_thread_data); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); - - if (!addr_only) - break; - } - } - } - } + { + u_key.addr = m->local_addr; + u_key.fib_index = m->fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + { + user_index = value.value; + u = pool_elt_at_index (tsm->users, user_index); + if (u->nsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt = pool_elt_at_index (tsm->list_pool, elt->next); + ses_index = elt->value; + + if (snat_is_session_static (s)) + continue; + + if (!addr_only + && (clib_net_to_host_u16 (s->in2out.port) != + m->local_port)) + continue; + + nat_free_session_data (sm, s, + tsm - sm->per_thread_data, 0); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); + + if (!addr_only && !sm->endpoint_dependent) + break; + } + } + } + } } else { if (!m) - { - if (sw_if_index != ~0) - return 0; - else - return VNET_API_ERROR_NO_SUCH_ENTRY; - } + { + if (sw_if_index != ~0) + return 0; + else + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + if (identity_nat) + { + if (vrf_id == ~0) + vrf_id = sm->inside_vrf_id; + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + find = local - m->locals; + })); + /* *INDENT-ON* */ + if (find == ~0) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + local = pool_elt_at_index (m->locals, find); + fib_index = local->fib_index; + pool_put (m->locals, local); + } + else + fib_index = m->fib_index; /* Free external address port */ if (!(addr_only || sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + --a->busy_##n##_port_refcounts[e_port]; \ if (e_port > 1024) \ { \ a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ } \ break; - foreach_snat_protocol + foreach_snat_protocol #undef _ - default: - nat_log_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - } + default: + nat_elog_info ("unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } if (sm->num_workers > 1) - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); m_key.addr = m->local_addr; m_key.port = m->local_port; m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; + m_key.fib_index = fib_index; kv.key = m_key.as_u64; if (!out2in_only) - clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0); - - m_key.addr = m->external_addr; - m_key.port = m->external_port; - m_key.fib_index = 0; - kv.key = m_key.as_u64; - clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); /* Delete session(s) for static mapping if exist */ if (!(sm->static_mapping_only) || - (sm->static_mapping_only && sm->static_mapping_connection_tracking)) - { - u_key.addr = m->local_addr; - u_key.fib_index = m->fib_index; - kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) - { - user_index = value.value; - u = pool_elt_at_index (tsm->users, user_index); - if (u->nstaticsessions) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - while (ses_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, ses_index); - elt = pool_elt_at_index (tsm->list_pool, elt->next); - ses_index = elt->value; - - if (!addr_only) - { - if ((s->out2in.addr.as_u32 != e_addr.as_u32) && - (clib_net_to_host_u16 (s->out2in.port) != e_port)) - continue; - } - - if (is_lb_session (s)) - continue; - - if (!snat_is_session_static (s)) - continue; + (sm->static_mapping_only && sm->static_mapping_connection_tracking)) + { + u_key.addr = m->local_addr; + u_key.fib_index = fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + { + user_index = value.value; + u = pool_elt_at_index (tsm->users, user_index); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt = pool_elt_at_index (tsm->list_pool, elt->next); + ses_index = elt->value; + + if (!addr_only) + { + if ((s->out2in.addr.as_u32 != e_addr.as_u32) || + (clib_net_to_host_u16 (s->out2in.port) != + e_port)) + continue; + } + + if (is_lb_session (s)) + continue; + + if (!snat_is_session_static (s)) + continue; + + nat_free_session_data (sm, s, + tsm - sm->per_thread_data, 0); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); + + if (!addr_only && !sm->endpoint_dependent) + break; + } + } + } + } - nat_free_session_data (sm, s, tsm - sm->per_thread_data); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, nat_fib_src_low); + if (pool_elts (m->locals)) + return 0; - if (!addr_only) - break; - } - if (addr_only && (u->nstaticsessions == 0) && (u->nsessions == 0)) - { - pool_put (tsm->users, u); - clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0); - } - } - } - } + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.fib_index = 0; + kv.key = m_key.as_u64; + clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0); vec_free (m->tag); vec_free (m->workers); @@ -1196,6 +1325,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, return 0; /* Add/delete external address to FIB */ + /* *INDENT-OFF* */ pool_foreach (interface, sm->interfaces, ({ if (nat_interface_is_inside(interface) || sm->out2in_dpo) @@ -1212,21 +1342,22 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); break; })); + /* *INDENT-ON* */ return 0; } -int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - snat_protocol_t proto, u32 vrf_id, - nat44_lb_addr_port_t *locals, u8 is_add, - twice_nat_type_t twice_nat, u8 out2in_only, - u8 *tag) +int +nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, + snat_protocol_t proto, + nat44_lb_addr_port_t * locals, u8 is_add, + twice_nat_type_t twice_nat, u8 out2in_only, + u8 * tag, u32 affinity) { - snat_main_t * sm = &snat_main; + snat_main_t *sm = &snat_main; snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_kv_8_8_t kv, value; - u32 fib_index; snat_address_t *a = 0; int i; nat44_lb_addr_port_t *local; @@ -1234,8 +1365,8 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, snat_main_per_thread_data_t *tsm; snat_user_key_t u_key; snat_user_t *u; - snat_session_t * s; - dlist_elt_t * head, * elt; + snat_session_t *s; + dlist_elt_t *head, *elt; uword *bitmap = 0; if (!sm->endpoint_dependent) @@ -1254,63 +1385,65 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (is_add) { if (m) - return VNET_API_ERROR_VALUE_EXIST; + return VNET_API_ERROR_VALUE_EXIST; if (vec_len (locals) < 2) - return VNET_API_ERROR_INVALID_VALUE; - - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - vrf_id, - FIB_SOURCE_PLUGIN_LOW); + return VNET_API_ERROR_INVALID_VALUE; /* Find external address in allocated addresses and reserve port for address and port pair mapping when dynamic translations enabled */ if (!(sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - /* External port must be unused */ - switch (proto) - { + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + if (a->busy_##n##_port_refcounts[e_port]) \ return VNET_API_ERROR_INVALID_VALUE; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + ++a->busy_##n##_port_refcounts[e_port]; \ if (e_port > 1024) \ { \ a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ } \ break; - foreach_snat_protocol + foreach_snat_protocol #undef _ - default: - nat_log_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - /* External address must be allocated */ - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - } + default: + nat_elog_info ("unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + } pool_get (sm->static_mappings, m); - memset (m, 0, sizeof (*m)); + clib_memset (m, 0, sizeof (*m)); m->tag = vec_dup (tag); m->external_addr = e_addr; - m->addr_only = 0; - m->vrf_id = vrf_id; - m->fib_index = fib_index; m->external_port = e_port; m->proto = proto; m->twice_nat = twice_nat; - m->out2in_only = out2in_only; + m->flags |= NAT_STATIC_MAPPING_FLAG_LB; + if (out2in_only) + m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY; + m->affinity = affinity; + + if (affinity) + m->affinity_per_service_list_head_index = + nat_affinity_get_per_service_list_head_index (); + else + m->affinity_per_service_list_head_index = ~0; m_key.addr = m->external_addr; m_key.port = m->external_port; @@ -1318,104 +1451,118 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, m_key.fib_index = 0; kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; - if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1)) - { - nat_log_err ("static_mapping_by_external key add failed"); - return VNET_API_ERROR_UNSPECIFIED; - } + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1)) + { + nat_elog_err ("static_mapping_by_external key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } m_key.fib_index = m->fib_index; for (i = 0; i < vec_len (locals); i++) - { - m_key.addr = locals[i].addr; - if (!out2in_only) - { - m_key.port = locals[i].port; - kv.key = m_key.as_u64; - kv.value = m - sm->static_mappings; - clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); - } - locals[i].prefix = (i == 0) ? locals[i].probability :\ - (locals[i - 1].prefix + locals[i].probability); - vec_add1 (m->locals, locals[i]); - if (sm->num_workers > 1) - { - ip4_header_t ip = { - .src_address = locals[i].addr, - }; - bitmap = clib_bitmap_set ( - bitmap, sm->worker_in2out_cb (&ip, m->fib_index), 1); - } - } + { + locals[i].fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + locals[i].vrf_id, + nat_fib_src_low); + m_key.addr = locals[i].addr; + m_key.fib_index = locals[i].fib_index; + if (!out2in_only) + { + m_key.port = locals[i].port; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + } + locals[i].prefix = (i == 0) ? locals[i].probability : + (locals[i - 1].prefix + locals[i].probability); + pool_get (m->locals, local); + *local = locals[i]; + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = locals[i].addr, + }; + bitmap = + clib_bitmap_set (bitmap, + sm->worker_in2out_cb (&ip, m->fib_index, 0), + 1); + } + } /* Assign workers */ if (sm->num_workers > 1) - { + { + /* *INDENT-OFF* */ clib_bitmap_foreach (i, bitmap, ({ vec_add1(m->workers, i); })); - } + /* *INDENT-ON* */ + } } else { if (!m) - return VNET_API_ERROR_NO_SUCH_ENTRY; + return VNET_API_ERROR_NO_SUCH_ENTRY; - fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW); + if (!is_lb_static_mapping (m)) + return VNET_API_ERROR_INVALID_VALUE; /* Free external address port */ if (!(sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + --a->busy_##n##_port_refcounts[e_port]; \ if (e_port > 1024) \ { \ a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ } \ break; - foreach_snat_protocol + foreach_snat_protocol #undef _ - default: - nat_log_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - } + default: + nat_elog_info ("unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } m_key.addr = m->external_addr; m_key.port = m->external_port; m_key.protocol = m->proto; m_key.fib_index = 0; kv.key = m_key.as_u64; - if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0)) - { - nat_log_err ("static_mapping_by_external key del failed"); - return VNET_API_ERROR_UNSPECIFIED; - } + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0)) + { + nat_elog_err ("static_mapping_by_external key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } - vec_foreach (local, m->locals) - { + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, + nat_fib_src_low); m_key.addr = local->addr; if (!out2in_only) { m_key.port = local->port; - m_key.fib_index = m->fib_index; + m_key.fib_index = local->fib_index; kv.key = m_key.as_u64; if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0)) { - nat_log_err ("static_mapping_by_local key del failed"); + nat_elog_err ("static_mapping_by_local key del failed"); return VNET_API_ERROR_UNSPECIFIED; } } @@ -1426,14 +1573,14 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, .src_address = local->addr, }; tsm = vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, m->fib_index)); + sm->worker_in2out_cb (&ip, m->fib_index, 0)); } else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); /* Delete sessions */ u_key.addr = local->addr; - u_key.fib_index = m->fib_index; + u_key.fib_index = local->fib_index; kv.key = u_key.as_u64; if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { @@ -1454,19 +1601,22 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (!(is_lb_session (s))) continue; - if ((s->in2out.addr.as_u32 != local->addr.as_u32) && + if ((s->in2out.addr.as_u32 != local->addr.as_u32) || (clib_net_to_host_u16 (s->in2out.port) != local->port)) continue; - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); } } } - } - vec_free(m->locals); - vec_free(m->tag); - vec_free(m->workers); + })); + /* *INDENT-ON* */ + if (m->affinity) + nat_affinity_flush_service (m->affinity_per_service_list_head_index); + pool_free (m->locals); + vec_free (m->tag); + vec_free (m->workers); pool_put (sm->static_mappings, m); } @@ -1475,66 +1625,252 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } int -snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm, - u8 twice_nat) +nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, + ip4_address_t l_addr, u16 l_port, + snat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add) { - snat_address_t *a = 0; - snat_session_t *ses; - u32 *ses_to_be_removed = 0, *ses_index; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m = 0; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + nat44_lb_addr_port_t *local, *prev_local, *match_local = 0; snat_main_per_thread_data_t *tsm; - snat_static_mapping_t *m; - snat_interface_t *interface; + snat_user_key_t u_key; + snat_user_t *u; + snat_session_t *s; + dlist_elt_t *head, *elt; + u32 elt_index, head_index, ses_index, *locals = 0; + uword *bitmap = 0; int i; - snat_address_t *addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses; - /* Find SNAT address */ - for (i=0; i < vec_len (addresses); i++) - { - if (addresses[i].addr.as_u32 == addr.as_u32) - { - a = addresses + i; - break; - } - } - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; + if (!sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; - if (delete_sm) - { + m_key.addr = e_addr; + m_key.port = e_port; + m_key.protocol = proto; + m_key.fib_index = 0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (!is_lb_static_mapping (m)) + return VNET_API_ERROR_INVALID_VALUE; + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if ((local->addr.as_u32 == l_addr.as_u32) && (local->port == l_port) && + (local->vrf_id == vrf_id)) + { + match_local = local; + break; + } + })); + /* *INDENT-ON* */ + + if (is_add) + { + if (match_local) + return VNET_API_ERROR_VALUE_EXIST; + + pool_get (m->locals, local); + clib_memset (local, 0, sizeof (*local)); + local->addr.as_u32 = l_addr.as_u32; + local->port = l_port; + local->probability = probability; + local->vrf_id = vrf_id; + local->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + nat_fib_src_low); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = local->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1)) + nat_elog_err ("static_mapping_by_local key add failed"); + } + } + else + { + if (!match_local) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (pool_elts (m->locals) < 3) + return VNET_API_ERROR_UNSPECIFIED; + + fib_table_unlock (match_local->fib_index, FIB_PROTOCOL_IP4, + nat_fib_src_low); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = match_local->fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0)) + nat_elog_err ("static_mapping_by_local key del failed"); + } + + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = local->addr, + }; + tsm = vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, m->fib_index, + 0)); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + /* Delete sessions */ + u_key.addr = match_local->addr; + u_key.fib_index = match_local->fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tsm->users, value.value); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt = pool_elt_at_index (tsm->list_pool, elt->next); + ses_index = elt->value; + + if (!(is_lb_session (s))) + continue; + + if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || + (clib_net_to_host_u16 (s->in2out.port) != + match_local->port)) + continue; + + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); + } + } + } + + pool_put (m->locals, match_local); + } + + vec_free (m->workers); + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + vec_add1 (locals, local - m->locals); + if (sm->num_workers > 1) + { + ip4_header_t ip; + ip.src_address.as_u32 = local->addr.as_u32, + bitmap = clib_bitmap_set (bitmap, + sm->worker_in2out_cb (&ip, local->fib_index, 0), + 1); + } + })); + /* *INDENT-ON* */ + + ASSERT (vec_len (locals) > 1); + + local = pool_elt_at_index (m->locals, locals[0]); + local->prefix = local->probability; + for (i = 1; i < vec_len (locals); i++) + { + local = pool_elt_at_index (m->locals, locals[i]); + prev_local = pool_elt_at_index (m->locals, locals[i - 1]); + local->prefix = local->probability + prev_local->prefix; + } + + /* Assign workers */ + if (sm->num_workers > 1) + { + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap, ({ vec_add1(m->workers, i); })); + /* *INDENT-ON* */ + } + + return 0; +} + +int +snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, + u8 twice_nat) +{ + snat_address_t *a = 0; + snat_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + snat_main_per_thread_data_t *tsm; + snat_static_mapping_t *m; + snat_interface_t *interface; + int i; + snat_address_t *addresses = + twice_nat ? sm->twice_nat_addresses : sm->addresses; + + /* Find SNAT address */ + for (i = 0; i < vec_len (addresses); i++) + { + if (addresses[i].addr.as_u32 == addr.as_u32) + { + a = addresses + i; + break; + } + } + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (delete_sm) + { + /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ if (m->external_addr.as_u32 == addr.as_u32) (void) snat_add_static_mapping (m->local_addr, m->external_addr, m->local_port, m->external_port, - m->vrf_id, m->addr_only, ~0, + m->vrf_id, is_addr_only_static_mapping(m), ~0, m->proto, 0, m->twice_nat, - m->out2in_only, m->tag); + is_out2in_only_static_mapping(m), m->tag, is_identity_static_mapping(m)); })); + /* *INDENT-ON* */ } else { /* Check if address is used in some static mapping */ - if (is_snat_address_used_in_static_mapping(sm, addr)) - { - nat_log_notice ("address used in static mapping"); - return VNET_API_ERROR_UNSPECIFIED; - } + if (is_snat_address_used_in_static_mapping (sm, addr)) + { + nat_elog_notice ("address used in static mapping"); + return VNET_API_ERROR_UNSPECIFIED; + } } if (a->fib_index != ~0) - fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4, - FIB_SOURCE_PLUGIN_LOW); + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, nat_fib_src_low); /* Delete sessions using address */ if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) { + /* *INDENT-OFF* */ vec_foreach (tsm, sm->per_thread_data) { pool_foreach (ses, tsm->sessions, ({ if (ses->out2in.addr.as_u32 == addr.as_u32) { - ses->outside_address_index = ~0; - nat_free_session_data (sm, ses, tsm - sm->per_thread_data); + nat_free_session_data (sm, ses, tsm - sm->per_thread_data, 0); vec_add1 (ses_to_be_removed, ses - tsm->sessions); } })); @@ -1546,10 +1882,15 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm, } vec_free (ses_to_be_removed); - } + } + /* *INDENT-ON* */ } - if (twice_nat) +#define _(N, i, n, s) \ + vec_free (a->busy_##n##_ports_per_thread); + foreach_snat_protocol +#undef _ + if (twice_nat) { vec_del1 (sm->twice_nat_addresses, i); return 0; @@ -1558,6 +1899,7 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm, vec_del1 (sm->addresses, i); /* Delete external address from FIB */ + /* *INDENT-OFF* */ pool_foreach (interface, sm->interfaces, ({ if (nat_interface_is_inside(interface) || sm->out2in_dpo) @@ -1574,62 +1916,71 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm, snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); break; })); + /* *INDENT-ON* */ return 0; } -int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) +int +snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) { snat_main_t *sm = &snat_main; snat_interface_t *i; - const char * feature_name, *del_feature_name; - snat_address_t * ap; - snat_static_mapping_t * m; - snat_det_map_t * dm; + const char *feature_name, *del_feature_name; + snat_address_t *ap; + snat_static_mapping_t *m; + snat_det_map_t *dm; nat_outside_fib_t *outside_fib; u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, - sw_if_index); + sw_if_index); if (sm->out2in_dpo && !is_inside) return VNET_API_ERROR_UNSUPPORTED; + /* *INDENT-OFF* */ pool_foreach (i, sm->output_feature_interfaces, ({ if (i->sw_if_index == sw_if_index) return VNET_API_ERROR_VALUE_EXIST; })); + /* *INDENT-ON* */ if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) - feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast"; + feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast"; else { if (sm->num_workers > 1 && !sm->deterministic) - feature_name = is_inside ? "nat44-in2out-worker-handoff" : "nat44-out2in-worker-handoff"; + feature_name = + is_inside ? "nat44-in2out-worker-handoff" : + "nat44-out2in-worker-handoff"; else if (sm->deterministic) - feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in"; + feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in"; else if (sm->endpoint_dependent) - feature_name = is_inside ? "nat44-ed-in2out" : "nat44-ed-out2in"; + { + feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; + } else - feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; + feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; } if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1) - sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, - NAT_FQ_NELTS); + sm->fq_in2out_index = + vlib_frame_queue_main_init (sm->handoff_in2out_index, NAT_FQ_NELTS); if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1) - sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, - NAT_FQ_NELTS); + sm->fq_out2in_index = + vlib_frame_queue_main_init (sm->handoff_out2in_index, NAT_FQ_NELTS); if (!is_inside) { + /* *INDENT-OFF* */ vec_foreach (outside_fib, sm->outside_fibs) { if (outside_fib->fib_index == fib_index) { if (is_del) { - outside_fib->refcount--; + outside_fib->refcount--; if (!outside_fib->refcount) vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); } @@ -1638,14 +1989,16 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) goto feature_set; } } + /* *INDENT-ON* */ if (!is_del) - { - vec_add2 (sm->outside_fibs, outside_fib, 1); - outside_fib->refcount = 1; - outside_fib->fib_index = fib_index; - } + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } } feature_set: + /* *INDENT-OFF* */ pool_foreach (i, sm->interfaces, ({ if (i->sw_if_index == sw_if_index) @@ -1674,8 +2027,8 @@ feature_set: else if (sm->endpoint_dependent) { del_feature_name = "nat44-ed-classify"; - feature_name = !is_inside ? "nat44-ed-in2out" : - "nat44-ed-out2in"; + feature_name = !is_inside ? "nat-pre-in2out" : + "nat-pre-out2in"; } else { @@ -1683,6 +2036,9 @@ feature_set: feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in"; } + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; vnet_feature_enable_disable ("ip4-unicast", del_feature_name, sw_if_index, 0, 0, 0); vnet_feature_enable_disable ("ip4-unicast", feature_name, @@ -1693,7 +2049,7 @@ feature_set: vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", sw_if_index, 1, 0, 0); - else + else if (!sm->deterministic) vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", sw_if_index, 1, 0, 0); @@ -1701,6 +2057,9 @@ feature_set: } else { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 0, 0, 0); pool_put (sm->interfaces, i); @@ -1710,7 +2069,7 @@ feature_set: vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", sw_if_index, 0, 0, 0); - else + else if (!sm->deterministic) vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", sw_if_index, 0, 0, 0); @@ -1737,8 +2096,9 @@ feature_set: } else if (sm->endpoint_dependent) { - del_feature_name = !is_inside ? "nat44-ed-in2out" : - "nat44-ed-out2in"; + del_feature_name = !is_inside ? "nat-pre-in2out" : + "nat-pre-out2in"; + feature_name = "nat44-ed-classify"; } else @@ -1747,6 +2107,9 @@ feature_set: feature_name = "nat44-classify"; } + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; vnet_feature_enable_disable ("ip4-unicast", del_feature_name, sw_if_index, 0, 0, 0); vnet_feature_enable_disable ("ip4-unicast", feature_name, @@ -1756,7 +2119,7 @@ feature_set: if (sm->endpoint_dependent) vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", sw_if_index, 0, 0, 0); - else + else if (!sm->deterministic) vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", sw_if_index, 0, 0, 0); } @@ -1766,6 +2129,7 @@ feature_set: goto fib; } })); + /* *INDENT-ON* */ if (is_del) return VNET_API_ERROR_NO_SUCH_ENTRY; @@ -1773,16 +2137,21 @@ feature_set: pool_get (sm->interfaces, i); i->sw_if_index = sw_if_index; i->flags = 0; - vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, + 0); + + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; 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 - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", + sw_if_index, 1, 0, 0); + else if (!sm->deterministic) + vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", + sw_if_index, 1, 0, 0); } set_flags: @@ -1796,12 +2165,13 @@ set_flags: /* Add/delete external addresses to FIB */ fib: + /* *INDENT-OFF* */ vec_foreach (ap, sm->addresses) snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); pool_foreach (m, sm->static_mappings, ({ - if (!(m->addr_only) || (m->local_addr.as_u32 == m->external_addr.as_u32)) + if (!(is_addr_only_static_mapping(m)) || (m->local_addr.as_u32 == m->external_addr.as_u32)) continue; snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); @@ -1811,83 +2181,163 @@ fib: ({ snat_add_del_addr_to_fib(&dm->out_addr, dm->out_plen, sw_if_index, !is_del); })); + /* *INDENT-ON* */ return 0; } -int snat_interface_add_del_output_feature (u32 sw_if_index, - u8 is_inside, - int is_del) +int +snat_interface_add_del_output_feature (u32 sw_if_index, + u8 is_inside, int is_del) { snat_main_t *sm = &snat_main; snat_interface_t *i; - snat_address_t * ap; - snat_static_mapping_t * m; + snat_address_t *ap; + snat_static_mapping_t *m; + nat_outside_fib_t *outside_fib; + u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index); + if (sm->deterministic || (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))) return VNET_API_ERROR_UNSUPPORTED; + /* *INDENT-OFF* */ pool_foreach (i, sm->interfaces, ({ if (i->sw_if_index == sw_if_index) return VNET_API_ERROR_VALUE_EXIST; })); + /* *INDENT-ON* */ - if (is_inside) + if (!is_inside) { - if (sm->endpoint_dependent) + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) { - 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); + if (outside_fib->fib_index == fib_index) + { + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; + } } + /* *INDENT-ON* */ + if (!is_del) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } + } + +feature_set: + if (is_inside) + { + if (sm->endpoint_dependent) + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !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 - { - vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", - sw_if_index, !is_del, 0, 0); - } + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", + sw_if_index, !is_del, 0, 0); + } goto fq; } if (sm->num_workers > 1) { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; vnet_feature_enable_disable ("ip4-unicast", - "nat44-out2in-worker-handoff", - sw_if_index, !is_del, 0, 0); + "nat44-out2in-worker-handoff", + sw_if_index, !is_del, 0, 0); vnet_feature_enable_disable ("ip4-output", - "nat44-in2out-output-worker-handoff", - sw_if_index, !is_del, 0, 0); + "nat44-in2out-output-worker-handoff", + sw_if_index, !is_del, 0, 0); } else { if (sm->endpoint_dependent) - { - vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-out2in", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output", - sw_if_index, !is_del, 0, 0); - } + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output", + sw_if_index, !is_del, 0, 0); + } else - { - vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", - sw_if_index, !is_del, 0, 0); - } + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", + sw_if_index, !is_del, 0, 0); + } } fq: if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1) sm->fq_in2out_output_index = - vlib_frame_queue_main_init (sm->in2out_output_node_index, 0); + vlib_frame_queue_main_init (sm->handoff_in2out_output_index, 0); if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) - sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0); + sm->fq_out2in_index = + vlib_frame_queue_main_init (sm->handoff_out2in_index, 0); + /* *INDENT-OFF* */ pool_foreach (i, sm->output_feature_interfaces, ({ if (i->sw_if_index == sw_if_index) @@ -1900,6 +2350,7 @@ fq: goto fib; } })); + /* *INDENT-ON* */ if (is_del) return VNET_API_ERROR_NO_SUCH_ENTRY; @@ -1917,21 +2368,24 @@ fib: if (is_inside) return 0; + /* *INDENT-OFF* */ vec_foreach (ap, sm->addresses) snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); pool_foreach (m, sm->static_mappings, ({ - if (!(m->addr_only) || (m->local_addr.as_u32 == m->external_addr.as_u32)) + if (!((is_addr_only_static_mapping(m))) || (m->local_addr.as_u32 == m->external_addr.as_u32)) continue; snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); })); + /* *INDENT-ON* */ return 0; } -int snat_set_workers (uword * bitmap) +int +snat_set_workers (uword * bitmap) { snat_main_t *sm = &snat_main; int i, j = 0; @@ -1943,12 +2397,15 @@ int snat_set_workers (uword * bitmap) return VNET_API_ERROR_INVALID_WORKER; vec_free (sm->workers); + /* *INDENT-OFF* */ clib_bitmap_foreach (i, bitmap, ({ vec_add1(sm->workers, i); sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j; + sm->per_thread_data[sm->first_worker_index + i].thread_index = i; j++; })); + /* *INDENT-ON* */ sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); sm->num_snat_thread = _vec_len (sm->workers); @@ -1956,79 +2413,210 @@ int snat_set_workers (uword * bitmap) return 0; } +static void +snat_update_outside_fib (u32 sw_if_index, u32 new_fib_index, + u32 old_fib_index) +{ + snat_main_t *sm = &snat_main; + nat_outside_fib_t *outside_fib; + snat_interface_t *i; + u8 is_add = 1; + u8 match = 0; + + if (new_fib_index == old_fib_index) + return; + + if (!vec_len (sm->outside_fibs)) + return; + + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces, + ({ + if (i->sw_if_index == sw_if_index) + { + if (!(nat_interface_is_outside (i))) + return; + match = 1; + } + })); + + pool_foreach (i, sm->output_feature_interfaces, + ({ + if (i->sw_if_index == sw_if_index) + { + if (!(nat_interface_is_outside (i))) + return; + match = 1; + } + })); + /* *INDENT-ON* */ + + if (!match) + return; + + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == old_fib_index) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + break; + } + } + + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == new_fib_index) + { + outside_fib->refcount++; + is_add = 0; + break; + } + } + + if (is_add) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = new_fib_index; + } +} + +static void +snat_ip4_table_bind (ip4_main_t * im, + uword opaque, + u32 sw_if_index, u32 new_fib_index, u32 old_fib_index) +{ + snat_update_outside_fib (sw_if_index, new_fib_index, old_fib_index); +} static void snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete); + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete); static void nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete); + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete); static int nat_alloc_addr_and_port_default (snat_address_t * addresses, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp, - u16 port_per_thread, - u32 snat_thread_index); - -static clib_error_t * snat_init (vlib_main_t * vm) + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u16 port_per_thread, u32 snat_thread_index); + +static clib_error_t * +snat_init (vlib_main_t * vm) { - snat_main_t * sm = &snat_main; - clib_error_t * error = 0; - ip4_main_t * im = &ip4_main; - ip_lookup_main_t * lm = &im->lookup_main; + snat_main_t *sm = &snat_main; + clib_error_t *error = 0; + ip4_main_t *im = &ip4_main; + ip_lookup_main_t *lm = &im->lookup_main; uword *p; vlib_thread_registration_t *tr; vlib_thread_main_t *tm = vlib_get_thread_main (); uword *bitmap = 0; u32 i; ip4_add_del_interface_address_callback_t cb4; - vlib_node_t * error_drop_node; + vlib_node_t *node; sm->vlib_main = vm; - sm->vnet_main = vnet_get_main(); + sm->vnet_main = vnet_get_main (); sm->ip4_main = im; sm->ip4_lookup_main = lm; - sm->api_main = &api_main; + sm->api_main = vlibapi_get_main (); sm->first_worker_index = 0; sm->num_workers = 0; sm->num_snat_thread = 1; sm->workers = 0; sm->port_per_thread = 0xffff - 1024; sm->fq_in2out_index = ~0; + sm->fq_in2out_output_index = ~0; sm->fq_out2in_index = ~0; - sm->udp_timeout = SNAT_UDP_TIMEOUT; - sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - sm->icmp_timeout = SNAT_ICMP_TIMEOUT; + sm->alloc_addr_and_port = nat_alloc_addr_and_port_default; + sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; sm->forwarding_enabled = 0; sm->log_class = vlib_log_register_class ("nat", 0); - error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); - sm->error_node_index = error_drop_node->index; + sm->log_level = SNAT_LOG_ERROR; + sm->mss_clamping = 0; + + node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); + sm->error_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out"); + sm->pre_in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in"); + sm->pre_out2in_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out"); + sm->pre_in2out_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in"); + sm->pre_out2in_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out"); + sm->in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output"); + sm->in2out_output_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-fast"); + sm->in2out_fast_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-slowpath"); + sm->in2out_slowpath_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output-slowpath"); + sm->in2out_slowpath_output_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); + sm->ed_in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-slowpath"); + sm->ed_in2out_slowpath_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in"); + sm->out2in_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in-fast"); + sm->out2in_fast_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in"); + sm->ed_out2in_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in-slowpath"); + sm->ed_out2in_slowpath_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-det-in2out"); + sm->det_in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-det-out2in"); + sm->det_out2in_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpinning"); + sm->hairpinning_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-dst"); + 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; p = hash_get_mem (tm->thread_registrations_by_name, "workers"); if (p) { tr = (vlib_thread_registration_t *) p[0]; if (tr) - { - sm->num_workers = tr->count; - sm->first_worker_index = tr->first_index; - } + { + sm->num_workers = tr->count; + sm->first_worker_index = tr->first_index; + } } vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1); @@ -2036,9 +2624,9 @@ static clib_error_t * snat_init (vlib_main_t * vm) /* Use all available workers by default */ if (sm->num_workers > 1) { - for (i=0; i < sm->num_workers; i++) - bitmap = clib_bitmap_set (bitmap, i, 1); - snat_set_workers(bitmap); + for (i = 0; i < sm->num_workers; i++) + bitmap = clib_bitmap_set (bitmap, i, 1); + snat_set_workers (bitmap); clib_bitmap_free (bitmap); } else @@ -2046,7 +2634,7 @@ static clib_error_t * snat_init (vlib_main_t * vm) sm->per_thread_data[0].snat_thread_index = 0; } - error = snat_api_init(vm, sm); + error = snat_api_init (vm, sm); if (error) return error; @@ -2063,32 +2651,58 @@ static clib_error_t * snat_init (vlib_main_t * vm) nat_dpo_module_init (); + /* Init counters */ + sm->total_users.name = "total-users"; + sm->total_users.stat_segment_name = "/nat44/total-users"; + vlib_validate_simple_counter (&sm->total_users, 0); + vlib_zero_simple_counter (&sm->total_users, 0); + sm->total_sessions.name = "total-sessions"; + sm->total_sessions.stat_segment_name = "/nat44/total-sessions"; + vlib_validate_simple_counter (&sm->total_sessions, 0); + vlib_zero_simple_counter (&sm->total_sessions, 0); + /* Init IPFIX logging */ - snat_ipfix_logging_init(vm); + snat_ipfix_logging_init (vm); /* Init NAT64 */ - error = nat64_init(vm); + error = nat64_init (vm); if (error) return error; - dslite_init(vm); + nat66_init (vm); + + ip4_table_bind_callback_t cbt4 = { + .function = snat_ip4_table_bind, + }; + vec_add1 (ip4_main.table_bind_callbacks, cbt4); - nat66_init(); + nat_fib_src_hi = fib_source_allocate ("nat-hi", + FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + nat_fib_src_low = fib_source_allocate ("nat-low", + FIB_SOURCE_PRIORITY_LOW, + FIB_SOURCE_BH_SIMPLE); - /* Init virtual fragmenentation reassembly */ - return nat_reass_init(vm); + return error; } VLIB_INIT_FUNCTION (snat_init); -void snat_free_outside_address_and_port (snat_address_t * addresses, - u32 thread_index, - snat_session_key_t * k, - u32 address_index) +void +snat_free_outside_address_and_port (snat_address_t * addresses, + u32 thread_index, snat_session_key_t * k) { snat_address_t *a; + u32 address_index; u16 port_host_byte_order = clib_net_to_host_u16 (k->port); + for (address_index = 0; address_index < vec_len (addresses); + address_index++) + { + if (addresses[address_index].addr.as_u32 == k->addr.as_u32) + break; + } + ASSERT (address_index < vec_len (addresses)); a = addresses + address_index; @@ -2097,48 +2711,72 @@ void snat_free_outside_address_and_port (snat_address_t * addresses, { #define _(N, i, n, s) \ case SNAT_PROTOCOL_##N: \ - ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - port_host_byte_order) == 1); \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - port_host_byte_order, 0); \ + ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \ + --a->busy_##n##_port_refcounts[port_host_byte_order]; \ a->busy_##n##_ports--; \ a->busy_##n##_ports_per_thread[thread_index]--; \ break; foreach_snat_protocol #undef _ default: - nat_log_info ("unknown protocol"); + nat_elog_info ("unknown protocol"); return; } } -/** - * @brief Match NAT44 static mapping. - * - * @param sm NAT main. - * @param match Address and port to match. - * @param mapping External or local address and port of the matched mapping. - * @param by_external If 0 match by local address otherwise match by external - * address. - * @param is_addr_only If matched mapping is address only - * @param twice_nat If matched mapping is twice NAT. - * @param lb If matched mapping is load-balanced. - * - * @returns 0 if match found otherwise 1. - */ -int snat_static_mapping_match (snat_main_t * sm, - snat_session_key_t match, - snat_session_key_t * mapping, - u8 by_external, - u8 *is_addr_only, - twice_nat_type_t *twice_nat, - u8 *lb) +static int +nat_set_outside_address_and_port (snat_address_t * addresses, + u32 thread_index, snat_session_key_t * k) +{ + snat_address_t *a = 0; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (k->port); + + for (address_index = 0; address_index < vec_len (addresses); + address_index++) + { + if (addresses[address_index].addr.as_u32 != k->addr.as_u32) + continue; + + a = addresses + address_index; + switch (k->protocol) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (a->busy_##n##_port_refcounts[port_host_byte_order]) \ + return VNET_API_ERROR_INSTANCE_IN_USE; \ + ++a->busy_##n##_port_refcounts[port_host_byte_order]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + return 0; + foreach_snat_protocol +#undef _ + default: + nat_elog_info ("unknown protocol"); + return 1; + } + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + +int +snat_static_mapping_match (snat_main_t * sm, + snat_session_key_t match, + snat_session_key_t * mapping, + u8 by_external, + u8 * is_addr_only, + twice_nat_type_t * twice_nat, + lb_nat_type_t * lb, ip4_address_t * ext_host_addr, + u8 * is_identity_nat) { clib_bihash_kv_8_8_t kv, value; snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; - u32 rand, lo = 0, hi, mid; + u32 rand, lo = 0, hi, mid, *tmp = 0, i; + u8 backend_index; + nat44_lb_addr_port_t *local; m_key.fib_index = match.fib_index; if (by_external) @@ -2160,63 +2798,115 @@ int snat_static_mapping_match (snat_main_t * sm, m_key.protocol = 0; kv.key = m_key.as_u64; if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - return 1; + return 1; } m = pool_elt_at_index (sm->static_mappings, value.value); if (by_external) { - if (vec_len (m->locals)) - { -get_local: - hi = vec_len (m->locals) - 1; - rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); - while (lo < hi) - { - mid = ((hi - lo) >> 1) + lo; - (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid); - } - if (!(m->locals[lo].prefix >= rand)) - return 1; - if (PREDICT_FALSE (sm->num_workers > 1)) - { - ip4_header_t ip = { - .src_address = m->locals[lo].addr, - }; - if (sm->worker_in2out_cb (&ip, m->fib_index) != vlib_get_thread_index ()) - goto get_local; - } - mapping->addr = m->locals[lo].addr; - mapping->port = clib_host_to_net_u16 (m->locals[lo].port); - } + if (is_lb_static_mapping (m)) + { + if (PREDICT_FALSE (lb != 0)) + *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT; + if (m->affinity && !nat_affinity_find_and_lock (ext_host_addr[0], + match.addr, + match.protocol, + match.port, + &backend_index)) + { + local = pool_elt_at_index (m->locals, backend_index); + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; + goto end; + } + // pick locals matching this worker + if (PREDICT_FALSE (sm->num_workers > 1)) + { + u32 thread_index = vlib_get_thread_index (); + /* *INDENT-OFF* */ + pool_foreach_index (i, m->locals, + ({ + local = pool_elt_at_index (m->locals, i); + + ip4_header_t ip = { + .src_address = local->addr, + }; + + if (sm->worker_in2out_cb (&ip, m->fib_index, 0) == + thread_index) + { + vec_add1 (tmp, i); + } + })); + /* *INDENT-ON* */ + ASSERT (vec_len (tmp) != 0); + } + else + { + /* *INDENT-OFF* */ + pool_foreach_index (i, m->locals, + ({ + vec_add1 (tmp, i); + })); + /* *INDENT-ON* */ + } + hi = vec_len (tmp) - 1; + local = pool_elt_at_index (m->locals, tmp[hi]); + rand = 1 + (random_u32 (&sm->random_seed) % local->prefix); + while (lo < hi) + { + mid = ((hi - lo) >> 1) + lo; + local = pool_elt_at_index (m->locals, tmp[mid]); + (rand > local->prefix) ? (lo = mid + 1) : (hi = mid); + } + local = pool_elt_at_index (m->locals, tmp[lo]); + if (!(local->prefix >= rand)) + return 1; + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; + if (m->affinity) + { + if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr, + match.protocol, match.port, + tmp[lo], m->affinity, + m->affinity_per_service_list_head_index)) + nat_elog_info ("create affinity record failed"); + } + vec_free (tmp); + } else - { - mapping->addr = m->local_addr; - /* Address only mapping doesn't change port */ - mapping->port = m->addr_only ? match.port - : clib_host_to_net_u16 (m->local_port); - } - mapping->fib_index = m->fib_index; + { + if (PREDICT_FALSE (lb != 0)) + *lb = NO_LB_NAT; + mapping->fib_index = m->fib_index; + mapping->addr = m->local_addr; + /* Address only mapping doesn't change port */ + mapping->port = is_addr_only_static_mapping (m) ? match.port + : clib_host_to_net_u16 (m->local_port); + } mapping->protocol = m->proto; } else { mapping->addr = m->external_addr; /* Address only mapping doesn't change port */ - mapping->port = m->addr_only ? match.port - : clib_host_to_net_u16 (m->external_port); + mapping->port = is_addr_only_static_mapping (m) ? match.port + : clib_host_to_net_u16 (m->external_port); mapping->fib_index = sm->outside_fib_index; } - if (PREDICT_FALSE(is_addr_only != 0)) - *is_addr_only = m->addr_only; +end: + if (PREDICT_FALSE (is_addr_only != 0)) + *is_addr_only = is_addr_only_static_mapping (m); - if (PREDICT_FALSE(twice_nat != 0)) + if (PREDICT_FALSE (twice_nat != 0)) *twice_nat = m->twice_nat; - if (PREDICT_FALSE(lb != 0)) - *lb = vec_len (m->locals) > 0; + if (PREDICT_FALSE (is_identity_nat != 0)) + *is_identity_nat = is_identity_static_mapping (m); return 0; } @@ -2226,35 +2916,31 @@ snat_random_port (u16 min, u16 max) { snat_main_t *sm = &snat_main; return min + random_u32 (&sm->random_seed) / - (random_u32_max() / (max - min + 1) + 1); + (random_u32_max () / (max - min + 1) + 1); } int snat_alloc_outside_address_and_port (snat_address_t * addresses, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp, - u16 port_per_thread, - u32 snat_thread_index) + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u16 port_per_thread, + u32 snat_thread_index) { snat_main_t *sm = &snat_main; - return sm->alloc_addr_and_port(addresses, fib_index, thread_index, k, - address_indexp, port_per_thread, - snat_thread_index); + return sm->alloc_addr_and_port (addresses, fib_index, thread_index, k, + port_per_thread, snat_thread_index); } static int nat_alloc_addr_and_port_default (snat_address_t * addresses, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp, - u16 port_per_thread, - u32 snat_thread_index) + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u16 port_per_thread, u32 snat_thread_index) { - int i, gi = 0; + int i; snat_address_t *a, *ga = 0; u32 portnum; @@ -2262,7 +2948,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses, { a = addresses + i; switch (k->protocol) - { + { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ @@ -2274,30 +2960,28 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses, portnum = (port_per_thread * \ snat_thread_index) + \ snat_random_port(1, port_per_thread) + 1024; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + --a->busy_##n##_port_refcounts[portnum]; \ a->busy_##n##_ports_per_thread[thread_index]++; \ a->busy_##n##_ports++; \ k->addr = a->addr; \ k->port = clib_host_to_net_u16(portnum); \ - *address_indexp = i; \ return 0; \ } \ } \ else if (a->fib_index == ~0) \ { \ ga = a; \ - gi = i; \ } \ } \ break; - foreach_snat_protocol + foreach_snat_protocol #undef _ - default: - nat_log_info ("unknown protocol"); - return 1; - } + default: + nat_elog_info ("unknown protocol"); + return 1; + } } @@ -2313,38 +2997,35 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses, portnum = (port_per_thread * \ snat_thread_index) + \ snat_random_port(1, port_per_thread) + 1024; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + ++a->busy_##n##_port_refcounts[portnum]; \ a->busy_##n##_ports_per_thread[thread_index]++; \ a->busy_##n##_ports++; \ k->addr = a->addr; \ k->port = clib_host_to_net_u16(portnum); \ - *address_indexp = gi; \ return 0; \ } break; foreach_snat_protocol #undef _ default: - nat_log_info ("unknown protocol"); + nat_elog_info ("unknown protocol"); return 1; } } /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted(0); + snat_ipfix_logging_addresses_exhausted (thread_index, 0); return 1; } static int nat_alloc_addr_and_port_mape (snat_address_t * addresses, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp, - u16 port_per_thread, - u32 snat_thread_index) + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u16 port_per_thread, u32 snat_thread_index) { snat_main_t *sm = &snat_main; snat_address_t *a = addresses; @@ -2366,13 +3047,12 @@ nat_alloc_addr_and_port_mape (snat_address_t * addresses, A = snat_random_port(1, pow2_mask(sm->psid_offset)); \ j = snat_random_port(0, pow2_mask(m)); \ portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + ++a->busy_##n##_port_refcounts[portnum]; \ a->busy_##n##_ports++; \ k->addr = a->addr; \ k->port = clib_host_to_net_u16 (portnum); \ - *address_indexp = i; \ return 0; \ } \ } \ @@ -2380,22 +3060,70 @@ nat_alloc_addr_and_port_mape (snat_address_t * addresses, foreach_snat_protocol #undef _ default: - nat_log_info ("unknown protocol"); + nat_elog_info ("unknown protocol"); return 1; } exhausted: /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted(0); + snat_ipfix_logging_addresses_exhausted (thread_index, 0); return 1; } -void -nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add) +static int +nat_alloc_addr_and_port_range (snat_address_t * addresses, + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u16 port_per_thread, u32 snat_thread_index) { - dpo_id_t dpo_v4 = DPO_INVALID; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, + snat_main_t *sm = &snat_main; + snat_address_t *a = addresses; + u16 portnum, ports; + + ports = sm->end_port - sm->start_port + 1; + + if (!vec_len (addresses)) + goto exhausted; + + switch (k->protocol) + { +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports < ports) \ + { \ + while (1) \ + { \ + portnum = snat_random_port(sm->start_port, sm->end_port); \ + if (a->busy_##n##_port_refcounts[portnum]) \ + continue; \ + ++a->busy_##n##_port_refcounts[portnum]; \ + a->busy_##n##_ports++; \ + k->addr = a->addr; \ + k->port = clib_host_to_net_u16 (portnum); \ + return 0; \ + } \ + } \ + break; + foreach_snat_protocol +#undef _ + default: + nat_elog_info ("unknown protocol"); + return 1; + } + +exhausted: + /* Totally out of translations to use... */ + snat_ipfix_logging_addresses_exhausted (thread_index, 0); + return 1; +} + +void +nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add) +{ + dpo_id_t dpo_v4 = DPO_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, .fp_len = 32, .fp_addr.ip4.as_u32 = addr.as_u32, }; @@ -2403,51 +3131,16 @@ nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add) if (is_add) { nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4); - fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI, - FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4); + fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4); dpo_reset (&dpo_v4); } else { - fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_PLUGIN_HI); - } -} - -uword -unformat_snat_protocol (unformat_input_t * input, va_list * args) -{ - u32 *r = va_arg (*args, u32 *); - - if (0); -#define _(N, i, n, s) else if (unformat (input, s)) *r = SNAT_PROTOCOL_##N; - foreach_snat_protocol -#undef _ - else - return 0; - return 1; -} - -u8 * -format_snat_protocol (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(N, j, n, str) case SNAT_PROTOCOL_##N: t = (u8 *) str; break; - foreach_snat_protocol -#undef _ - default: - s = format (s, "unknown"); - return s; + fib_table_entry_special_remove (0, &pfx, nat_fib_src_hi); } - s = format (s, "%s", t); - return s; } -u8 * format_snat_key (u8 * s, va_list * args); - u8 * format_session_kvp (u8 * s, va_list * args) { @@ -2469,7 +3162,8 @@ format_static_mapping_kvp (u8 * s, va_list * args) k.as_u64 = v->key; - s = format (s, "%U static-mapping-index %llu", format_snat_key, &k, v->value); + s = format (s, "%U static-mapping-index %llu", + format_static_mapping_key, &k, v->value); return s; } @@ -2483,7 +3177,7 @@ format_user_kvp (u8 * s, va_list * args) k.as_u64 = v->key; s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr, - k.fib_index, v->value); + k.fib_index, v->value); return s; } @@ -2497,16 +3191,18 @@ format_ed_session_kvp (u8 * s, va_list * args) k.as_u64[0] = v->key[0]; k.as_u64[1] = v->key[1]; - s = format (s, "local %U:%d remote %U:%d proto %U fib %d session-index %llu", - format_ip4_address, &k.l_addr, clib_net_to_host_u16 (k.l_port), - format_ip4_address, &k.r_addr, clib_net_to_host_u16 (k.r_port), - format_ip_protocol, k.proto, k.fib_index, v->value); + s = + format (s, "local %U:%d remote %U:%d proto %U fib %d session-index %llu", + format_ip4_address, &k.l_addr, clib_net_to_host_u16 (k.l_port), + format_ip4_address, &k.r_addr, clib_net_to_host_u16 (k.r_port), + format_ip_protocol, k.proto, k.fib_index, v->value); return s; } static u32 -snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) +snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0, + u8 is_output) { snat_main_t *sm = &snat_main; u32 next_worker_index = 0; @@ -2514,7 +3210,7 @@ snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) next_worker_index = sm->first_worker_index; hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) + - (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >>24); + (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24); if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; @@ -2525,7 +3221,8 @@ snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) } static u32 -snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) +snat_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip0, + u32 rx_fib_index0, u8 is_output) { snat_main_t *sm = &snat_main; udp_header_t *udp; @@ -2544,36 +3241,18 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) m_key.protocol = 0; m_key.fib_index = rx_fib_index0; kv.key = m_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - return m->workers[0]; - } + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + return m->workers[0]; + } } proto = ip_proto_to_snat_proto (ip0->protocol); udp = ip4_next_header (ip0); port = udp->dst_port; - if (PREDICT_FALSE (ip4_is_fragment (ip0))) - { - if (PREDICT_FALSE (nat_reass_is_drop_frag (0))) - return vlib_get_thread_index (); - - if (PREDICT_TRUE (!ip4_is_first_fragment (ip0))) - { - nat_reass_ip4_t *reass; - - reass = nat_ip4_reass_find (ip0->src_address, ip0->dst_address, - ip0->fragment_id, ip0->protocol); - - if (reass && (reass->thread_index != (u32) ~ 0)) - return reass->thread_index; - else - return vlib_get_thread_index (); - } - } - /* unknown protocol */ if (PREDICT_FALSE (proto == ~0)) { @@ -2583,30 +3262,32 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP)) { - icmp46_header_t * icmp = (icmp46_header_t *) udp; - icmp_echo_header_t *echo = (icmp_echo_header_t *)(icmp + 1); - if (!icmp_is_error_message (icmp)) - port = echo->identifier; + icmp46_header_t *icmp = (icmp46_header_t *) udp; + icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); + if (!icmp_type_is_error_message + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) + port = vnet_buffer (b)->ip.reass.l4_src_port; else - { - ip4_header_t *inner_ip = (ip4_header_t *)(echo + 1); - proto = ip_proto_to_snat_proto (inner_ip->protocol); - void *l4_header = ip4_next_header (inner_ip); - switch (proto) - { - case SNAT_PROTOCOL_ICMP: - icmp = (icmp46_header_t*)l4_header; - echo = (icmp_echo_header_t *)(icmp + 1); - port = echo->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - port = ((tcp_udp_header_t*)l4_header)->src_port; - break; - default: - return vlib_get_thread_index (); - } - } + { + /* if error message, then it's not fragmented and we can access it */ + ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); + proto = ip_proto_to_snat_proto (inner_ip->protocol); + void *l4_header = ip4_next_header (inner_ip); + switch (proto) + { + case SNAT_PROTOCOL_ICMP: + icmp = (icmp46_header_t *) l4_header; + echo = (icmp_echo_header_t *) (icmp + 1); + port = echo->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + port = ((tcp_udp_header_t *) l4_header)->src_port; + break; + default: + return vlib_get_thread_index (); + } + } } /* try static mappings with port */ @@ -2617,11 +3298,12 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) m_key.protocol = proto; m_key.fib_index = rx_fib_index0; kv.key = m_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - return m->workers[0]; - } + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + return m->workers[0]; + } } /* worker by outside port */ @@ -2632,119 +3314,635 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) } static u32 -nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index) +nat44_ed_get_worker_in2out_cb (ip4_header_t * ip, u32 rx_fib_index, + u8 is_output) +{ + snat_main_t *sm = &snat_main; + u32 next_worker_index = sm->first_worker_index; + u32 hash; + + clib_bihash_kv_16_8_t kv16, value16; + snat_main_per_thread_data_t *tsm; + udp_header_t *udp; + + if (PREDICT_FALSE (is_output)) + { + u32 fib_index = sm->outside_fib_index; + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = ip->dst_address.as_u32, + } + , + }; + + udp = ip4_next_header (ip); + + switch (vec_len (sm->outside_fibs)) + { + case 0: + fib_index = sm->outside_fib_index; + break; + case 1: + fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + + make_ed_kv (&kv16, &ip->src_address, &ip->dst_address, + ip->protocol, fib_index, udp->src_port, udp->dst_port); + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + { + if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed, + &kv16, &value16))) + { + next_worker_index += tsm->thread_index; + + nat_elog_debug_handoff ( + "HANDOFF IN2OUT-OUTPUT-FEATURE (session)", + next_worker_index, fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + + return next_worker_index; + } + } + /* *INDENT-ON* */ + } + + hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) + next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; + else + next_worker_index += sm->workers[hash % _vec_len (sm->workers)]; + + if (PREDICT_TRUE (!is_output)) + { + nat_elog_debug_handoff ("HANDOFF IN2OUT", + next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + } + else + { + nat_elog_debug_handoff ("HANDOFF IN2OUT-OUTPUT-FEATURE", + next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + } + + return next_worker_index; +} + +static u32 +nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip, + u32 rx_fib_index, u8 is_output) { snat_main_t *sm = &snat_main; clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t kv16, value16; + snat_main_per_thread_data_t *tsm; + u32 proto, next_worker_index = 0; udp_header_t *udp; u16 port; snat_static_mapping_t *m; u32 hash; - /* first try static mappings without port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + proto = ip_proto_to_snat_proto (ip->protocol); + + if (PREDICT_TRUE (proto == SNAT_PROTOCOL_UDP || proto == SNAT_PROTOCOL_TCP)) + { + udp = ip4_next_header (ip); + + make_ed_kv (&kv16, &ip->dst_address, &ip->src_address, + ip->protocol, rx_fib_index, udp->dst_port, udp->src_port); + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + { + if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed, + &kv16, &value16))) + { + next_worker_index = sm->first_worker_index + tsm->thread_index; + nat_elog_debug_handoff ("HANDOFF OUT2IN (session)", + next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; + } + } + /* *INDENT-ON* */ + } + else if (proto == SNAT_PROTOCOL_ICMP) + { + nat_ed_ses_key_t key; + + if (!get_icmp_o2i_ed_key (b, ip, &key)) + { + + key.fib_index = rx_fib_index; + kv16.key[0] = key.as_u64[0]; + kv16.key[1] = key.as_u64[1]; + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + { + if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed, + &kv16, &value16))) + { + next_worker_index = sm->first_worker_index + + tsm->thread_index; + nat_elog_debug_handoff ("HANDOFF OUT2IN (session)", + next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; + } + } + /* *INDENT-ON* */ + } + } + + /* first try static mappings without port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + make_sm_kv (&kv, &ip->dst_address, 0, 0, 0); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + next_worker_index = m->workers[0]; + goto done; + } + } + + /* unknown protocol */ + if (PREDICT_FALSE (proto == ~0)) + { + /* use current thread */ + next_worker_index = vlib_get_thread_index (); + goto done; + } + + udp = ip4_next_header (ip); + port = udp->dst_port; + + if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP)) + { + icmp46_header_t *icmp = (icmp46_header_t *) udp; + icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); + if (!icmp_type_is_error_message + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) + port = vnet_buffer (b)->ip.reass.l4_src_port; + else + { + /* if error message, then it's not fragmented and we can access it */ + ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); + proto = ip_proto_to_snat_proto (inner_ip->protocol); + void *l4_header = ip4_next_header (inner_ip); + switch (proto) + { + case SNAT_PROTOCOL_ICMP: + icmp = (icmp46_header_t *) l4_header; + echo = (icmp_echo_header_t *) (icmp + 1); + port = echo->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + port = ((tcp_udp_header_t *) l4_header)->src_port; + break; + default: + next_worker_index = vlib_get_thread_index (); + goto done; + } + } + } + + /* try static mappings with port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + make_sm_kv (&kv, &ip->dst_address, proto, 0, + clib_net_to_host_u16 (port)); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + if (!is_lb_static_mapping (m)) + { + next_worker_index = m->workers[0]; + goto done; + } + + hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers)))) + next_worker_index = + m->workers[hash & (_vec_len (m->workers) - 1)]; + else + next_worker_index = m->workers[hash % _vec_len (m->workers)]; + goto done; + } + } + + /* worker by outside port */ + next_worker_index = sm->first_worker_index; + next_worker_index += + sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; + +done: + nat_elog_debug_handoff ("HANDOFF OUT2IN", next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; +} + +void +nat_ha_sadd_cb (ip4_address_t * in_addr, u16 in_port, + ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, + ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, + u32 fib_index, u16 flags, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + snat_user_t *u; + snat_session_t *s; + clib_bihash_kv_8_8_t kv; + f64 now = vlib_time_now (sm->vlib_main); + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + snat_main_per_thread_data_t *tsm; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = eh_addr->as_u32, + }, + }; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + + if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) + { + if (nat_set_outside_address_and_port + (sm->addresses, thread_index, &key)) + return; + } + + u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index); + if (!u) + return; + + s = nat_session_alloc_or_recycle (sm, u, thread_index, now); + if (!s) + return; + + s->last_heard = now; + s->flags = flags; + s->ext_host_addr.as_u32 = eh_addr->as_u32; + s->ext_host_port = eh_port; + user_session_increment (sm, u, snat_is_session_static (s)); + switch (vec_len (sm->outside_fibs)) + { + case 0: + key.fib_index = sm->outside_fib_index; + break; + case 1: + key.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + key.fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + s->out2in = key; + kv.key = key.as_u64; + kv.value = s - tsm->sessions; + if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 1)) + nat_elog_warn ("out2in key add failed"); + + key.addr.as_u32 = in_addr->as_u32; + key.port = in_port; + key.fib_index = fib_index; + s->in2out = key; + kv.key = key.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 1)) + nat_elog_warn ("in2out key add failed"); +} + +void +nat_ha_sdel_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index, + u32 ti) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + clib_bihash_kv_8_8_t kv, value; + u32 thread_index; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + if (sm->num_workers > 1) + thread_index = + sm->first_worker_index + + (sm->workers[(clib_net_to_host_u16 (out_port) - + 1024) / sm->port_per_thread]); + else + thread_index = sm->num_workers; + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + key.fib_index = fib_index; + kv.key = key.as_u64; + if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + nat_free_session_data (sm, s, thread_index, 1); + nat44_delete_session (sm, s, thread_index); +} + +void +nat_ha_sref_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index, + u32 total_pkts, u64 total_bytes, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + clib_bihash_kv_8_8_t kv, value; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + key.fib_index = fib_index; + kv.key = key.as_u64; + if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + s->total_pkts = total_pkts; + s->total_bytes = total_bytes; +} + +void +nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port, + ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, + ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, + u32 fib_index, u16 flags, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + snat_user_t *u; + snat_session_t *s; + clib_bihash_kv_16_8_t kv; + f64 now = vlib_time_now (sm->vlib_main); + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + snat_main_per_thread_data_t *tsm; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = eh_addr->as_u32, + }, + }; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + + if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) { - make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - return m->workers[0]; - } + if (nat_set_outside_address_and_port + (sm->addresses, thread_index, &key)) + return; } - proto = ip_proto_to_snat_proto (ip->protocol); - - /* unknown protocol */ - if (PREDICT_FALSE (proto == ~0)) + key.addr.as_u32 = ehn_addr->as_u32; + key.port = ehn_port; + if (flags & SNAT_SESSION_FLAG_TWICE_NAT) { - /* use current thread */ - return vlib_get_thread_index (); + if (nat_set_outside_address_and_port + (sm->twice_nat_addresses, thread_index, &key)) + return; } - udp = ip4_next_header (ip); - port = udp->dst_port; + u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index); + if (!u) + return; - if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP)) + s = nat_ed_session_alloc (sm, u, thread_index, now); + if (!s) + return; + + s->last_heard = now; + s->flags = flags; + s->ext_host_nat_addr.as_u32 = s->ext_host_addr.as_u32 = eh_addr->as_u32; + s->ext_host_nat_port = s->ext_host_port = eh_port; + if (is_twice_nat_session (s)) { - icmp46_header_t * icmp = (icmp46_header_t *) udp; - icmp_echo_header_t *echo = (icmp_echo_header_t *)(icmp + 1); - if (!icmp_is_error_message (icmp)) - port = echo->identifier; - else + s->ext_host_nat_addr.as_u32 = ehn_addr->as_u32; + s->ext_host_nat_port = ehn_port; + } + user_session_increment (sm, u, snat_is_session_static (s)); + switch (vec_len (sm->outside_fibs)) + { + case 0: + key.fib_index = sm->outside_fib_index; + break; + case 1: + key.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) { - ip4_header_t *inner_ip = (ip4_header_t *)(echo + 1); - proto = ip_proto_to_snat_proto (inner_ip->protocol); - void *l4_header = ip4_next_header (inner_ip); - switch (proto) + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) { - case SNAT_PROTOCOL_ICMP: - icmp = (icmp46_header_t*)l4_header; - echo = (icmp_echo_header_t *)(icmp + 1); - port = echo->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - port = ((tcp_udp_header_t*)l4_header)->src_port; - break; - default: - return vlib_get_thread_index (); + if (fib_entry_get_resolving_interface (fei) != ~0) + { + key.fib_index = outside_fib->fib_index; + break; + } } } + /* *INDENT-ON* */ + break; } + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + s->out2in = key; + kv.value = s - tsm->sessions; - /* try static mappings with port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - make_sm_kv (&kv, &ip->dst_address, proto, rx_fib_index, - clib_net_to_host_u16 (port)); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - if (!vec_len(m->locals)) - return m->workers[0]; + key.addr.as_u32 = in_addr->as_u32; + key.port = in_port; + key.fib_index = fib_index; + s->in2out = key; + + make_ed_kv (&kv, in_addr, &s->ext_host_nat_addr, + snat_proto_to_ip_proto (proto), fib_index, in_port, + s->ext_host_nat_port); + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1)) + nat_elog_warn ("in2out key add failed"); + + make_ed_kv (&kv, out_addr, eh_addr, snat_proto_to_ip_proto (proto), + s->out2in.fib_index, out_port, eh_port); + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1)) + nat_elog_warn ("out2in key add failed"); +} - hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + - (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >>24); +void +nat_ha_sdel_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 ti) +{ + snat_main_t *sm = &snat_main; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + u32 thread_index; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; - if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers)))) - return m->workers[hash & (_vec_len (m->workers) - 1)]; - else - return m->workers[hash % _vec_len (m->workers)]; - } - } + if (sm->num_workers > 1) + thread_index = + sm->first_worker_index + + (sm->workers[(clib_net_to_host_u16 (out_port) - + 1024) / sm->port_per_thread]); + else + thread_index = sm->num_workers; + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); - /* worker by outside port */ - next_worker_index = sm->first_worker_index; - next_worker_index += - sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; + key.l_addr.as_u32 = out_addr->as_u32; + key.l_port = out_port; + key.r_addr.as_u32 = eh_addr->as_u32; + key.r_port = eh_port; + key.proto = proto; + key.fib_index = fib_index; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + return; - return next_worker_index; + s = pool_elt_at_index (tsm->sessions, value.value); + nat_free_session_data (sm, s, thread_index, 1); + nat44_delete_session (sm, s, thread_index); +} + +void +nat_ha_sref_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 total_pkts, u64 total_bytes, + u32 thread_index) +{ + snat_main_t *sm = &snat_main; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.l_addr.as_u32 = out_addr->as_u32; + key.l_port = out_port; + key.r_addr.as_u32 = eh_addr->as_u32; + key.r_port = eh_port; + key.proto = proto; + key.fib_index = fib_index; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + s->total_pkts = total_pkts; + s->total_bytes = total_bytes; } static clib_error_t * snat_config (vlib_main_t * vm, unformat_input_t * input) { - snat_main_t * sm = &snat_main; - nat66_main_t * nm = &nat66_main; - u32 translation_buckets = 1024; - u32 translation_memory_size = 128<<20; - u32 user_buckets = 128; - u32 user_memory_size = 64<<20; - u32 max_translations_per_user = 100; - u32 outside_vrf_id = 0; - u32 outside_ip6_vrf_id = 0; - u32 inside_vrf_id = 0; + snat_main_t *sm = &snat_main; + nat66_main_t *nm = &nat66_main; + //dslite_main_t *dm = &dslite_main; + snat_main_per_thread_data_t *tsm; + u32 static_mapping_buckets = 1024; - u32 static_mapping_memory_size = 64<<20; + uword static_mapping_memory_size = 64 << 20; + u32 nat64_bib_buckets = 1024; u32 nat64_bib_memory_size = 128 << 20; + u32 nat64_st_buckets = 2048; - u32 nat64_st_memory_size = 256 << 20; + uword nat64_st_memory_size = 256 << 20; + + u32 user_buckets = 128; + uword user_memory_size = 64 << 20; + u32 translation_buckets = 1024; + uword translation_memory_size = 128 << 20; + + u32 max_translations_per_user = ~0; + + u32 outside_vrf_id = 0; + u32 outside_ip6_vrf_id = 0; + u32 inside_vrf_id = 0; u8 static_mapping_only = 0; u8 static_mapping_connection_tracking = 0; - snat_main_per_thread_data_t *tsm; - dslite_main_t * dm = &dslite_main; + + // configurable timeouts + u32 udp_timeout = SNAT_UDP_TIMEOUT; + u32 icmp_timeout = SNAT_ICMP_TIMEOUT; + u32 tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; + u32 tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; sm->deterministic = 0; sm->out2in_dpo = 0; @@ -2752,82 +3950,111 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { - if (unformat (input, "translation hash buckets %d", &translation_buckets)) - ; + if (unformat + (input, "translation hash buckets %d", &translation_buckets)) + ; + else if (unformat (input, "udp timeout %d", &udp_timeout)) + ; + else if (unformat (input, "icmp timeout %d", &icmp_timeout)) + ; + else if (unformat (input, "tcp transitory timeout %d", + &tcp_transitory_timeout)); + else if (unformat (input, "tcp established timeout %d", + &tcp_established_timeout)); else if (unformat (input, "translation hash memory %d", - &translation_memory_size)); + &translation_memory_size)); else if (unformat (input, "user hash buckets %d", &user_buckets)) - ; - else if (unformat (input, "user hash memory %d", - &user_memory_size)) - ; + ; + else if (unformat (input, "user hash memory %d", &user_memory_size)) + ; else if (unformat (input, "max translations per user %d", - &max_translations_per_user)) - ; - else if (unformat (input, "outside VRF id %d", - &outside_vrf_id)) - ; - else if (unformat (input, "outside ip6 VRF id %d", - &outside_ip6_vrf_id)) - ; - else if (unformat (input, "inside VRF id %d", - &inside_vrf_id)) - ; + &max_translations_per_user)) + ; + else if (unformat (input, "outside VRF id %d", &outside_vrf_id)) + ; + else if (unformat (input, "outside ip6 VRF id %d", &outside_ip6_vrf_id)) + ; + else if (unformat (input, "inside VRF id %d", &inside_vrf_id)) + ; else if (unformat (input, "static mapping only")) - { - static_mapping_only = 1; - if (unformat (input, "connection tracking")) - static_mapping_connection_tracking = 1; - } + { + static_mapping_only = 1; + if (unformat (input, "connection tracking")) + static_mapping_connection_tracking = 1; + } else if (unformat (input, "deterministic")) - sm->deterministic = 1; + sm->deterministic = 1; else if (unformat (input, "nat64 bib hash buckets %d", - &nat64_bib_buckets)) - ; + &nat64_bib_buckets)) + ; else if (unformat (input, "nat64 bib hash memory %d", - &nat64_bib_memory_size)) - ; - else if (unformat (input, "nat64 st hash buckets %d", &nat64_st_buckets)) - ; + &nat64_bib_memory_size)) + ; + else + if (unformat (input, "nat64 st hash buckets %d", &nat64_st_buckets)) + ; else if (unformat (input, "nat64 st hash memory %d", - &nat64_st_memory_size)) - ; + &nat64_st_memory_size)) + ; else if (unformat (input, "out2in dpo")) - sm->out2in_dpo = 1; - else if (unformat (input, "dslite ce")) - dslite_set_ce(dm, 1); + sm->out2in_dpo = 1; + //else if (unformat (input, "dslite ce")) + //dslite_set_ce (dm, 1); else if (unformat (input, "endpoint-dependent")) - sm->endpoint_dependent = 1; + sm->endpoint_dependent = 1; else return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); } - /* for show commands, etc. */ + if (sm->deterministic && sm->endpoint_dependent) + return clib_error_return (0, + "deterministic and endpoint-dependent modes are mutually exclusive"); + + if (static_mapping_only && (sm->deterministic || sm->endpoint_dependent)) + return clib_error_return (0, + "static mapping only mode available only for simple nat"); + + if (sm->out2in_dpo && (sm->deterministic || sm->endpoint_dependent)) + return clib_error_return (0, + "out2in dpo mode available only for simple nat"); + + /* optionally configurable timeouts for testing purposes */ + sm->udp_timeout = udp_timeout; + sm->tcp_transitory_timeout = tcp_transitory_timeout; + sm->tcp_established_timeout = tcp_established_timeout; + sm->icmp_timeout = icmp_timeout; + + sm->min_timeout = nat44_minimal_timeout (sm); + + sm->user_buckets = user_buckets; + sm->user_memory_size = user_memory_size; + sm->translation_buckets = translation_buckets; sm->translation_memory_size = translation_memory_size; + /* do not exceed load factor 10 */ sm->max_translations = 10 * translation_buckets; - sm->user_buckets = user_buckets; - sm->user_memory_size = user_memory_size; - sm->max_translations_per_user = max_translations_per_user; + sm->max_translations_per_user = max_translations_per_user == ~0 ? + sm->max_translations : max_translations_per_user; + sm->outside_vrf_id = outside_vrf_id; sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - outside_vrf_id, - FIB_SOURCE_PLUGIN_HI); + outside_vrf_id, + nat_fib_src_hi); nm->outside_vrf_id = outside_ip6_vrf_id; nm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, - outside_ip6_vrf_id, - FIB_SOURCE_PLUGIN_HI); + outside_ip6_vrf_id, + nat_fib_src_hi); sm->inside_vrf_id = inside_vrf_id; sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - inside_vrf_id, - FIB_SOURCE_PLUGIN_HI); + inside_vrf_id, + nat_fib_src_hi); sm->static_mapping_only = static_mapping_only; sm->static_mapping_connection_tracking = static_mapping_connection_tracking; - nat64_set_hash(nat64_bib_buckets, nat64_bib_memory_size, nat64_st_buckets, - nat64_st_memory_size); + nat64_set_hash (nat64_bib_buckets, nat64_bib_memory_size, nat64_st_buckets, + nat64_st_memory_size); if (sm->deterministic) { @@ -2840,30 +4067,62 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) else { if (sm->endpoint_dependent) - { - sm->worker_in2out_cb = snat_get_worker_in2out_cb; - sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb; - sm->in2out_node_index = nat44_ed_in2out_node.index; - sm->in2out_output_node_index = nat44_ed_in2out_output_node.index; - sm->out2in_node_index = nat44_ed_out2in_node.index; - sm->icmp_match_in2out_cb = icmp_match_in2out_ed; - sm->icmp_match_out2in_cb = icmp_match_out2in_ed; - } + { + sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb; + sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb; + + sm->handoff_out2in_index = nat_pre_out2in_node.index; + sm->handoff_in2out_index = nat_pre_in2out_node.index; + sm->handoff_in2out_output_index = nat44_ed_in2out_output_node.index; + + sm->in2out_node_index = nat44_ed_in2out_node.index; + sm->in2out_output_node_index = nat44_ed_in2out_output_node.index; + sm->out2in_node_index = nat44_ed_out2in_node.index; + + sm->icmp_match_in2out_cb = icmp_match_in2out_ed; + sm->icmp_match_out2in_cb = icmp_match_out2in_ed; + nat_affinity_init (vm); + nat_ha_init (vm, nat_ha_sadd_ed_cb, nat_ha_sdel_ed_cb, + nat_ha_sref_ed_cb); + } else - { - sm->worker_in2out_cb = snat_get_worker_in2out_cb; - sm->worker_out2in_cb = snat_get_worker_out2in_cb; - sm->in2out_node_index = snat_in2out_node.index; - sm->in2out_output_node_index = snat_in2out_output_node.index; - sm->out2in_node_index = snat_out2in_node.index; - sm->icmp_match_in2out_cb = icmp_match_in2out_slow; - sm->icmp_match_out2in_cb = icmp_match_out2in_slow; - } + { + sm->worker_in2out_cb = snat_get_worker_in2out_cb; + sm->worker_out2in_cb = snat_get_worker_out2in_cb; + + sm->handoff_out2in_index = snat_out2in_node.index; + sm->handoff_in2out_index = snat_in2out_node.index; + sm->handoff_in2out_output_index = snat_in2out_output_node.index; + + sm->in2out_node_index = snat_in2out_node.index; + sm->in2out_output_node_index = snat_in2out_output_node.index; + sm->out2in_node_index = snat_out2in_node.index; + sm->icmp_match_in2out_cb = icmp_match_in2out_slow; + sm->icmp_match_out2in_cb = icmp_match_out2in_slow; + nat_ha_init (vm, nat_ha_sadd_cb, nat_ha_sdel_cb, nat_ha_sref_cb); + } if (!static_mapping_only || - (static_mapping_only && static_mapping_connection_tracking)) - { + (static_mapping_only && static_mapping_connection_tracking)) + { + /* *INDENT-OFF* */ vec_foreach (tsm, sm->per_thread_data) { + tsm->min_session_timeout = 0; + + tsm->cleared = 0; + tsm->cleanup_runs = 0; + tsm->cleanup_timeout = 0; + + pool_alloc (tsm->sessions, sm->max_translations); + pool_alloc (tsm->list_pool, sm->max_translations); + pool_alloc (tsm->global_lru_pool, sm->max_translations); + + dlist_elt_t *head; + pool_get (tsm->global_lru_pool, head); + tsm->global_lru_head_index = head - tsm->global_lru_pool; + clib_dlist_init (tsm->global_lru_pool, + tsm->global_lru_head_index); + if (sm->endpoint_dependent) { clib_bihash_init_16_8 (&tsm->in2out_ed, "in2out-ed", @@ -2877,6 +4136,9 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) translation_memory_size); clib_bihash_set_kvp_format_fn_16_8 (&tsm->out2in_ed, format_ed_session_kvp); + clib_bihash_init_16_8 + (&sm->ed_ext_ports, "ed-nat-5-tuple-port-overload-hash", + translation_buckets, translation_memory_size); } else { @@ -2898,24 +4160,26 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp); } + /* *INDENT-ON* */ - } + } else - { - sm->icmp_match_in2out_cb = icmp_match_in2out_fast; - sm->icmp_match_out2in_cb = icmp_match_out2in_fast; - } + { + sm->icmp_match_in2out_cb = icmp_match_in2out_fast; + sm->icmp_match_out2in_cb = icmp_match_out2in_fast; + } clib_bihash_init_8_8 (&sm->static_mapping_by_local, - "static_mapping_by_local", static_mapping_buckets, - static_mapping_memory_size); + "static_mapping_by_local", static_mapping_buckets, + static_mapping_memory_size); clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local, - format_static_mapping_kvp); + format_static_mapping_kvp); clib_bihash_init_8_8 (&sm->static_mapping_by_external, - "static_mapping_by_external", static_mapping_buckets, - static_mapping_memory_size); + "static_mapping_by_external", + static_mapping_buckets, + static_mapping_memory_size); clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external, - format_static_mapping_kvp); + format_static_mapping_kvp); } return 0; @@ -2923,230 +4187,13 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) VLIB_CONFIG_FUNCTION (snat_config, "nat"); -u8 * format_snat_session_state (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break; - foreach_snat_session_state -#undef _ - default: - t = format (t, "unknown"); - } - s = format (s, "%s", t); - return s; -} - -u8 * format_snat_key (u8 * s, va_list * args) -{ - snat_session_key_t * key = va_arg (*args, snat_session_key_t *); - - s = format (s, "%U proto %U port %d fib %d", - format_ip4_address, &key->addr, - format_snat_protocol, key->protocol, - clib_net_to_host_u16 (key->port), key->fib_index); - return s; -} - -u8 * format_snat_session (u8 * s, va_list * args) -{ - snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *); - snat_session_t * sess = va_arg (*args, snat_session_t *); - - if (snat_is_unk_proto_session (sess)) - { - s = format (s, " i2o %U proto %u fib %u\n", - format_ip4_address, &sess->in2out.addr, - clib_net_to_host_u16 (sess->in2out.port), - sess->in2out.fib_index); - s = format (s, " o2i %U proto %u fib %u\n", - format_ip4_address, &sess->out2in.addr, - clib_net_to_host_u16 (sess->out2in.port), - sess->out2in.fib_index); - } - else - { - s = format (s, " i2o %U\n", format_snat_key, &sess->in2out); - s = format (s, " o2i %U\n", format_snat_key, &sess->out2in); - } - if (is_ed_session (sess) || is_fwd_bypass_session (sess)) - { - if (is_twice_nat_session (sess)) - { - s = format (s, " external host o2i %U:%d i2o %U:%d\n", - format_ip4_address, &sess->ext_host_addr, - clib_net_to_host_u16 (sess->ext_host_port), - format_ip4_address, &sess->ext_host_nat_addr, - clib_net_to_host_u16 (sess->ext_host_nat_port)); - } - else - { - if (sess->ext_host_addr.as_u32) - s = format (s, " external host %U:%u\n", - format_ip4_address, &sess->ext_host_addr, - clib_net_to_host_u16 (sess->ext_host_port)); - } - } - s = format (s, " index %llu\n", sess - sm->sessions); - s = format (s, " last heard %.2f\n", sess->last_heard); - s = format (s, " total pkts %d, total bytes %lld\n", - sess->total_pkts, sess->total_bytes); - if (snat_is_session_static (sess)) - s = format (s, " static translation\n"); - else - s = format (s, " dynamic translation\n"); - if (is_fwd_bypass_session (sess)) - s = format (s, " forwarding-bypass\n"); - if (is_lb_session (sess)) - s = format (s, " load-balancing\n"); - if (is_twice_nat_session (sess)) - s = format (s, " twice-nat\n"); - - return s; -} - -u8 * format_snat_user (u8 * s, va_list * args) -{ - snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *); - snat_user_t * u = va_arg (*args, snat_user_t *); - int verbose = va_arg (*args, int); - dlist_elt_t * head, * elt; - u32 elt_index, head_index; - u32 session_index; - snat_session_t * sess; - - s = format (s, "%U: %d dynamic translations, %d static translations\n", - format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions); - - if (verbose == 0) - return s; - - if (u->nsessions || u->nstaticsessions) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (sm->list_pool, head_index); - - elt_index = head->next; - elt = pool_elt_at_index (sm->list_pool, elt_index); - session_index = elt->value; - - while (session_index != ~0) - { - sess = pool_elt_at_index (sm->sessions, session_index); - - s = format (s, " %U\n", format_snat_session, sm, sess); - - elt_index = elt->next; - elt = pool_elt_at_index (sm->list_pool, elt_index); - session_index = elt->value; - } - } - - return s; -} - -u8 * format_snat_static_mapping (u8 * s, va_list * args) -{ - snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *); - nat44_lb_addr_port_t *local; - - if (m->addr_only) - s = format (s, "local %U external %U vrf %d %s %s", - format_ip4_address, &m->local_addr, - format_ip4_address, &m->external_addr, - m->vrf_id, - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - m->out2in_only ? "out2in-only" : ""); - else - { - if (vec_len (m->locals)) - { - s = format (s, "%U vrf %d external %U:%d %s %s", - format_snat_protocol, m->proto, - m->vrf_id, - format_ip4_address, &m->external_addr, m->external_port, - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - m->out2in_only ? "out2in-only" : ""); - vec_foreach (local, m->locals) - s = format (s, "\n local %U:%d probability %d\%", - format_ip4_address, &local->addr, local->port, - local->probability); - } - else - s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", - format_snat_protocol, m->proto, - format_ip4_address, &m->local_addr, m->local_port, - format_ip4_address, &m->external_addr, m->external_port, - m->vrf_id, - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - m->out2in_only ? "out2in-only" : ""); - } - return s; -} - -u8 * format_snat_static_map_to_resolve (u8 * s, va_list * args) -{ - snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *); - vnet_main_t *vnm = vnet_get_main(); - - if (m->addr_only) - s = format (s, "local %U external %U vrf %d", - format_ip4_address, &m->l_addr, - format_vnet_sw_if_index_name, vnm, m->sw_if_index, - m->vrf_id); - else - s = format (s, "%U local %U:%d external %U:%d vrf %d", - format_snat_protocol, m->proto, - format_ip4_address, &m->l_addr, m->l_port, - format_vnet_sw_if_index_name, vnm, m->sw_if_index, - m->e_port, m->vrf_id); - - return s; -} - -u8 * format_det_map_ses (u8 * s, va_list * args) -{ - snat_det_map_t * det_map = va_arg (*args, snat_det_map_t *); - ip4_address_t in_addr, out_addr; - u32 in_offset, out_offset; - snat_det_session_t * ses = va_arg (*args, snat_det_session_t *); - u32 * i = va_arg (*args, u32 *); - - u32 user_index = *i / SNAT_DET_SES_PER_USER; - in_addr.as_u32 = clib_host_to_net_u32 ( - clib_net_to_host_u32(det_map->in_addr.as_u32) + user_index); - in_offset = clib_net_to_host_u32(in_addr.as_u32) - - clib_net_to_host_u32(det_map->in_addr.as_u32); - out_offset = in_offset / det_map->sharing_ratio; - out_addr.as_u32 = clib_host_to_net_u32( - clib_net_to_host_u32(det_map->out_addr.as_u32) + out_offset); - s = format (s, "in %U:%d out %U:%d external host %U:%d state: %U expire: %d\n", - format_ip4_address, &in_addr, - clib_net_to_host_u16 (ses->in_port), - format_ip4_address, &out_addr, - clib_net_to_host_u16 (ses->out.out_port), - format_ip4_address, &ses->out.ext_host_addr, - clib_net_to_host_u16 (ses->out.ext_host_port), - format_snat_session_state, ses->state, - ses->expire); - - return s; -} - static void nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete) + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete) { snat_main_t *sm = &snat_main; snat_static_map_resolve_t *rp; @@ -3160,9 +4207,9 @@ nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, { rp = sm->to_resolve + i; if (rp->addr_only == 0) - continue; + continue; if (rp->sw_if_index == sw_if_index) - goto match; + goto match; } return; @@ -3182,12 +4229,12 @@ match: { /* Don't trip over lease renewal, static config */ if (m) - return; + return; } else { if (!m) - return; + return; } /* Indetity mapping? */ @@ -3197,27 +4244,24 @@ match: l_addr.as_u32 = rp->l_addr.as_u32; /* Add the static mapping */ rv = snat_add_static_mapping (l_addr, - address[0], - rp->l_port, - rp->e_port, - rp->vrf_id, - rp->addr_only, - ~0 /* sw_if_index */, - rp->proto, - !is_delete, - 0, 0, rp->tag); + address[0], + rp->l_port, + rp->e_port, + rp->vrf_id, + rp->addr_only, ~0 /* sw_if_index */ , + rp->proto, !is_delete, rp->twice_nat, + rp->out2in_only, rp->tag, rp->identity_nat); if (rv) - nat_log_notice ("snat_add_static_mapping returned %d", rv); + nat_elog_notice_X1 ("snat_add_static_mapping returned %d", "i4", rv); } static void snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete) + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete) { snat_main_t *sm = &snat_main; snat_static_map_resolve_t *rp; @@ -3227,18 +4271,18 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im, u8 twice_nat = 0; snat_address_t *addresses = sm->addresses; - for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) + for (i = 0; i < vec_len (sm->auto_add_sw_if_indices); i++) { if (sw_if_index == sm->auto_add_sw_if_indices[i]) - goto match; + goto match; } - for (i = 0; i < vec_len(sm->auto_add_sw_if_indices_twice_nat); i++) + for (i = 0; i < vec_len (sm->auto_add_sw_if_indices_twice_nat); i++) { twice_nat = 1; addresses = sm->twice_nat_addresses; if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i]) - goto match; + goto match; } return; @@ -3247,98 +4291,102 @@ match: if (!is_delete) { /* Don't trip over lease renewal, static config */ - for (j = 0; j < vec_len(addresses); j++) - if (addresses[j].addr.as_u32 == address->as_u32) - return; + for (j = 0; j < vec_len (addresses); j++) + if (addresses[j].addr.as_u32 == address->as_u32) + return; (void) snat_add_address (sm, address, ~0, twice_nat); /* Scan static map resolution vector */ for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->addr_only) - continue; - /* On this interface? */ - if (rp->sw_if_index == sw_if_index) - { - /* Indetity mapping? */ - if (rp->l_addr.as_u32 == 0) - l_addr.as_u32 = address[0].as_u32; - else - l_addr.as_u32 = rp->l_addr.as_u32; - /* Add the static mapping */ - rv = snat_add_static_mapping (l_addr, - address[0], - rp->l_port, - rp->e_port, - rp->vrf_id, - rp->addr_only, - ~0 /* sw_if_index */, - rp->proto, - rp->is_add, - 0, 0, rp->tag); - if (rv) - nat_log_notice ("snat_add_static_mapping returned %d", rv); - } - } + { + rp = sm->to_resolve + j; + if (rp->addr_only) + continue; + /* On this interface? */ + if (rp->sw_if_index == sw_if_index) + { + /* Indetity mapping? */ + if (rp->l_addr.as_u32 == 0) + l_addr.as_u32 = address[0].as_u32; + else + l_addr.as_u32 = rp->l_addr.as_u32; + /* Add the static mapping */ + rv = snat_add_static_mapping (l_addr, + address[0], + rp->l_port, + rp->e_port, + rp->vrf_id, + rp->addr_only, + ~0 /* sw_if_index */ , + rp->proto, + rp->is_add, rp->twice_nat, + rp->out2in_only, rp->tag, + rp->identity_nat); + if (rv) + nat_elog_notice_X1 ("snat_add_static_mapping returned %d", + "i4", rv); + } + } return; } else { - (void) snat_del_address(sm, address[0], 1, twice_nat); + (void) snat_del_address (sm, address[0], 1, twice_nat); return; } } -int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del, - u8 twice_nat) +int +snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, + u8 twice_nat) { - ip4_main_t * ip4_main = sm->ip4_main; - ip4_address_t * first_int_addr; + ip4_main_t *ip4_main = sm->ip4_main; + ip4_address_t *first_int_addr; snat_static_map_resolve_t *rp; u32 *indices_to_delete = 0; int i, j; u32 *auto_add_sw_if_indices = - twice_nat ? sm->auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices; + twice_nat ? sm-> + auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices; - first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, - 0 /* just want the address*/); + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address */ + ); - for (i = 0; i < vec_len(auto_add_sw_if_indices); i++) + for (i = 0; i < vec_len (auto_add_sw_if_indices); i++) { if (auto_add_sw_if_indices[i] == sw_if_index) - { - if (is_del) - { - /* if have address remove it */ - if (first_int_addr) - (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat); - else - { - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->sw_if_index == sw_if_index) - vec_add1 (indices_to_delete, j); - } - if (vec_len(indices_to_delete)) - { - for (j = vec_len(indices_to_delete)-1; j >= 0; j--) - vec_del1(sm->to_resolve, j); - vec_free(indices_to_delete); - } - } - if (twice_nat) - vec_del1(sm->auto_add_sw_if_indices_twice_nat, i); - else - vec_del1(sm->auto_add_sw_if_indices, i); - } - else - return VNET_API_ERROR_VALUE_EXIST; + { + if (is_del) + { + /* if have address remove it */ + if (first_int_addr) + (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat); + else + { + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->sw_if_index == sw_if_index) + vec_add1 (indices_to_delete, j); + } + if (vec_len (indices_to_delete)) + { + for (j = vec_len (indices_to_delete) - 1; j >= 0; j--) + vec_del1 (sm->to_resolve, j); + vec_free (indices_to_delete); + } + } + if (twice_nat) + vec_del1 (sm->auto_add_sw_if_indices_twice_nat, i); + else + vec_del1 (sm->auto_add_sw_if_indices, i); + } + else + return VNET_API_ERROR_VALUE_EXIST; - return 0; - } + return 0; + } } if (is_del) @@ -3346,20 +4394,20 @@ int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del, /* add to the auto-address list */ if (twice_nat) - vec_add1(sm->auto_add_sw_if_indices_twice_nat, sw_if_index); + vec_add1 (sm->auto_add_sw_if_indices_twice_nat, sw_if_index); else - vec_add1(sm->auto_add_sw_if_indices, sw_if_index); + vec_add1 (sm->auto_add_sw_if_indices, sw_if_index); /* If the address is already bound - or static - add it now */ if (first_int_addr) - (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); + (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); return 0; } int -nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, - snat_protocol_t proto, u32 vrf_id, int is_in) +nat44_del_session (snat_main_t * sm, ip4_address_t * addr, u16 port, + snat_protocol_t proto, u32 vrf_id, int is_in) { snat_main_per_thread_data_t *tsm; clib_bihash_kv_8_8_t kv, value; @@ -3376,7 +4424,7 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, if (sm->num_workers > 1) tsm = vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, fib_index)); + sm->worker_in2out_cb (&ip, fib_index, 0)); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); @@ -3389,10 +4437,10 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, if (!clib_bihash_search_8_8 (t, &kv, &value)) { if (pool_is_free_index (tsm->sessions, value.value)) - return VNET_API_ERROR_UNSPECIFIED; + return VNET_API_ERROR_UNSPECIFIED; s = pool_elt_at_index (tsm->sessions, value.value); - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); return 0; } @@ -3401,9 +4449,9 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, } int -nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, - ip4_address_t *eh_addr, u16 eh_port, u8 proto, - u32 vrf_id, int is_in) +nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 vrf_id, int is_in) { ip4_header_t ip; clib_bihash_16_8_t *t; @@ -3420,7 +4468,7 @@ nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, if (sm->num_workers > 1) tsm = vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, fib_index)); + sm->worker_in2out_cb (&ip, fib_index, 0)); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); @@ -3430,7 +4478,7 @@ nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, key.l_port = clib_host_to_net_u16 (port); key.r_port = clib_host_to_net_u16 (eh_port); key.proto = proto; - key.fib_index = clib_host_to_net_u32 (fib_index); + key.fib_index = fib_index; kv.key[0] = key.as_u64[0]; kv.key[1] = key.as_u64[1]; if (clib_bihash_search_16_8 (t, &kv, &value)) @@ -3439,7 +4487,7 @@ nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, if (pool_is_free_index (tsm->sessions, value.value)) return VNET_API_ERROR_UNSPECIFIED; s = pool_elt_at_index (tsm->sessions, value.value); - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); return 0; } @@ -3449,17 +4497,66 @@ nat_set_alloc_addr_and_port_mape (u16 psid, u16 psid_offset, u16 psid_length) { snat_main_t *sm = &snat_main; + sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE; sm->alloc_addr_and_port = nat_alloc_addr_and_port_mape; sm->psid = psid; sm->psid_offset = psid_offset; sm->psid_length = psid_length; } +void +nat_set_alloc_addr_and_port_range (u16 start_port, u16 end_port) +{ + snat_main_t *sm = &snat_main; + + sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE; + sm->alloc_addr_and_port = nat_alloc_addr_and_port_range; + sm->start_port = start_port; + sm->end_port = end_port; +} + void nat_set_alloc_addr_and_port_default (void) { snat_main_t *sm = &snat_main; + sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; sm->alloc_addr_and_port = nat_alloc_addr_and_port_default; } +VLIB_NODE_FN (nat_default_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat_default_node) = { + .name = "nat-default", + .vector_size = sizeof (u32), + .format_trace = 0, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, + .n_next_nodes = NAT_N_NEXT, + .next_nodes = { + [NAT_NEXT_DROP] = "error-drop", + [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT_NEXT_IN2OUT_PRE] = "nat-pre-in2out", + [NAT_NEXT_OUT2IN_PRE] = "nat-pre-out2in", + [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out", + [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath", + [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", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */