diff options
author | Matus Fabian <matfabia@cisco.com> | 2017-09-04 02:17:18 -0700 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2017-09-06 08:49:25 +0000 |
commit | 704018cf117b6667f08b09d6db5fbec105bf6d57 (patch) | |
tree | 27a52337f4b5f35092a422290998f7c37b4071f7 /src/plugins/nat/nat.c | |
parent | 030ab4815f777d6a0fdbfbc457e0f55fc32df8d3 (diff) |
NAT: Destination NAT44 with load-balancing (VPP-954)
added load-balancing static mappings with unequal load support
Change-Id: Ie505e41f24d46f812b94dd28bdafe3dc170a6060
Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src/plugins/nat/nat.c')
-rw-r--r-- | src/plugins/nat/nat.c | 430 |
1 files changed, 399 insertions, 31 deletions
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index f9ecb943a90..fabd0bc26ae 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -506,15 +506,16 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (snat_is_unk_proto_session (s)) { clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; + nat_ed_ses_key_t up_key; up_key.l_addr = s->in2out.addr; up_key.r_addr = s->ext_host_addr; up_key.fib_index = s->in2out.fib_index; up_key.proto = s->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_key.rsvd = 0; + up_key.l_port = 0; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0)) clib_warning ("in2out key del failed"); @@ -522,7 +523,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, up_key.fib_index = s->out2in.fib_index; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0)) clib_warning ("out2in key del failed"); @@ -589,6 +590,243 @@ delete: 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) +{ + 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; + snat_user_key_t w_key0; + snat_worker_key_t w_key1; + u32 worker_index = 0; + + m_key.addr = e_addr; + m_key.port = e_port; + m_key.protocol = proto; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + 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); + + if (is_add) + { + if (m) + 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); + + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ + if (!sm->static_mapping_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) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + return VNET_API_ERROR_INVALID_VALUE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + if (e_port > 1024) \ + a->busy_##n##_ports++; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("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)); + 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_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.protocol = m->proto; + m_key.fib_index = sm->outside_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_external, &kv, 1)) + { + clib_warning ("static_mapping_by_external key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (m->external_port); + kv.key = m_key.as_u64; + kv.value = ~0ULL; + if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 1)) + { + clib_warning ("static_mapping_by_local key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + m_key.fib_index = m->fib_index; + + /* Assign worker */ + if (sm->workers) + { + w_key0.addr = locals[0].addr; + w_key0.fib_index = fib_index; + kv.key = w_key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) + worker_index = sm->first_worker_index + + sm->workers[sm->next_worker++ % vec_len (sm->workers)]; + else + worker_index = value.value; + + w_key1.addr = m->external_addr; + w_key1.port = clib_host_to_net_u16 (m->external_port); + w_key1.fib_index = sm->outside_fib_index; + kv.key = w_key1.as_u64; + kv.value = worker_index; + if (clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1)) + { + clib_warning ("worker-by-out add key failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + for (i = 0; i < vec_len (locals); i++) + { + m_key.addr = locals[i].addr; + 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 = locals[i - 1].prefix + locals[i].probability; + vec_add1 (m->locals, locals[i]); + m_key.port = clib_host_to_net_u16 (locals[i].port); + kv.key = m_key.as_u64; + kv.value = ~0ULL; + if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 1)) + { + clib_warning ("in2out key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + /* Assign worker */ + if (sm->workers) + { + w_key0.addr = locals[i].addr; + w_key0.fib_index = fib_index; + kv.key = w_key0.as_u64; + kv.value = worker_index; + if (clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1)) + { + clib_warning ("worker-by-in key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + } + } + else + { + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4); + + /* Free external address port */ + if (!sm->static_mapping_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) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + if (e_port > 1024) \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("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 = sm->outside_fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0)) + { + clib_warning ("static_mapping_by_external key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (m->external_port); + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 0)) + { + clib_warning ("outi2in key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + vec_foreach (local, m->locals) + { + m_key.addr = local->addr; + m_key.port = local->port; + m_key.fib_index = m->fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0)) + { + clib_warning ("static_mapping_by_local key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (local->port); + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 0)) + { + clib_warning ("in2out key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + pool_put (sm->static_mappings, m); + } + + return 0; +} + int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) { snat_address_t *a = 0; @@ -649,15 +887,16 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) if (snat_is_unk_proto_session (ses)) { clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; + nat_ed_ses_key_t up_key; up_key.l_addr = ses->in2out.addr; up_key.r_addr = ses->ext_host_addr; up_key.fib_index = ses->in2out.fib_index; up_key.proto = ses->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_key.rsvd = 0; + up_key.l_port = 0; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0)) clib_warning ("in2out key del failed"); @@ -665,7 +904,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) up_key.fib_index = ses->out2in.fib_index; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0)) clib_warning ("out2in key del failed"); } @@ -1048,6 +1287,7 @@ int snat_static_mapping_match (snat_main_t * sm, 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; if (by_external) mapping_hash = &sm->static_mapping_by_external; @@ -1073,11 +1313,29 @@ int snat_static_mapping_match (snat_main_t * sm, if (by_external) { - 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); + if (vec_len (m->locals)) + { + hi = vec_len (m->locals) - 1; + rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); + while (lo < hi) + { + mid = ((hi - 1) >> 1) + lo; + (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid); + } + if (!(m->locals[lo].prefix >= rand)) + return 1; + mapping->addr = m->locals[lo].addr; + mapping->port = clib_host_to_net_u16 (m->locals[lo].port); + } + 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; + mapping->protocol = m->proto; } else { @@ -1518,6 +1776,101 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = { }; static clib_error_t * +add_lb_static_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t * error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0; + int is_add = 1; + int rv; + snat_protocol_t proto; + u8 proto_set = 0; + nat44_lb_addr_port_t *locals = 0, local; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "local %U:%u probability %u", + unformat_ip4_address, &l_addr, &l_port, &probability)) + { + memset (&local, 0, sizeof (local)); + local.addr = l_addr; + local.port = (u16) l_port; + local.probability = (u8) probability; + vec_add1 (locals, local); + } + else if (unformat (line_input, "external %U:%u", unformat_ip4_address, + &e_addr, &e_port)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "protocol %U", unformat_snat_protocol, + &proto)) + proto_set = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (locals) < 2) + { + error = clib_error_return (0, "at least two local must be set"); + goto done; + } + + if (!proto_set) + { + error = clib_error_return (0, "missing protocol"); + goto done; + } + + rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id, + locals, is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External addres must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + vec_free (locals); + + return error; +} + +VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = { + .path = "nat44 add load-balancing static mapping", + .function = add_lb_static_mapping_command_fn, + .short_help = + "nat44 add load-balancing static mapping protocol tcp|udp external <addr>:<port> local <addr>:<port> probability <n> [vrf <table-id>] [del]", +}; + +static clib_error_t * set_workers_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -1839,10 +2192,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets, user_memory_size); - clib_bihash_init_16_8 (&sm->in2out_unk_proto, "in2out-unk-proto", + clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", translation_buckets, translation_memory_size); - clib_bihash_init_16_8 (&sm->out2in_unk_proto, "out2in-unk-proto", + clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed", translation_buckets, translation_memory_size); } else @@ -1884,18 +2237,10 @@ u8 * format_snat_session_state (u8 * s, va_list * args) u8 * format_snat_key (u8 * s, va_list * args) { snat_session_key_t * key = va_arg (*args, snat_session_key_t *); - char * protocol_string = "unknown"; - static char *protocol_strings[] = { - "UDP", - "TCP", - "ICMP", - }; - - if (key->protocol < ARRAY_LEN(protocol_strings)) - protocol_string = protocol_strings[key->protocol]; - s = format (s, "%U proto %s port %d fib %d", - format_ip4_address, &key->addr, protocol_string, + 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; } @@ -1919,6 +2264,9 @@ u8 * format_snat_session (u8 * s, va_list * args) s = format (s, " i2o %U\n", format_snat_key, &sess->in2out); s = format (s, " o2i %U\n", format_snat_key, &sess->out2in); } + if (sess->ext_host_addr.as_u32) + s = format (s, " external host %U\n", + format_ip4_address, &sess->ext_host_addr); 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); @@ -1926,6 +2274,8 @@ u8 * format_snat_session (u8 * s, va_list * args) s = format (s, " static translation\n"); else s = format (s, " dynamic translation\n"); + if (sess->flags & SNAT_SESSION_FLAG_LOAD_BALANCING) + s = format (s, " load-balancing\n"); return s; } @@ -1973,6 +2323,7 @@ u8 * format_snat_user (u8 * s, va_list * args) 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", @@ -1980,12 +2331,25 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args) format_ip4_address, &m->external_addr, 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->local_addr, m->local_port, - format_ip4_address, &m->external_addr, m->external_port, - m->vrf_id); - + { + if (vec_len (m->locals)) + { + s = format (s, "%U vrf %d external %U:%d", + format_snat_protocol, m->proto, + m->vrf_id, + format_ip4_address, &m->external_addr, m->external_port); + 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", + 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); + } return s; } @@ -2208,6 +2572,10 @@ show_snat_command_fn (vlib_main_t * vm, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed, + verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, + verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_out, |