NAT64: Hairpinning (VPP-699)

Change-Id: I83a6c277fa211ac2c2ca2d603650c992886af0a7
Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:
Matus Fabian
2017-06-15 02:28:50 -07:00
committed by Ole Trøan
parent acd4c63e3c
commit 029f3d2c1c
8 changed files with 749 additions and 67 deletions

View File

@ -95,7 +95,10 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add)
vec_add2 (nm->addr_pool, a, 1);
a->addr = *addr;
a->fib_index = ip4_fib_index_from_table_id (vrf_id);
a->fib_index = 0;
if (vrf_id != ~0)
a->fib_index =
fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id);
#define _(N, i, n, s) \
clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535);
foreach_snat_protocol
@ -218,7 +221,7 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
nat64_main_t *nm = &nat64_main;
snat_main_t *sm = &snat_main;
int i;
snat_address_t *a;
snat_address_t *a, *ga = 0;
u32 portnum;
for (i = 0; i < vec_len (nm->addr_pool); i++)
@ -230,22 +233,27 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
case SNAT_PROTOCOL_##N: \
if (a->busy_##n##_ports < (65535-1024)) \
{ \
while (1) \
if (a->fib_index == fib_index) \
{ \
portnum = random_u32 (&sm->random_seed); \
portnum &= 0xFFFF; \
if (portnum < 1024) \
continue; \
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); \
a->busy_##n##_ports++; \
*port = portnum; \
addr->as_u32 = a->addr.as_u32; \
return 0; \
} \
while (1) \
{ \
portnum = random_u32 (&sm->random_seed); \
portnum &= 0xFFFF; \
if (portnum < 1024) \
continue; \
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); \
a->busy_##n##_ports++; \
*port = portnum; \
addr->as_u32 = a->addr.as_u32; \
return 0; \
} \
} \
else if (a->fib_index == 0) \
ga = a; \
} \
break;
foreach_snat_protocol
@ -254,8 +262,39 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
clib_warning ("unknown protocol");
return 1;
}
}
if (ga)
{
switch (proto)
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
while (1) \
{ \
portnum = random_u32 (&sm->random_seed); \
portnum &= 0xFFFF; \
if (portnum < 1024) \
continue; \
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); \
a->busy_##n##_ports++; \
*port = portnum; \
addr->as_u32 = a->addr.as_u32; \
return 0; \
}
break;
foreach_snat_protocol
#undef _
default:
clib_warning ("unknown protocol");
return 1;
}
}
/* Totally out of translations to use... */
//TODO: IPFix
return 1;

View File

@ -107,7 +107,7 @@ nat64_cli_pool_walk (snat_address_t * ap, void *ctx)
if (ap->fib_index != ~0)
{
fib_table_t *fib;
fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4);
fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP6);
if (!fib)
return -1;
vlib_cli_output (vm, " %U tenant VRF: %u", format_ip4_address,

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,9 @@ static u8 well_known_prefix[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
};
/* *INDENT-ON* */
typedef struct
{
u32 sw_if_index;
@ -95,10 +96,13 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
ip46_address_t saddr, daddr;
ip6_address_t ip6_saddr;
udp_header_t *udp = ip4_next_header (ip4);
tcp_header_t *tcp = ip4_next_header (ip4);
snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
u16 dport = udp->dst_port;
u16 sport = udp->src_port;
u32 sw_if_index, fib_index;
u16 *checksum;
ip_csum_t csum;
sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
@ -142,6 +146,16 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
udp->dst_port = bibe->in_port;
if (proto == SNAT_PROTOCOL_UDP)
checksum = &udp->checksum;
else
checksum = &tcp->checksum;
csum = ip_csum_sub_even (*checksum, dport);
csum = ip_csum_add_even (csum, udp->dst_port);
*checksum = ip_csum_fold (csum);
vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
return 0;
}
@ -205,6 +219,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
((u16 *) (icmp))[2] = bibe->in_port;
vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
}
else
{
@ -269,12 +284,17 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
((u16 *) (icmp))[2] = bibe->in_port;
vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
}
else
{
udp_header_t *udp = ip4_next_header (ip4);
tcp_header_t *tcp = ip4_next_header (ip4);
u16 dport = udp->dst_port;
u16 sport = udp->src_port;
u16 *checksum;
ip_csum_t csum;
ste =
nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
@ -291,6 +311,19 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
udp->src_port = bibe->in_port;
if (proto == SNAT_PROTOCOL_UDP)
checksum = &udp->checksum;
else
checksum = &tcp->checksum;
if (*checksum)
{
csum = ip_csum_sub_even (*checksum, sport);
csum = ip_csum_add_even (csum, udp->src_port);
*checksum = ip_csum_fold (csum);
}
vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
}
return 0;

View File

