summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/nat')
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed.c1257
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed.h211
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_affinity.h2
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_api.c337
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_cli.c197
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_format.c71
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_handoff.c6
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_in2out.c2
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_out2in.c8
9 files changed, 1195 insertions, 896 deletions
diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c
index dcd7ae0a140..4e13907a9d8 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed.c
@@ -385,43 +385,15 @@ is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr)
snat_static_mapping_t *m;
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;
+ if (is_sm_addr_only (m->flags) || is_sm_out2in_only (m->flags) ||
+ is_sm_identity_nat (m->flags))
+ continue;
+ if (m->external_addr.as_u32 == addr.as_u32)
+ return 1;
}
-
return 0;
}
-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, nat_protocol_t proto,
- int addr_only, u8 *tag, int twice_nat,
- int out2in_only, int identity_nat,
- ip4_address_t pool_addr, int exact)
-{
- snat_static_map_resolve_t *rp;
-
- vec_add2 (sm->to_resolve, rp, 1);
- rp->l_addr.as_u32 = l_addr.as_u32;
- rp->l_port = l_port;
- rp->sw_if_index = sw_if_index;
- rp->e_port = e_port;
- rp->vrf_id = vrf_id;
- rp->proto = proto;
- rp->addr_only = addr_only;
- rp->twice_nat = twice_nat;
- rp->out2in_only = out2in_only;
- rp->identity_nat = identity_nat;
- rp->tag = vec_dup (tag);
- rp->pool_addr = pool_addr;
- rp->exact = exact;
-}
-
u32
get_thread_idx_by_port (u16 e_port)
{
@@ -481,610 +453,814 @@ nat_ed_static_mapping_del_sessions (snat_main_t * sm,
}
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, nat_protocol_t proto, int is_add,
- twice_nat_type_t twice_nat, u8 out2in_only, u8 *tag,
- u8 identity_nat, ip4_address_t pool_addr, int exact)
+nat44_ed_reserve_port (ip4_address_t addr, u16 port, nat_protocol_t proto)
{
+ u32 ti = get_thread_idx_by_port (port);
snat_main_t *sm = &snat_main;
- snat_static_mapping_t *m;
- clib_bihash_kv_8_8_t kv, value;
snat_address_t *a = 0;
- u32 fib_index = ~0;
- snat_interface_t *interface;
- snat_main_per_thread_data_t *tsm;
- snat_static_map_resolve_t *rp, *rp_match = 0;
- nat44_lb_addr_port_t *local;
- u32 find = ~0;
int i;
- /* If the external address is a specific interface address */
- if (sw_if_index != ~0)
+ for (i = 0; i < vec_len (sm->addresses); i++)
{
- 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;
+ a = sm->addresses + i;
- if (!addr_only)
- {
- if ((rp->l_port != l_port && rp->e_port != e_port)
- || rp->proto != proto)
- continue;
- }
+ if (a->addr.as_u32 != addr.as_u32)
+ continue;
- rp_match = rp;
- break;
+ switch (proto)
+ {
+#define _(N, j, n, s) \
+ case NAT_PROTOCOL_##N: \
+ if (a->busy_##n##_port_refcounts[port]) \
+ goto done; \
+ ++a->busy_##n##_port_refcounts[port]; \
+ if (port > 1024) \
+ { \
+ a->busy_##n##_ports++; \
+ a->busy_##n##_ports_per_thread[ti]++; \
+ } \
+ break;
+ foreach_nat_protocol
+#undef _
+ default : nat_elog_info (sm, "unknown protocol");
+ goto done;
}
- /* Might be already set... */
- first_int_addr = ip4_interface_first_address
- (sm->ip4_main, sw_if_index, 0 /* just want the address */ );
+ return 0;
+ }
- if (is_add)
- {
- if (rp_match)
- return VNET_API_ERROR_VALUE_EXIST;
+done:
+ return 1;
+}
- snat_add_static_mapping_when_resolved (
- sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, addr_only,
- tag, twice_nat, out2in_only, identity_nat, pool_addr, exact);
+int
+nat44_ed_free_port (ip4_address_t addr, u16 port, nat_protocol_t proto)
+{
+ u32 ti = get_thread_idx_by_port (port);
+ snat_main_t *sm = &snat_main;
+ snat_address_t *a = 0;
+ int i;
- /* 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;
+ for (i = 0; i < vec_len (sm->addresses); i++)
+ {
+ a = sm->addresses + i;
- vec_del1 (sm->to_resolve, i);
+ if (a->addr.as_u32 != addr.as_u32)
+ continue;
- 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;
+ switch (proto)
+ {
+#define _(N, j, n, s) \
+ case NAT_PROTOCOL_##N: \
+ --a->busy_##n##_port_refcounts[port]; \
+ if (port > 1024) \
+ { \
+ a->busy_##n##_ports--; \
+ a->busy_##n##_ports_per_thread[ti]--; \
+ } \
+ break;
+ foreach_nat_protocol
+#undef _
+ default : nat_elog_info (sm, "unknown protocol");
+ goto done;
}
+
+ return 0;
}
- init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto);
- if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
- m = 0;
- else
- m = pool_elt_at_index (sm->static_mappings, value.value);
+done:
+ return 1;
+}
- if (is_add)
- {
- if (m)
- {
- if (is_identity_static_mapping (m))
- {
- pool_foreach (local, m->locals)
- {
- if (local->vrf_id == vrf_id)
- return VNET_API_ERROR_VALUE_EXIST;
- }
- 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,
- sm->fib_src_low);
- init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
- m->proto, 0, 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;
- }
+void
+nat44_ed_add_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+ nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+ u32 flags, ip4_address_t pool_addr, u8 *tag)
+{
+ snat_static_map_resolve_t *rp;
+ snat_main_t *sm = &snat_main;
- if (twice_nat && addr_only)
- return VNET_API_ERROR_UNSUPPORTED;
+ vec_add2 (sm->to_resolve, rp, 1);
+ rp->l_addr.as_u32 = l_addr.as_u32;
+ rp->l_port = l_port;
+ rp->e_port = e_port;
+ rp->sw_if_index = sw_if_index;
+ rp->vrf_id = vrf_id;
+ rp->proto = proto;
+ rp->flags = flags;
+ rp->pool_addr = pool_addr;
+ rp->tag = vec_dup (tag);
+}
- /* Convert VRF id to FIB index */
- if (vrf_id != ~0)
- fib_index =
- fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
- sm->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_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
- }
+int
+nat44_ed_get_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+ nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+ u32 flags, int *out)
+{
+ snat_static_map_resolve_t *rp;
+ snat_main_t *sm = &snat_main;
+ int i;
- if (!(out2in_only || identity_nat))
- {
- init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index,
- addr_only ? 0 : proto);
- if (!clib_bihash_search_8_8
- (&sm->static_mapping_by_local, &kv, &value))
- return VNET_API_ERROR_VALUE_EXIST;
- }
+ for (i = 0; i < vec_len (sm->to_resolve); i++)
+ {
+ rp = sm->to_resolve + i;
- /* 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))
+ if (rp->sw_if_index == sw_if_index && rp->vrf_id == vrf_id)
{
- for (i = 0; i < vec_len (sm->addresses); i++)
+ if (is_sm_identity_nat (rp->flags) && is_sm_identity_nat (flags))
{
- if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
+ if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
{
- a = sm->addresses + i;
- /* External port must be unused */
- switch (proto)
+ if (rp->e_port != e_port || rp->proto != proto)
{
-#define _(N, j, n, s) \
- case NAT_PROTOCOL_##N: \
- if (a->busy_##n##_port_refcounts[e_port]) \
- return VNET_API_ERROR_INVALID_VALUE; \
- ++a->busy_##n##_port_refcounts[e_port]; \
- if (e_port > 1024) \
- { \
- a->busy_##n##_ports++; \
- a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \
- } \
- break;
- foreach_nat_protocol
-#undef _
- default : nat_elog_info (sm, "unknown protocol");
- return VNET_API_ERROR_INVALID_VALUE_2;
+ continue;
}
- break;
}
}
- /* External address must be allocated */
- if (!a && (l_addr.as_u32 != e_addr.as_u32))
+ else if (rp->l_addr.as_u32 == l_addr.as_u32)
{
- if (sw_if_index != ~0)
+ if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
{
- for (i = 0; i < vec_len (sm->to_resolve); i++)
+ if (rp->l_port != l_port || rp->e_port != e_port ||
+ rp->proto != proto)
{
- 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;
+ continue;
}
}
- return VNET_API_ERROR_NO_SUCH_ENTRY;
}
+ else
+ {
+ continue;
+ }
+ if (out)
+ {
+ *out = i;
+ }
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+nat44_ed_del_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+ nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+ u32 flags)
+{
+ snat_main_t *sm = &snat_main;
+ int i;
+ if (!nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+ sw_if_index, flags, &i))
+ {
+ vec_del1 (sm->to_resolve, i);
+ return 0;
+ }
+ return 1;
+}
+
+static_always_inline int
+nat44_ed_validate_sm_input (u32 flags)
+{
+ // identity nat can be initiated only from inside interface
+ if (is_sm_identity_nat (flags) && is_sm_out2in_only (flags))
+ {
+ return VNET_API_ERROR_UNSUPPORTED;
+ }
+
+ if (is_sm_twice_nat (flags) || is_sm_self_twice_nat (flags))
+ {
+ if (is_sm_addr_only (flags) || is_sm_identity_nat (flags))
+ {
+ return VNET_API_ERROR_UNSUPPORTED;
}
+ }
+ return 0;
+}
+
+int
+nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
+ u16 l_port, u16 e_port, nat_protocol_t proto,
+ u32 vrf_id, u32 sw_if_index, u32 flags,
+ ip4_address_t pool_addr, u8 *tag)
+{
+ snat_main_t *sm = &snat_main;
+ clib_bihash_kv_8_8_t kv, value;
+ snat_interface_t *interface;
+ nat44_lb_addr_port_t *local;
+ snat_static_mapping_t *m;
+ u32 fib_index = ~0;
+ int rv;
+
+ rv = nat44_ed_validate_sm_input (flags);
+ if (rv != 0)
+ {
+ return rv;
+ }
- pool_get (sm->static_mappings, m);
- clib_memset (m, 0, sizeof (*m));
- m->tag = vec_dup (tag);
- m->local_addr = l_addr;
- m->external_addr = e_addr;
- m->twice_nat = twice_nat;
+ if (is_sm_addr_only (flags))
+ {
+ e_port = l_port = proto = 0;
+ }
+
+ if (is_sm_switch_address (flags))
+ {
+ // this mapping is interface bound
+ ip4_address_t *first_int_addr;
- if (twice_nat == TWICE_NAT && exact)
+ // check if this record isn't registered for resolve
+ if (!nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+ sw_if_index, flags, 0))
{
- m->flags |= NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS;
- m->pool_addr = pool_addr;
+ return VNET_API_ERROR_VALUE_EXIST;
}
+ // register record for resolve
+ nat44_ed_add_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+ sw_if_index, flags, pool_addr, tag);
- 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)
+ first_int_addr =
+ ip4_interface_first_address (sm->ip4_main, sw_if_index, 0);
+ if (!first_int_addr)
{
- m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT;
- pool_get (m->locals, local);
- local->vrf_id = vrf_id;
- local->fib_index = fib_index;
+ // dhcp resolution required
+ return 0;
}
- else
+
+ e_addr.as_u32 = first_int_addr->as_u32;
+ }
+
+ if (is_sm_identity_nat (flags))
+ {
+ l_port = e_port;
+ l_addr.as_u32 = e_addr.as_u32;
+ }
+
+ // fib index 0
+ init_nat_k (&kv, e_addr, e_port, 0, proto);
+
+ 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_sm_identity_nat (m->flags))
{
- m->vrf_id = vrf_id;
- m->fib_index = fib_index;
+ return VNET_API_ERROR_VALUE_EXIST;
}
- if (!addr_only)
+
+ // case:
+ // adding local identity nat record for different vrf table
+ pool_foreach (local, m->locals)
{
- m->local_port = l_port;
- m->external_port = e_port;
- m->proto = proto;
+ if (local->vrf_id == vrf_id)
+ {
+ return VNET_API_ERROR_VALUE_EXIST;
+ }
}
- if (sm->num_workers > 1)
+ 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, sm->fib_src_low);
+
+ init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
+ m->proto, 0, m - sm->static_mappings);
+ clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
+
+ return 0;
+ }
+
+ if (vrf_id != ~0)
+ {
+ fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
+ sm->fib_src_low);
+ }
+ else
+ {
+ // fallback to default vrf
+ vrf_id = sm->inside_vrf_id;
+ fib_index = sm->inside_fib_index;
+ fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+ }
+
+ // test if local mapping record doesn't exist
+ // identity nat supports multiple records in local mapping
+ if (!(is_sm_out2in_only (flags) || is_sm_identity_nat (flags)))
+ {
+ init_nat_k (&kv, l_addr, l_port, fib_index, proto);
+ if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value))
{
- ip4_header_t ip = {
- .src_address = m->local_addr,
- };
- vec_add1 (m->workers, nat44_ed_get_in2out_worker_index (
- 0, &ip, m->fib_index, 0));
- tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
+ return VNET_API_ERROR_VALUE_EXIST;
}
- else
- tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
+ }
- if (!out2in_only)
+ if (!(is_sm_out2in_only (flags) || is_sm_addr_only (flags) ||
+ sm->static_mapping_only))
+ {
+ if (nat44_ed_reserve_port (e_addr, e_port, proto))
{
- init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto,
- 0, m - sm->static_mappings);
- clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
+ // remove resolve record
+ if (is_sm_switch_address (flags) && !is_sm_identity_nat (flags))
+ {
+ nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto,
+ vrf_id, sw_if_index, flags);
+ }
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
}
+ }
- init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0,
- m - sm->static_mappings);
- clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1);
+ pool_get (sm->static_mappings, m);
+ clib_memset (m, 0, sizeof (*m));
+
+ m->flags = flags;
+ m->local_addr = l_addr;
+ m->external_addr = e_addr;
+
+ m->tag = vec_dup (tag);
+
+ if (is_sm_exact_address (flags) && is_sm_twice_nat (flags))
+ {
+ m->pool_addr = pool_addr;
+ }
+
+ if (!is_sm_addr_only (flags))
+ {
+ m->local_port = l_port;
+ m->external_port = e_port;
+ m->proto = proto;
+ }
+
+ if (is_sm_identity_nat (flags))
+ {
+ pool_get (m->locals, local);
+
+ local->vrf_id = vrf_id;
+ local->fib_index = fib_index;
}
else
{
- if (!m)
+ m->vrf_id = vrf_id;
+ m->fib_index = fib_index;
+ }
+
+ if (!is_sm_out2in_only (flags))
+ {
+ init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, 0,
+ m - sm->static_mappings);
+ clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
+ }
+
+ init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0,
+ m - sm->static_mappings);
+ clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1);
+
+ if (sm->num_workers > 1)
+ {
+ // store worker index for this record
+ ip4_header_t ip = {
+ .src_address = m->local_addr,
+ };
+ u32 worker_index;
+ worker_index =
+ nat44_ed_get_in2out_worker_index (0, &ip, m->fib_index, 0);
+ vec_add1 (m->workers, worker_index);
+ }