NAT: DS-Lite (VPP-1040)
Dual-Stack Lite enables a broadband service provider to share IPv4 addresses among customers by combining two well-known technologies: IPv4-in-IPv6 and NAT. Change-Id: I039740f8548c623cd1ac89b8ecda1a6cc4aafb9c Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:
@ -26,7 +26,12 @@ nat_plugin_la_SOURCES = nat/nat.c \
|
||||
nat/nat64_cli.c \
|
||||
nat/nat64_in2out.c \
|
||||
nat/nat64_out2in.c \
|
||||
nat/nat64_db.c
|
||||
nat/nat64_db.c \
|
||||
nat/dslite_dpo.c \
|
||||
nat/dslite.c \
|
||||
nat/dslite_in2out.c \
|
||||
nat/dslite_out2in.c \
|
||||
nat/dslite_cli.c
|
||||
|
||||
API_FILES += nat/nat.api
|
||||
|
||||
|
164
src/plugins/nat/dslite.c
Normal file
164
src/plugins/nat/dslite.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <nat/dslite.h>
|
||||
#include <nat/dslite_dpo.h>
|
||||
#include <vnet/fib/fib_table.h>
|
||||
|
||||
dslite_main_t dslite_main;
|
||||
|
||||
void
|
||||
dslite_init (vlib_main_t * vm)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
vlib_thread_registration_t *tr;
|
||||
vlib_thread_main_t *tm = vlib_get_thread_main ();
|
||||
uword *p;
|
||||
dslite_per_thread_data_t *td;
|
||||
u32 translation_buckets = 1024;
|
||||
u32 translation_memory_size = 128 << 20;
|
||||
u32 b4_buckets = 128;
|
||||
u32 b4_memory_size = 64 << 20;
|
||||
|
||||
dm->first_worker_index = 0;
|
||||
dm->num_workers = 0;
|
||||
|
||||
p = hash_get_mem (tm->thread_registrations_by_name, "workers");
|
||||
if (p)
|
||||
{
|
||||
tr = (vlib_thread_registration_t *) p[0];
|
||||
if (tr)
|
||||
{
|
||||
dm->num_workers = tr->count;
|
||||
dm->first_worker_index = tr->first_index;
|
||||
}
|
||||
}
|
||||
|
||||
if (dm->num_workers)
|
||||
dm->port_per_thread = (0xffff - 1024) / dm->num_workers;
|
||||
else
|
||||
dm->port_per_thread = 0xffff - 1024;
|
||||
|
||||
vec_validate (dm->per_thread_data, tm->n_vlib_mains - 1);
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
vec_foreach (td, dm->per_thread_data)
|
||||
{
|
||||
clib_bihash_init_24_8 (&td->in2out, "in2out", translation_buckets,
|
||||
translation_memory_size);
|
||||
|
||||
clib_bihash_init_8_8 (&td->out2in, "out2in", translation_buckets,
|
||||
translation_memory_size);
|
||||
|
||||
clib_bihash_init_16_8 (&td->b4_hash, "b4s", b4_buckets, b4_memory_size);
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
dslite_dpo_module_init ();
|
||||
}
|
||||
|
||||
int
|
||||
dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr)
|
||||
{
|
||||
dpo_id_t dpo_v6 = DPO_INVALID;
|
||||
|
||||
dslite_dpo_create (DPO_PROTO_IP6, 0, &dpo_v6);
|
||||
fib_prefix_t pfx = {
|
||||
.fp_proto = FIB_PROTOCOL_IP6,
|
||||
.fp_len = 128,
|
||||
.fp_addr.ip6.as_u64[0] = addr->as_u64[0],
|
||||
.fp_addr.ip6.as_u64[1] = addr->as_u64[1],
|
||||
};
|
||||
fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI,
|
||||
FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v6);
|
||||
dpo_reset (&dpo_v6);
|
||||
|
||||
dm->aftr_ip6_addr.as_u64[0] = addr->as_u64[0];
|
||||
dm->aftr_ip6_addr.as_u64[1] = addr->as_u64[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr, u8 is_add)
|
||||
{
|
||||
vlib_thread_main_t *tm = vlib_get_thread_main ();
|
||||
snat_address_t *a = 0;
|
||||
int i = 0;
|
||||
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,
|
||||
};
|
||||
|
||||
for (i = 0; i < vec_len (dm->addr_pool); i++)
|
||||
{
|
||||
if (dm->addr_pool[i].addr.as_u32 == addr->as_u32)
|
||||
{
|
||||
a = dm->addr_pool + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_add)
|
||||
{
|
||||
if (a)
|
||||
return VNET_API_ERROR_VALUE_EXIST;
|
||||
vec_add2 (dm->addr_pool, a, 1);
|
||||
a->addr = *addr;
|
||||
#define _(N, i, n, s) \
|
||||
clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \
|
||||
a->busy_##n##_ports = 0; \
|
||||
vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
|
||||
foreach_snat_protocol
|
||||
#undef _
|
||||
dslite_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);
|
||||
dpo_reset (&dpo_v4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!a)
|
||||
return VNET_API_ERROR_NO_SUCH_ENTRY;
|
||||
#define _(N, id, n, s) \
|
||||
clib_bitmap_free (a->busy_##n##_port_bitmap); \
|
||||
vec_free (a->busy_##n##_ports_per_thread);
|
||||
foreach_snat_protocol
|
||||
#undef _
|
||||
fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_PLUGIN_HI);
|
||||
vec_del1 (dm->addr_pool, i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *
|
||||
format_dslite_trace (u8 * s, va_list * args)
|
||||
{
|
||||
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
|
||||
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
|
||||
dslite_trace_t *t = va_arg (*args, dslite_trace_t *);
|
||||
|
||||
s =
|
||||
format (s, "next index %d, session %d", t->next_index, t->session_index);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
133
src/plugins/nat/dslite.h
Normal file
133
src/plugins/nat/dslite.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef __included_dslite_h__
|
||||
#define __included_dslite_h__
|
||||
|
||||
#include <vppinfra/bihash_8_8.h>
|
||||
#include <vppinfra/bihash_16_8.h>
|
||||
#include <vppinfra/bihash_24_8.h>
|
||||
#include <nat/nat.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
ip6_address_t softwire_id;
|
||||
ip4_address_t addr;
|
||||
u16 port;
|
||||
u8 proto;
|
||||
u8 pad;
|
||||
};
|
||||
u64 as_u64[3];
|
||||
};
|
||||
} dslite_session_key_t;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
typedef CLIB_PACKED (struct
|
||||
{
|
||||
snat_session_key_t out2in;
|
||||
dslite_session_key_t in2out;
|
||||
u32 per_b4_index;
|
||||
u32 per_b4_list_head_index;
|
||||
f64 last_heard;
|
||||
u64 total_bytes;
|
||||
u32 total_pkts;
|
||||
u32 outside_address_index;
|
||||
}) dslite_session_t;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ip6_address_t addr;
|
||||
u32 sessions_per_b4_list_head_index;
|
||||
u32 nsessions;
|
||||
} dslite_b4_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Main lookup tables */
|
||||
clib_bihash_8_8_t out2in;
|
||||
clib_bihash_24_8_t in2out;
|
||||
|
||||
/* Find a B4 */
|
||||
clib_bihash_16_8_t b4_hash;
|
||||
|
||||
/* B4 pool */
|
||||
dslite_b4_t *b4s;
|
||||
|
||||
/* Session pool */
|
||||
dslite_session_t *sessions;
|
||||
|
||||
/* Pool of doubly-linked list elements */
|
||||
dlist_elt_t *list_pool;
|
||||
} dslite_per_thread_data_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ip6_address_t aftr_ip6_addr;
|
||||
ip4_address_t aftr_ip4_addr;
|
||||
dslite_per_thread_data_t *per_thread_data;
|
||||
snat_address_t *addr_pool;
|
||||
u32 num_workers;
|
||||
u32 first_worker_index;
|
||||
u16 port_per_thread;
|
||||
} dslite_main_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 next_index;
|
||||
u32 session_index;
|
||||
} dslite_trace_t;
|
||||
|
||||
#define foreach_dslite_error \
|
||||
_(IN2OUT, "valid in2out DS-Lite packets") \
|
||||
_(OUT2IN, "valid out2in DS-Lite packets") \
|
||||
_(NO_TRANSLATION, "no translation") \
|
||||
_(BAD_IP6_PROTOCOL, "bad ip6 protocol") \
|
||||
_(OUT_OF_PORTS, "out of ports") \
|
||||
_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \
|
||||
_(BAD_ICMP_TYPE, "unsupported icmp type") \
|
||||
_(UNKNOWN, "unknown")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define _(sym,str) DSLITE_ERROR_##sym,
|
||||
foreach_dslite_error
|
||||
#undef _
|
||||
DSLITE_N_ERROR,
|
||||
} dslite_error_t;
|
||||
|
||||
extern dslite_main_t dslite_main;
|
||||
extern vlib_node_registration_t dslite_in2out_node;
|
||||
extern vlib_node_registration_t dslite_in2out_slowpath_node;
|
||||
extern vlib_node_registration_t dslite_out2in_node;
|
||||
|
||||
void dslite_init (vlib_main_t * vm);
|
||||
int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr);
|
||||
int dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr,
|
||||
u8 is_add);
|
||||
u8 *format_dslite_trace (u8 * s, va_list * args);
|
||||
|
||||
#endif /* __included_dslite_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
280
src/plugins/nat/dslite_cli.c
Normal file
280
src/plugins/nat/dslite_cli.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nat/dslite.h>
|
||||
|
||||
static clib_error_t *
|
||||
dslite_add_del_pool_addr_command_fn (vlib_main_t * vm,
|
||||
unformat_input_t * input,
|
||||
vlib_cli_command_t * cmd)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
unformat_input_t _line_input, *line_input = &_line_input;
|
||||
ip4_address_t start_addr, end_addr, this_addr;
|
||||
u32 start_host_order, end_host_order;
|
||||
int i, count, rv;
|
||||
u8 is_add = 1;
|
||||
clib_error_t *error = 0;
|
||||
|
||||
/* 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, "%U - %U",
|
||||
unformat_ip4_address, &start_addr,
|
||||
unformat_ip4_address, &end_addr))
|
||||
;
|
||||
else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
|
||||
end_addr = start_addr;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
|
||||
end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
|
||||
|
||||
if (end_host_order < start_host_order)
|
||||
{
|
||||
error = clib_error_return (0, "end address less than start address");
|
||||
goto done;
|
||||
}
|
||||
|
||||
count = (end_host_order - start_host_order) + 1;
|
||||
this_addr = start_addr;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
rv = dslite_add_del_pool_addr (dm, &this_addr, is_add);
|
||||
|
||||
switch (rv)
|
||||
{
|
||||
case VNET_API_ERROR_NO_SUCH_ENTRY:
|
||||
error =
|
||||
clib_error_return (0, "DS-Lite pool address %U not exist.",
|
||||
format_ip4_address, &this_addr);
|
||||
goto done;
|
||||
case VNET_API_ERROR_VALUE_EXIST:
|
||||
error =
|
||||
clib_error_return (0, "DS-Lite pool address %U exist.",
|
||||
format_ip4_address, &this_addr);
|
||||
goto done;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
increment_v4_address (&this_addr);
|
||||
}
|
||||
|
||||
done:
|
||||
unformat_free (line_input);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
dslite_show_pool_command_fn (vlib_main_t * vm,
|
||||
unformat_input_t * input,
|
||||
vlib_cli_command_t * cmd)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
snat_address_t *ap;
|
||||
|
||||
vlib_cli_output (vm, "DS-Lite pool:");
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
vec_foreach (ap, dm->addr_pool)
|
||||
{
|
||||
vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
dslite_set_aftr_tunnel_addr_command_fn (vlib_main_t * vm,
|
||||
unformat_input_t * input,
|
||||
vlib_cli_command_t * cmd)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
unformat_input_t _line_input, *line_input = &_line_input;
|
||||
ip6_address_t ip6_addr;
|
||||
int rv;
|
||||
clib_error_t *error = 0;
|
||||
|
||||
/* 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, "%U", unformat_ip6_address, &ip6_addr))
|
||||
;
|
||||
else
|
||||
{
|
||||
error = clib_error_return (0, "unknown input '%U'",
|
||||
format_unformat_error, line_input);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr);
|
||||
|
||||
if (rv)
|
||||
error =
|
||||
clib_error_return (0,
|
||||
"Set DS-Lite AFTR tunnel endpoint address failed.");
|
||||
|
||||
done:
|
||||
unformat_free (line_input);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
dslite_show_aftr_ip6_addr_command_fn (vlib_main_t * vm,
|
||||
unformat_input_t * input,
|
||||
vlib_cli_command_t * cmd)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
|
||||
vlib_cli_output (vm, "%U", format_ip6_address, &dm->aftr_ip6_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 *
|
||||
format_dslite_session (u8 * s, va_list * args)
|
||||
{
|
||||
dslite_session_t *session = va_arg (*args, dslite_session_t *);
|
||||
u32 indent = format_get_indent (s);
|
||||
|
||||
s = format (s, "%Uin %U:%u out %U:%u protocol %U\n",
|
||||
format_white_space, indent + 2,
|
||||
format_ip4_address, &session->in2out.addr,
|
||||
clib_net_to_host_u16 (session->in2out.port),
|
||||
format_ip4_address, &session->out2in.addr,
|
||||
clib_net_to_host_u16 (session->out2in.port),
|
||||
format_snat_protocol, session->in2out.proto);
|
||||
s = format (s, "%Utotal pkts %d, total bytes %lld\n",
|
||||
format_white_space, indent + 4,
|
||||
session->total_pkts, session->total_bytes);
|
||||
return s;
|
||||
}
|
||||
|
||||
static u8 *
|
||||
format_dslite_b4 (u8 * s, va_list * args)
|
||||
{
|
||||
dslite_per_thread_data_t *td = va_arg (*args, dslite_per_thread_data_t *);
|
||||
dslite_b4_t *b4 = va_arg (*args, dslite_b4_t *);
|
||||
dlist_elt_t *head, *elt;
|
||||
u32 elt_index, head_index;
|
||||
u32 session_index;
|
||||
dslite_session_t *session;
|
||||
|
||||
s =
|
||||
format (s, "B4 %U %d sessions\n", format_ip6_address, &b4->addr,
|
||||
b4->nsessions);
|
||||
|
||||
if (b4->nsessions == 0)
|
||||
return s;
|
||||
|
||||
head_index = b4->sessions_per_b4_list_head_index;
|
||||
head = pool_elt_at_index (td->list_pool, head_index);
|
||||
elt_index = head->next;
|
||||
elt = pool_elt_at_index (td->list_pool, elt_index);
|
||||
session_index = elt->value;
|
||||
while (session_index != ~0)
|
||||
{
|
||||
session = pool_elt_at_index (td->sessions, session_index);
|
||||
s = format (s, "%U", format_dslite_session, session);
|
||||
elt_index = elt->next;
|
||||
elt = pool_elt_at_index (td->list_pool, elt_index);
|
||||
session_index = elt->value;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
dslite_show_sessions_command_fn (vlib_main_t * vm,
|
||||
unformat_input_t * input,
|
||||
vlib_cli_command_t * cmd)
|
||||
{
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
dslite_per_thread_data_t *td;
|
||||
dslite_b4_t *b4;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
vec_foreach (td, dm->per_thread_data)
|
||||
{
|
||||
pool_foreach (b4, td->b4s,
|
||||
({
|
||||
vlib_cli_output (vm, "%U", format_dslite_b4, td, b4);
|
||||
}));
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
VLIB_CLI_COMMAND (dslite_add_pool_address_command, static) = {
|
||||
.path = "dslite add pool address",
|
||||
.short_help = "dslite add pool address <ip4-range-start> [- <ip4-range-end>] "
|
||||
" [del]",
|
||||
.function = dslite_add_del_pool_addr_command_fn,
|
||||
};
|
||||
|
||||
VLIB_CLI_COMMAND (show_dslite_pool_command, static) = {
|
||||
.path = "show dslite pool",
|
||||
.short_help = "show dslite pool",
|
||||
.function = dslite_show_pool_command_fn,
|
||||
};
|
||||
|
||||
VLIB_CLI_COMMAND (dslite_set_aftr_tunnel_addr, static) = {
|
||||
.path = "dslite set aftr-tunnel-endpoint-address",
|
||||
.short_help = "dslite set aftr-tunnel-endpoint-address <ip6>",
|
||||
.function = dslite_set_aftr_tunnel_addr_command_fn,
|
||||
};
|
||||
|
||||
VLIB_CLI_COMMAND (dslite_show_aftr_ip6_addr, static) = {
|
||||
.path = "show dslite aftr-tunnel-endpoint-address",
|
||||
.short_help = "show dslite aftr-tunnel-endpoint-address",
|
||||
.function = dslite_show_aftr_ip6_addr_command_fn,
|
||||
};
|
||||
|
||||
VLIB_CLI_COMMAND (dslite_show_sessions, static) = {
|
||||
.path = "show dslite sessions",
|
||||
.short_help = "show dslite sessions",
|
||||
.function = dslite_show_sessions_command_fn,
|
||||
};
|
||||
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
80
src/plugins/nat/dslite_dpo.c
Normal file
80
src/plugins/nat/dslite_dpo.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <vnet/ip/ip.h>
|
||||
#include <nat/dslite_dpo.h>
|
||||
|
||||
dpo_type_t dslite_dpo_type;
|
||||
|
||||
void
|
||||
dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo)
|
||||
{
|
||||
dpo_set (dpo, dslite_dpo_type, dproto, aftr_index);
|
||||
}
|
||||
|
||||
u8 *
|
||||
format_dslite_dpo (u8 * s, va_list * args)
|
||||
{
|
||||
index_t index = va_arg (*args, index_t);
|
||||
CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
|
||||
|
||||
return (format (s, "DS-Lite: AFTR:%d", index));
|
||||
}
|
||||
|
||||
static void
|
||||
dslite_dpo_lock (dpo_id_t * dpo)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
dslite_dpo_unlock (dpo_id_t * dpo)
|
||||
{
|
||||
}
|
||||
|
||||
const static dpo_vft_t dslite_dpo_vft = {
|
||||
.dv_lock = dslite_dpo_lock,
|
||||
.dv_unlock = dslite_dpo_unlock,
|
||||
.dv_format = format_dslite_dpo,
|
||||
};
|
||||
|
||||
const static char *const dslite_ip4_nodes[] = {
|
||||
"dslite-out2in",
|
||||
NULL,
|
||||
};
|
||||
|
||||
const static char *const dslite_ip6_nodes[] = {
|
||||
"dslite-in2out",
|
||||
NULL,
|
||||
};
|
||||
|
||||
const static char *const *const dslite_nodes[DPO_PROTO_NUM] = {
|
||||
[DPO_PROTO_IP4] = dslite_ip4_nodes,
|
||||
[DPO_PROTO_IP6] = dslite_ip6_nodes,
|
||||
[DPO_PROTO_MPLS] = NULL,
|
||||
};
|
||||
|
||||
void
|
||||
dslite_dpo_module_init (void)
|
||||
{
|
||||
dslite_dpo_type = dpo_register_new_type (&dslite_dpo_vft, dslite_nodes);
|
||||
}
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
36
src/plugins/nat/dslite_dpo.h
Normal file
36
src/plugins/nat/dslite_dpo.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __included_dslite_dpo_h__
|
||||
#define __included_dslite_dpo_h__
|
||||
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/dpo/dpo.h>
|
||||
|
||||
void dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo);
|
||||
|
||||
u8 *format_dslite_dpo (u8 * s, va_list * args);
|
||||
|
||||
void dslite_dpo_module_init (void);
|
||||
|
||||
#endif /* __included_dslite_dpo_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
490
src/plugins/nat/dslite_in2out.c
Normal file
490
src/plugins/nat/dslite_in2out.c
Normal file
File diff suppressed because it is too large
Load Diff
297
src/plugins/nat/dslite_out2in.c
Normal file
297
src/plugins/nat/dslite_out2in.c
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <nat/dslite.h>
|
||||
|
||||
vlib_node_registration_t dslite_out2in_node;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DSLITE_OUT2IN_NEXT_IP4_LOOKUP,
|
||||
DSLITE_OUT2IN_NEXT_IP6_LOOKUP,
|
||||
DSLITE_OUT2IN_NEXT_DROP,
|
||||
DSLITE_OUT2IN_N_NEXT,
|
||||
} dslite_out2in_next_t;
|
||||
|
||||
static char *dslite_out2in_error_strings[] = {
|
||||
#define _(sym,string) string,
|
||||
foreach_dslite_error
|
||||
#undef _
|
||||
};
|
||||
|
||||
static inline u32
|
||||
dslite_icmp_out2in (dslite_main_t * dm, ip4_header_t * ip4,
|
||||
dslite_session_t ** sp, u32 next, u8 * error,
|
||||
u32 thread_index)
|
||||
{
|
||||
dslite_session_t *s = 0;
|
||||
icmp46_header_t *icmp = ip4_next_header (ip4);
|
||||
clib_bihash_kv_8_8_t kv, value;
|
||||
snat_session_key_t key;
|
||||
u32 n = next;
|
||||
icmp_echo_header_t *echo;
|
||||
u32 new_addr, old_addr;
|
||||
u16 old_id, new_id;
|
||||
ip_csum_t sum;
|
||||
|
||||
echo = (icmp_echo_header_t *) (icmp + 1);
|
||||
|
||||
if (icmp_is_error_message (icmp) || (icmp->type != ICMP4_echo_reply))
|
||||
{
|
||||
n = DSLITE_OUT2IN_NEXT_DROP;
|
||||
*error = DSLITE_ERROR_BAD_ICMP_TYPE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
key.addr = ip4->dst_address;
|
||||
key.port = echo->identifier;
|
||||
key.protocol = SNAT_PROTOCOL_ICMP;
|
||||
key.fib_index = 0;
|
||||
kv.key = key.as_u64;
|
||||
|
||||
if (clib_bihash_search_8_8
|
||||
(&dm->per_thread_data[thread_index].out2in, &kv, &value))
|
||||
{
|
||||
next = DSLITE_OUT2IN_NEXT_DROP;
|
||||
*error = DSLITE_ERROR_NO_TRANSLATION;
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
s =
|
||||
pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
|
||||
value.value);
|
||||
}
|
||||
|
||||
old_id = echo->identifier;
|
||||
echo->identifier = new_id = s->in2out.port;
|
||||
sum = icmp->checksum;
|
||||
sum = ip_csum_update (sum, old_id, new_id, icmp_echo_header_t, identifier);
|
||||
icmp->checksum = ip_csum_fold (sum);
|
||||
|
||||
old_addr = ip4->dst_address.as_u32;
|
||||
ip4->dst_address = s->in2out.addr;
|
||||
new_addr = ip4->dst_address.as_u32;
|
||||
|
||||
sum = ip4->checksum;
|
||||
sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
|
||||
ip4->checksum = ip_csum_fold (sum);
|
||||
|
||||
done:
|
||||
*sp = s;
|
||||
return n;
|
||||
}
|
||||
|
||||
static uword
|
||||
dslite_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
|
||||
vlib_frame_t * frame)
|
||||
{
|
||||
u32 n_left_from, *from, *to_next;
|
||||
dslite_out2in_next_t next_index;
|
||||
vlib_node_runtime_t *error_node;
|
||||
u32 thread_index = vlib_get_thread_index ();
|
||||
f64 now = vlib_time_now (vm);
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
|
||||
error_node = vlib_node_get_runtime (vm, dslite_out2in_node.index);
|
||||
|
||||
from = vlib_frame_vector_args (frame);
|
||||
n_left_from = frame->n_vectors;
|
||||
next_index = node->cached_next_index;
|
||||
|
||||
|
||||
while (n_left_from > 0)
|
||||
{
|
||||
u32 n_left_to_next;
|
||||
|
||||
vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
|
||||
|
||||
while (n_left_from > 0 && n_left_to_next > 0)
|
||||
{
|
||||
u32 bi0;
|
||||
vlib_buffer_t *b0;
|
||||
u32 next0 = DSLITE_OUT2IN_NEXT_IP6_LOOKUP;
|
||||
u8 error0 = DSLITE_ERROR_OUT2IN;
|
||||
ip4_header_t *ip40;
|
||||
ip6_header_t *ip60;
|
||||
u32 proto0;
|
||||
udp_header_t *udp0;
|
||||
tcp_header_t *tcp0;
|
||||
clib_bihash_kv_8_8_t kv0, value0;
|
||||
snat_session_key_t key0;
|
||||
dslite_session_t *s0 = 0;
|
||||
ip_csum_t sum0;
|
||||
u32 new_addr0, old_addr0;
|
||||
u16 new_port0, old_port0;
|
||||
|
||||
/* 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);
|
||||
ip40 = vlib_buffer_get_current (b0);
|
||||
proto0 = ip_proto_to_snat_proto (ip40->protocol);
|
||||
|
||||
if (PREDICT_FALSE (proto0 == ~0))
|
||||
{
|
||||
error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
|
||||
next0 = DSLITE_OUT2IN_NEXT_DROP;
|
||||
goto trace0;
|
||||
}
|
||||
|
||||
if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
|
||||
{
|
||||
next0 =
|
||||
dslite_icmp_out2in (dm, ip40, &s0, next0, &error0,
|
||||
thread_index);
|
||||
if (PREDICT_FALSE (next0 == DSLITE_OUT2IN_NEXT_DROP))
|
||||
goto trace0;
|
||||
|
||||
goto encap0;
|
||||
}
|
||||
|
||||
udp0 = ip4_next_header (ip40);
|
||||
tcp0 = (tcp_header_t *) udp0;
|
||||
|
||||
key0.addr = ip40->dst_address;
|
||||
key0.port = udp0->dst_port;
|
||||
key0.protocol = proto0;
|
||||
key0.fib_index = 0;
|
||||
kv0.key = key0.as_u64;
|
||||
|
||||
if (clib_bihash_search_8_8
|
||||
(&dm->per_thread_data[thread_index].out2in, &kv0, &value0))
|
||||
{
|
||||
next0 = DSLITE_OUT2IN_NEXT_DROP;
|
||||
error0 = DSLITE_ERROR_NO_TRANSLATION;
|
||||
goto trace0;
|
||||
}
|
||||
else
|
||||
{
|
||||
s0 =
|
||||
pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
|
||||
value0.value);
|
||||
}
|
||||
|
||||
old_addr0 = ip40->dst_address.as_u32;
|
||||
ip40->dst_address = s0->in2out.addr;
|
||||
new_addr0 = ip40->dst_address.as_u32;
|
||||
|
||||
sum0 = ip40->checksum;
|
||||
sum0 =
|
||||
ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
|
||||
dst_address);
|
||||
ip40->checksum = ip_csum_fold (sum0);
|
||||
|
||||
if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
|
||||
{
|
||||
old_port0 = tcp0->dst_port;
|
||||
tcp0->dst_port = s0->in2out.port;
|
||||
new_port0 = tcp0->dst_port;
|
||||
|
||||
sum0 = tcp0->checksum;
|
||||
sum0 =
|
||||
ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
|
||||
dst_address);
|
||||
sum0 =
|
||||
ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
|
||||
length);
|
||||
tcp0->checksum = ip_csum_fold (sum0);
|
||||
}
|
||||
else
|
||||
{
|
||||
old_port0 = udp0->dst_port;
|
||||
udp0->dst_port = s0->in2out.port;
|
||||
udp0->checksum = 0;
|
||||
}
|
||||
|
||||
encap0:
|
||||
/* Construct IPv6 header */
|
||||
vlib_buffer_advance (b0, -(sizeof (ip6_header_t)));
|
||||
ip60 = vlib_buffer_get_current (b0);
|
||||
ip60->ip_version_traffic_class_and_flow_label =
|
||||
clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
|
||||
ip60->payload_length = ip40->length;
|
||||
ip60->protocol = IP_PROTOCOL_IP_IN_IP;
|
||||
ip60->hop_limit = ip40->ttl;
|
||||
ip60->src_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0];
|
||||
ip60->src_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1];
|
||||
ip60->dst_address.as_u64[0] = s0->in2out.softwire_id.as_u64[0];
|
||||
ip60->dst_address.as_u64[1] = s0->in2out.softwire_id.as_u64[1];
|
||||
|
||||
/* Accounting */
|
||||
s0->last_heard = now;
|
||||
s0->total_pkts++;
|
||||
s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
|
||||
/* Per-B4 LRU list maintenance */
|
||||
clib_dlist_remove (dm->per_thread_data[thread_index].list_pool,
|
||||
s0->per_b4_index);
|
||||
clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
|
||||
s0->per_b4_list_head_index, s0->per_b4_index);
|
||||
trace0:
|
||||
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
|
||||
&& (b0->flags & VLIB_BUFFER_IS_TRACED)))
|
||||
{
|
||||
dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
|
||||
t->next_index = next0;
|
||||
t->session_index = ~0;
|
||||
if (s0)
|
||||
t->session_index =
|
||||
s0 - dm->per_thread_data[thread_index].sessions;
|
||||
}
|
||||
|
||||
b0->error = error_node->errors[error0];
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return frame->n_vectors;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
VLIB_REGISTER_NODE (dslite_out2in_node) = {
|
||||
.function = dslite_out2in_node_fn,
|
||||
.name = "dslite-out2in",
|
||||
.vector_size = sizeof (u32),
|
||||
.format_trace = format_dslite_trace,
|
||||
.type = VLIB_NODE_TYPE_INTERNAL,
|
||||
.n_errors = ARRAY_LEN (dslite_out2in_error_strings),
|
||||
.error_strings = dslite_out2in_error_strings,
|
||||
.n_next_nodes = DSLITE_OUT2IN_N_NEXT,
|
||||
/* edit / add dispositions here */
|
||||
.next_nodes = {
|
||||
[DSLITE_OUT2IN_NEXT_DROP] = "error-drop",
|
||||
[DSLITE_OUT2IN_NEXT_IP4_LOOKUP] = "ip4-lookup",
|
||||
[DSLITE_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup",
|
||||
},
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
VLIB_NODE_FUNCTION_MULTIARCH (dslite_out2in_node, dslite_out2in_node_fn);
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
@ -366,12 +366,15 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
|
||||
s->in2out.fib_index);
|
||||
|
||||
snat_free_outside_address_and_port
|
||||
(sm, thread_index, &s->out2in, s->outside_address_index);
|
||||
(sm->addresses, thread_index, &s->out2in, s->outside_address_index);
|
||||
}
|
||||
s->outside_address_index = ~0;
|
||||
|
||||
if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, thread_index,
|
||||
&key1, &address_index))
|
||||
if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
|
||||
thread_index, &key1,
|
||||
&address_index, sm->vrf_mode,
|
||||
sm->port_per_thread,
|
||||
sm->per_thread_data[thread_index].snat_thread_index))
|
||||
{
|
||||
ASSERT(0);
|
||||
|
||||
@ -389,9 +392,11 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
|
||||
{
|
||||
static_mapping = 0;
|
||||
/* Try to create dynamic translation */
|
||||
if (snat_alloc_outside_address_and_port (sm, rx_fib_index0,
|
||||
if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
|
||||
thread_index, &key1,
|
||||
&address_index))
|
||||
&address_index, sm->vrf_mode,
|
||||
sm->port_per_thread,
|
||||
sm->per_thread_data[thread_index].snat_thread_index))
|
||||
{
|
||||
b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
|
||||
return SNAT_IN2OUT_NEXT_DROP;
|
||||
@ -1265,7 +1270,8 @@ create_ses:
|
||||
s->out2in.port,
|
||||
s->in2out.fib_index);
|
||||
|
||||
snat_free_outside_address_and_port (sm, thread_index, &s->out2in,
|
||||
snat_free_outside_address_and_port (sm->addresses, thread_index,
|
||||
&s->out2in,
|
||||
s->outside_address_index);
|
||||
|
||||
/* Remove in2out, out2in keys */
|
||||
|
@ -1567,3 +1567,30 @@ define nat64_prefix_details {
|
||||
u8 prefix_len;
|
||||
u32 vrf_id;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* DS-Lite APIs
|
||||
*/
|
||||
|
||||
/** \brief Add/delete address range to DS-Lite pool
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param start_addr - start address of the range
|
||||
@param end_addr - end address of the range
|
||||
@param is_add - 1 if add, 0 if delete
|
||||
*/
|
||||
autoreply define dslite_add_del_pool_addr_range {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u8 start_addr[4];
|
||||
u8 end_addr[4];
|
||||
u8 is_add;
|
||||
};
|
||||
|
||||
autoreply define dslite_set_aftr_addr {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u8 ip4_addr[4];
|
||||
u8 ip6_addr[16];
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <nat/nat_ipfix_logging.h>
|
||||
#include <nat/nat_det.h>
|
||||
#include <nat/nat64.h>
|
||||
#include <nat/dslite.h>
|
||||
#include <vnet/fib/fib_table.h>
|
||||
#include <vnet/fib/ip4_fib.h>
|
||||
|
||||
@ -1448,12 +1449,14 @@ static clib_error_t * snat_init (vlib_main_t * vm)
|
||||
|
||||
error = nat64_init(vm);
|
||||
|
||||
dslite_init(vm);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
VLIB_INIT_FUNCTION (snat_init);
|
||||
|
||||
void snat_free_outside_address_and_port (snat_main_t * sm,
|
||||
void snat_free_outside_address_and_port (snat_address_t * addresses,
|
||||
u32 thread_index,
|
||||
snat_session_key_t * k,
|
||||
u32 address_index)
|
||||
@ -1461,9 +1464,9 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
|
||||
snat_address_t *a;
|
||||
u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
|
||||
|
||||
ASSERT (address_index < vec_len (sm->addresses));
|
||||
ASSERT (address_index < vec_len (addresses));
|
||||
|
||||
a = sm->addresses + address_index;
|
||||
a = addresses + address_index;
|
||||
|
||||
switch (k->protocol)
|
||||
{
|
||||
@ -1572,38 +1575,42 @@ int snat_static_mapping_match (snat_main_t * sm,
|
||||
}
|
||||
|
||||
static_always_inline u16
|
||||
snat_random_port (snat_main_t * sm, u16 min, u16 max)
|
||||
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);
|
||||
}
|
||||
|
||||
int snat_alloc_outside_address_and_port (snat_main_t * sm,
|
||||
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)
|
||||
u32 * address_indexp,
|
||||
u8 vrf_mode,
|
||||
u16 port_per_thread,
|
||||
u32 snat_thread_index)
|
||||
{
|
||||
int i;
|
||||
snat_address_t *a;
|
||||
u32 portnum;
|
||||
|
||||
for (i = 0; i < vec_len (sm->addresses); i++)
|
||||
for (i = 0; i < vec_len (addresses); i++)
|
||||
{
|
||||
a = sm->addresses + i;
|
||||
if (sm->vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index)
|
||||
a = addresses + i;
|
||||
if (vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index)
|
||||
continue;
|
||||
switch (k->protocol)
|
||||
{
|
||||
#define _(N, j, n, s) \
|
||||
case SNAT_PROTOCOL_##N: \
|
||||
if (a->busy_##n##_ports_per_thread[thread_index] < sm->port_per_thread) \
|
||||
if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
|
||||
{ \
|
||||
while (1) \
|
||||
{ \
|
||||
portnum = (sm->port_per_thread * \
|
||||
sm->per_thread_data[thread_index].snat_thread_index) + \
|
||||
snat_random_port(sm, 1, sm->port_per_thread) + 1024; \
|
||||
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)) \
|
||||
continue; \
|
||||
clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
|
||||
|
@ -376,16 +376,19 @@ extern vlib_node_registration_t snat_det_out2in_node;
|
||||
extern vlib_node_registration_t snat_hairpin_dst_node;
|
||||
extern vlib_node_registration_t snat_hairpin_src_node;
|
||||
|
||||
void snat_free_outside_address_and_port (snat_main_t * sm,
|
||||
void snat_free_outside_address_and_port (snat_address_t * addresses,
|
||||
u32 thread_index,
|
||||
snat_session_key_t * k,
|
||||
u32 address_index);
|
||||
|
||||
int snat_alloc_outside_address_and_port (snat_main_t * sm,
|
||||
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);
|
||||
u32 * address_indexp,
|
||||
u8 vrf_mode,
|
||||
u16 port_per_thread,
|
||||
u32 snat_thread_index);
|
||||
|
||||
int snat_static_mapping_match (snat_main_t * sm,
|
||||
snat_session_key_t match,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <nat/nat.h>
|
||||
#include <nat/nat_det.h>
|
||||
#include <nat/nat64.h>
|
||||
#include <nat/dslite.h>
|
||||
#include <vlibapi/api.h>
|
||||
#include <vlibmemory/api.h>
|
||||
|
||||
@ -3289,6 +3290,87 @@ vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp,
|
||||
FINISH;
|
||||
}
|
||||
|
||||
/***************/
|
||||
/*** DS-Lite ***/
|
||||
/***************/
|
||||
|
||||
static void
|
||||
vl_api_dslite_set_aftr_addr_t_handler (vl_api_dslite_set_aftr_addr_t * mp)
|
||||
{
|
||||
vl_api_dslite_set_aftr_addr_reply_t *rmp;
|
||||
snat_main_t *sm = &snat_main;
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
int rv = 0;
|
||||
ip6_address_t ip6_addr;
|
||||
|
||||
memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16);
|
||||
|
||||
rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr);
|
||||
|
||||
REPLY_MACRO (VL_API_DSLITE_SET_AFTR_ADDR_REPLY);
|
||||
}
|
||||
|
||||
static void *
|
||||
vl_api_dslite_set_aftr_addr_t_print (vl_api_dslite_set_aftr_addr_t * mp,
|
||||
void *handle)
|
||||
{
|
||||
u8 *s;
|
||||
|
||||
s = format (0, "SCRIPT: dslite_set_aftr_addr ");
|
||||
s = format (s, "ip6_addr %U ip4_addr %U\n",
|
||||
format_ip6_address, mp->ip6_addr,
|
||||
format_ip4_address, mp->ip4_addr);
|
||||
|
||||
FINISH;
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_dslite_add_del_pool_addr_range_t_handler
|
||||
(vl_api_dslite_add_del_pool_addr_range_t * mp)
|
||||
{
|
||||
vl_api_dslite_add_del_pool_addr_range_reply_t *rmp;
|
||||
snat_main_t *sm = &snat_main;
|
||||
dslite_main_t *dm = &dslite_main;
|
||||
int rv = 0;
|
||||
ip4_address_t this_addr;
|
||||
u32 start_host_order, end_host_order;
|
||||
int i, count;
|
||||
u32 *tmp;
|
||||
|
||||
tmp = (u32 *) mp->start_addr;
|
||||
start_host_order = clib_host_to_net_u32 (tmp[0]);
|
||||
tmp = (u32 *) mp->end_addr;
|
||||
end_host_order = clib_host_to_net_u32 (tmp[0]);
|
||||
|
||||
count = (end_host_order - start_host_order) + 1;
|
||||
memcpy (&this_addr.as_u8, mp->start_addr, 4);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if ((rv = dslite_add_del_pool_addr (dm, &this_addr, mp->is_add)))
|
||||
goto send_reply;
|
||||
|
||||
increment_v4_address (&this_addr);
|
||||
}
|
||||
|
||||
send_reply:
|
||||
REPLY_MACRO (VL_API_DSLITE_ADD_DEL_POOL_ADDR_RANGE_REPLY);
|
||||
}
|
||||
|
||||
static void *vl_api_dslite_add_del_pool_addr_range_t_print
|
||||
(vl_api_dslite_add_del_pool_addr_range_t * mp, void *handle)
|
||||
{
|
||||
u8 *s;
|
||||
|
||||
s = format (0, "SCRIPT: dslite_add_del_pool_addr_range ");
|
||||
s = format (s, "%U - %U\n",
|
||||
format_ip4_address, mp->start_addr,
|
||||
format_ip4_address, mp->end_addr);
|
||||
|
||||
FINISH;
|
||||
}
|
||||
|
||||
|
||||
/* List of message types that this plugin understands */
|
||||
#define foreach_snat_plugin_api_msg \
|
||||
_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \
|
||||
@ -3360,7 +3442,9 @@ _(NAT64_SET_TIMEOUTS, nat64_set_timeouts) \
|
||||
_(NAT64_GET_TIMEOUTS, nat64_get_timeouts) \
|
||||
_(NAT64_ST_DUMP, nat64_st_dump) \
|
||||
_(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \
|
||||
_(NAT64_PREFIX_DUMP, nat64_prefix_dump)
|
||||
_(NAT64_PREFIX_DUMP, nat64_prefix_dump) \
|
||||
_(DSLITE_ADD_DEL_POOL_ADDR_RANGE, dslite_add_del_pool_addr_range) \
|
||||
_(DSLITE_SET_AFTR_ADDR, dslite_set_aftr_addr)
|
||||
|
||||
/* Set up the API message handling tables */
|
||||
static clib_error_t *
|
||||
|
148
test/test_nat.py
148
test/test_nat.py
@ -5,6 +5,7 @@ import unittest
|
||||
import struct
|
||||
|
||||
from framework import VppTestCase, VppTestRunner, running_extended_tests
|
||||
from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
|
||||
from scapy.layers.inet import IP, TCP, UDP, ICMP
|
||||
from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
|
||||
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
|
||||
@ -4007,5 +4008,152 @@ class TestNAT64(MethodHolder):
|
||||
self.logger.info(self.vapi.cli("show nat64 session table all"))
|
||||
self.clear_nat64()
|
||||
|
||||
|
||||
class TestDSlite(MethodHolder):
|
||||
""" DS-Lite Test Cases """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestDSlite, cls).setUpClass()
|
||||
|
||||
try:
|
||||
cls.nat_addr = '10.0.0.3'
|
||||
cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr)
|
||||
|
||||
cls.create_pg_interfaces(range(2))
|
||||
cls.pg0.admin_up()
|
||||
cls.pg0.config_ip4()
|
||||
cls.pg0.resolve_arp()
|
||||
cls.pg1.admin_up()
|
||||
cls.pg1.config_ip6()
|
||||
cls.pg1.generate_remote_hosts(2)
|
||||
cls.pg1.configure_ipv6_neighbors()
|
||||
|
||||
except Exception:
|
||||
super(TestDSlite, cls).tearDownClass()
|
||||
raise
|
||||
|
||||
def test_dslite(self):
|
||||
""" Test DS-Lite """
|
||||
self.vapi.dslite_add_del_pool_addr_range(self.nat_addr_n,
|
||||
self.nat_addr_n)
|
||||
aftr_ip4 = '192.0.0.1'
|
||||
aftr_ip4_n = socket.inet_pton(socket.AF_INET, aftr_ip4)
|
||||
aftr_ip6 = '2001:db8:85a3::8a2e:370:1'
|
||||
aftr_ip6_n = socket.inet_pton(socket.AF_INET6, aftr_ip6)
|
||||
self.vapi.dslite_set_aftr_addr(aftr_ip6_n, aftr_ip4_n)
|
||||
|
||||
# UDP
|
||||
p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
|
||||
IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[0].ip6) /
|
||||
IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
|
||||
UDP(sport=20000, dport=10000))
|
||||
self.pg1.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg0.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertFalse(capture.haslayer(IPv6))
|
||||
self.assertEqual(capture[IP].src, self.nat_addr)
|
||||
self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
|
||||
self.assertNotEqual(capture[UDP].sport, 20000)
|
||||
self.assertEqual(capture[UDP].dport, 10000)
|
||||
self.check_ip_checksum(capture)
|
||||
out_port = capture[UDP].sport
|
||||
|
||||
p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
|
||||
IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
|
||||
UDP(sport=10000, dport=out_port))
|
||||
self.pg0.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg1.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertEqual(capture[IPv6].src, aftr_ip6)
|
||||
self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[0].ip6)
|
||||
self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
|
||||
self.assertEqual(capture[IP].dst, '192.168.1.1')
|
||||
self.assertEqual(capture[UDP].sport, 10000)
|
||||
self.assertEqual(capture[UDP].dport, 20000)
|
||||
self.check_ip_checksum(capture)
|
||||
|
||||
# TCP
|
||||
p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
|
||||
IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6) /
|
||||
IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
|
||||
TCP(sport=20001, dport=10001))
|
||||
self.pg1.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg0.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertFalse(capture.haslayer(IPv6))
|
||||
self.assertEqual(capture[IP].src, self.nat_addr)
|
||||
self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
|
||||
self.assertNotEqual(capture[TCP].sport, 20001)
|
||||
self.assertEqual(capture[TCP].dport, 10001)
|
||||
self.check_ip_checksum(capture)
|
||||
self.check_tcp_checksum(capture)
|
||||
out_port = capture[TCP].sport
|
||||
|
||||
p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
|
||||
IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
|
||||
TCP(sport=10001, dport=out_port))
|
||||
self.pg0.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg1.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertEqual(capture[IPv6].src, aftr_ip6)
|
||||
self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
|
||||
self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
|
||||
self.assertEqual(capture[IP].dst, '192.168.1.1')
|
||||
self.assertEqual(capture[TCP].sport, 10001)
|
||||
self.assertEqual(capture[TCP].dport, 20001)
|
||||
self.check_ip_checksum(capture)
|
||||
self.check_tcp_checksum(capture)
|
||||
|
||||
# ICMP
|
||||
p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
|
||||
IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6) /
|
||||
IP(dst=self.pg0.remote_ip4, src='192.168.1.1') /
|
||||
ICMP(id=4000, type='echo-request'))
|
||||
self.pg1.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg0.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertFalse(capture.haslayer(IPv6))
|
||||
self.assertEqual(capture[IP].src, self.nat_addr)
|
||||
self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
|
||||
self.assertNotEqual(capture[ICMP].id, 4000)
|
||||
self.check_ip_checksum(capture)
|
||||
self.check_icmp_checksum(capture)
|
||||
out_id = capture[ICMP].id
|
||||
|
||||
p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
|
||||
IP(dst=self.nat_addr, src=self.pg0.remote_ip4) /
|
||||
ICMP(id=out_id, type='echo-reply'))
|
||||
self.pg0.add_stream(p)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
capture = self.pg1.get_capture(1)
|
||||
capture = capture[0]
|
||||
self.assertEqual(capture[IPv6].src, aftr_ip6)
|
||||
self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
|
||||
self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
|
||||
self.assertEqual(capture[IP].dst, '192.168.1.1')
|
||||
self.assertEqual(capture[ICMP].id, 4000)
|
||||
self.check_ip_checksum(capture)
|
||||
self.check_icmp_checksum(capture)
|
||||
|
||||
def tearDown(self):
|
||||
super(TestDSlite, self).tearDown()
|
||||
if not self.vpp_dead:
|
||||
self.logger.info(self.vapi.cli("show dslite pool"))
|
||||
self.logger.info(
|
||||
self.vapi.cli("show dslite aftr-tunnel-endpoint-address"))
|
||||
self.logger.info(self.vapi.cli("show dslite sessions"))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(testRunner=VppTestRunner)
|
||||
|
@ -1647,6 +1647,34 @@ class VppPapiProvider(object):
|
||||
"""
|
||||
return self.api(self.papi.nat64_prefix_dump, {})
|
||||
|
||||
def dslite_set_aftr_addr(self, ip6, ip4):
|
||||
"""Set DS-Lite AFTR addresses
|
||||
|
||||
:param ip4: IPv4 address
|
||||
:param ip6: IPv6 address
|
||||
"""
|
||||
return self.api(
|
||||
self.papi.dslite_set_aftr_addr,
|
||||
{'ip4_addr': ip4,
|
||||
'ip6_addr': ip6})
|
||||
|
||||
def dslite_add_del_pool_addr_range(
|
||||
self,
|
||||
start_addr,
|
||||
end_addr,
|
||||
is_add=1):
|
||||
"""Add/del address range to DS-Lite pool
|
||||
|
||||
:param start_addr: First IP address
|
||||
:param end_addr: Last IP address
|
||||
:param is_add: 1 if add, 0 if delete (Default value = 1)
|
||||
"""
|
||||
return self.api(
|
||||
self.papi.dslite_add_del_pool_addr_range,
|
||||
{'start_addr': start_addr,
|
||||
'end_addr': end_addr,
|
||||
'is_add': is_add})
|
||||
|
||||
def control_ping(self):
|
||||
self.api(self.papi.control_ping)
|
||||
|
||||
|
Reference in New Issue
Block a user