@ -1245,7 +1245,7 @@ static void *vl_api_nat64_add_del_pool_addr_range_t_print
{
u8 *s;
s = format (0, "SCRIPT: nat64_add_del_pool_addr_range");
s = format (0, "SCRIPT: nat64_add_del_pool_addr_range ");
s = format (s, "%U - %U vrf_id %u %s\n",
format_ip4_address, mp->start_addr,
format_ip4_address, mp->end_addr,
@ -1273,7 +1273,7 @@ nat64_api_pool_walk (snat_address_t * a, void *arg)
clib_memcpy (rmp->address, &(a->addr), 4);
if (a->fib_index != ~0)
{
fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4);
fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP6);
if (!fib)
return -1;
rmp->vrf_id = ntohl (fib->ft_table_id);

View File

@ -321,17 +321,9 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
//We have an ICMP inside an ICMP
//It needs to be translated, but not for error ICMP messages
icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
csum = inner_icmp->checksum;
//Only types ICMP4_echo_request and ICMP4_echo_reply are handled by icmp_to_icmp6_header
csum = ip_csum_sub_even (csum, *((u16 *) inner_icmp));
inner_icmp->type = (inner_icmp->type == ICMP4_echo_request) ?
ICMP6_echo_request : ICMP6_echo_reply;
csum = ip_csum_add_even (csum, *((u16 *) inner_icmp));
csum =
ip_csum_add_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6));
csum =
ip_csum_add_even (csum, inner_ip4->length - sizeof (*inner_ip4));
inner_icmp->checksum = ip_csum_fold (csum);
inner_L4_checksum = &inner_icmp->checksum;
inner_ip4->protocol = IP_PROTOCOL_ICMP6;
}
@ -341,8 +333,6 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
os_panic ();
}
csum = *inner_L4_checksum; //Initial checksum of the inner L4 header
inner_ip6->ip_version_traffic_class_and_flow_label =
clib_host_to_net_u32 ((6 << 28) + (inner_ip4->tos << 20));
inner_ip6->payload_length =
@ -367,14 +357,42 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
sizeof (*inner_frag));
}
/* UDP checksum is optional */
if (csum)
csum = *inner_L4_checksum;
if (inner_ip6->protocol == IP_PROTOCOL_ICMP6)
{
csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
*inner_L4_checksum = ip_csum_fold (csum);
//Recompute ICMP checksum
icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
inner_icmp->checksum = 0;
csum = ip_csum_with_carry (0, inner_ip6->payload_length);
csum =
ip_csum_with_carry (csum,
clib_host_to_net_u16 (inner_ip6->protocol));
csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[0]);
csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[1]);
csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[0]);
csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[1]);
csum =
ip_incremental_checksum (csum, inner_icmp,
clib_net_to_host_u16
(inner_ip6->payload_length));
inner_icmp->checksum = ~ip_csum_fold (csum);
}
else
{
/* UDP checksum is optional */
if (csum)
{
csum =
ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
csum =
ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
csum =
ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
csum =
ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
*inner_L4_checksum = ip_csum_fold (csum);
}
}
}
else
@ -518,6 +536,7 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
csum = ip_csum_sub_even (*checksum, ip4->src_address.as_u32);
csum = ip_csum_sub_even (csum, ip4->dst_address.as_u32);
*checksum = ip_csum_fold (csum);
// Deal with fragmented packets
if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
@ -558,7 +577,7 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
if ((rv = fn (ip4, ip6, ctx)) != 0)
return rv;
csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
csum = ip_csum_add_even (*checksum, ip6->src_address.as_u64[0]);
csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);

View File

@ -314,13 +314,9 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
else if (inner_protocol == IP_PROTOCOL_ICMP6)
{
icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
csum = inner_icmp->checksum;
csum = ip_csum_sub_even (csum, *((u16 *) inner_icmp));
//It cannot be of a different type as ip6_icmp_to_icmp6_in_place succeeded
inner_icmp->type = (inner_icmp->type == ICMP6_echo_request) ?
ICMP4_echo_request : ICMP4_echo_reply;
csum = ip_csum_add_even (csum, *((u16 *) inner_icmp));
inner_icmp->checksum = ip_csum_fold (csum);
inner_protocol = IP_PROTOCOL_ICMP; //Will be copied to ip6 later
inner_L4_checksum = &inner_icmp->checksum;
}
@ -334,6 +330,7 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]);
csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]);
csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]);
*inner_L4_checksum = ip_csum_fold (csum);
if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0)
return rv;
@ -353,19 +350,23 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
{
//Remove remainings of the pseudo-header in the csum
//Recompute ICMP checksum
icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
inner_icmp->checksum = 0;
csum =
ip_csum_sub_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6));
csum =
ip_csum_sub_even (csum, inner_ip4->length - sizeof (*inner_ip4));
ip_incremental_checksum (0, inner_icmp,
clib_net_to_host_u16 (inner_ip4->length)
- sizeof (*inner_ip4));
inner_icmp->checksum = ~ip_csum_fold (csum);
}
else
{
//Update to new pseudo-header
csum = *inner_L4_checksum;
csum = ip_csum_add_even (csum, inner_ip4->src_address.as_u32);
csum = ip_csum_add_even (csum, inner_ip4->dst_address.as_u32);
*inner_L4_checksum = ip_csum_fold (csum);
}
*inner_L4_checksum = ip_csum_fold (csum);
//Move up icmp header
ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8);
@ -512,6 +513,7 @@ ip6_to_ip4_tcp_udp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]);
csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]);
csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]);
*checksum = ip_csum_fold (csum);
no_csum:
ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4));
@ -552,7 +554,7 @@ no_csum:
}
else
{
csum = ip_csum_add_even (csum, ip4->dst_address.as_u32);
csum = ip_csum_add_even (*checksum, ip4->dst_address.as_u32);
csum = ip_csum_add_even (csum, ip4->src_address.as_u32);
*checksum = ip_csum_fold (csum);
}

File diff suppressed because it is too large Load Diff