From a774b53623f60b5e8ea8ed634d6a41e847743715 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 2 May 2017 03:15:22 -0700 Subject: NAT64: Move IPv6-IPv4 virtual reassembly code from MAP-T to common library (VPP-708) Change-Id: I9ad636f80bf109ffac9ca1b6d80d5f2c31f2076a Signed-off-by: Matus Fabian --- src/vnet/ip/ip4_to_ip6.h | 577 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 src/vnet/ip/ip4_to_ip6.h (limited to 'src/vnet/ip/ip4_to_ip6.h') diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h new file mode 100644 index 00000000..96b8bf1e --- /dev/null +++ b/src/vnet/ip/ip4_to_ip6.h @@ -0,0 +1,577 @@ +/* + * 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. + */ +/** + * @file + * @brief IPv4 to IPv6 translation + */ +#ifndef __included_ip4_to_ip6_h__ +#define __included_ip4_to_ip6_h__ + +#include + + +/** + * IPv4 to IPv6 set call back function type + */ +typedef int (*ip4_to_ip6_set_fn_t) (ip4_header_t * ip4, ip6_header_t * ip6, + void *ctx); + +/* *INDENT-OFF* */ +static u8 icmp_to_icmp6_updater_pointer_table[] = + { 0, 1, 4, 4, ~0, + ~0, ~0, ~0, 7, 6, + ~0, ~0, 8, 8, 8, + 8, 24, 24, 24, 24 + }; +/* *INDENT-ON* */ + +#define frag_id_4to6(id) (id) + +/** + * @brief Get TCP/UDP port number or ICMP id from IPv4 packet. + * + * @param ip4 IPv4 header. + * @param sender 1 get sender port, 0 get receiver port. + * + * @returns Port number on success, 0 otherwise. + */ +always_inline u16 +ip4_get_port (ip4_header_t * ip, u8 sender) +{ + if (ip->ip_version_and_header_length != 0x45 || + ip4_get_fragment_offset (ip)) + return 0; + + if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) || + (ip->protocol == IP_PROTOCOL_UDP))) + { + udp_header_t *udp = (void *) (ip + 1); + return (sender) ? udp->src_port : udp->dst_port; + } + else if (ip->protocol == IP_PROTOCOL_ICMP) + { + icmp46_header_t *icmp = (void *) (ip + 1); + if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) + { + return *((u16 *) (icmp + 1)); + } + else if (clib_net_to_host_u16 (ip->length) >= 64) + { + ip = (ip4_header_t *) (icmp + 2); + if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) || + (ip->protocol == IP_PROTOCOL_UDP))) + { + udp_header_t *udp = (void *) (ip + 1); + return (sender) ? udp->dst_port : udp->src_port; + } + else if (ip->protocol == IP_PROTOCOL_ICMP) + { + icmp46_header_t *icmp = (void *) (ip + 1); + if (icmp->type == ICMP4_echo_request || + icmp->type == ICMP4_echo_reply) + { + return *((u16 *) (icmp + 1)); + } + } + } + } + return 0; +} + +/** + * @brief Convert type and code value from ICMP4 to ICMP6. + * + * @param icmp ICMP header. + * @param inner_ip4 Inner IPv4 header if present, 0 otherwise. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +icmp_to_icmp6_header (icmp46_header_t * icmp, ip4_header_t ** inner_ip4) +{ + *inner_ip4 = NULL; + switch (icmp->type) + { + case ICMP4_echo_reply: + icmp->type = ICMP6_echo_reply; + break; + case ICMP4_echo_request: + icmp->type = ICMP6_echo_request; + break; + case ICMP4_destination_unreachable: + *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8); + + switch (icmp->code) + { + case ICMP4_destination_unreachable_destination_unreachable_net: //0 + case ICMP4_destination_unreachable_destination_unreachable_host: //1 + icmp->type = ICMP6_destination_unreachable; + icmp->code = ICMP6_destination_unreachable_no_route_to_destination; + break; + case ICMP4_destination_unreachable_protocol_unreachable: //2 + icmp->type = ICMP6_parameter_problem; + icmp->code = ICMP6_parameter_problem_unrecognized_next_header; + break; + case ICMP4_destination_unreachable_port_unreachable: //3 + icmp->type = ICMP6_destination_unreachable; + icmp->code = ICMP6_destination_unreachable_port_unreachable; + break; + case ICMP4_destination_unreachable_fragmentation_needed_and_dont_fragment_set: //4 + icmp->type = + ICMP6_packet_too_big; + icmp->code = 0; + { + u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1))); + if (advertised_mtu) + advertised_mtu += 20; + else + advertised_mtu = 1000; //FIXME ! (RFC 1191 - plateau value) + + //FIXME: = minimum(advertised MTU+20, MTU_of_IPv6_nexthop, (MTU_of_IPv4_nexthop)+20) + *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (advertised_mtu); + } + break; + + case ICMP4_destination_unreachable_source_route_failed: //5 + case ICMP4_destination_unreachable_destination_network_unknown: //6 + case ICMP4_destination_unreachable_destination_host_unknown: //7 + case ICMP4_destination_unreachable_source_host_isolated: //8 + case ICMP4_destination_unreachable_network_unreachable_for_type_of_service: //11 + case ICMP4_destination_unreachable_host_unreachable_for_type_of_service: //12 + icmp->type = + ICMP6_destination_unreachable; + icmp->code = ICMP6_destination_unreachable_no_route_to_destination; + break; + case ICMP4_destination_unreachable_network_administratively_prohibited: //9 + case ICMP4_destination_unreachable_host_administratively_prohibited: //10 + case ICMP4_destination_unreachable_communication_administratively_prohibited: //13 + case ICMP4_destination_unreachable_precedence_cutoff_in_effect: //15 + icmp->type = ICMP6_destination_unreachable; + icmp->code = + ICMP6_destination_unreachable_destination_administratively_prohibited; + break; + case ICMP4_destination_unreachable_host_precedence_violation: //14 + default: + return -1; + } + break; + + case ICMP4_time_exceeded: //11 + *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8); + icmp->type = ICMP6_time_exceeded; + break; + + case ICMP4_parameter_problem: + *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8); + + switch (icmp->code) + { + case ICMP4_parameter_problem_pointer_indicates_error: + case ICMP4_parameter_problem_bad_length: + icmp->type = ICMP6_parameter_problem; + icmp->code = ICMP6_parameter_problem_erroneous_header_field; + { + u8 ptr = + icmp_to_icmp6_updater_pointer_table[*((u8 *) (icmp + 1))]; + if (ptr == 0xff) + return -1; + + *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (ptr); + } + break; + default: + //All other codes cause error + return -1; + } + break; + + default: + //All other types cause error + return -1; + break; + } + return 0; +} + +/** + * @brief Translate ICMP4 packet to ICMP6. + * + * @param p Buffer to translate. + * @param fn The function to translate outer header. + * @param ctx A context passed in the outer header translate function. + * @param inner_fn The function to translate inner header. + * @param inner_ctx A context passed in the inner header translate function. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, + ip4_to_ip6_set_fn_t inner_fn, void *inner_ctx) +{ + ip4_header_t *ip4, *inner_ip4; + ip6_header_t *ip6, *inner_ip6; + u32 ip_len; + icmp46_header_t *icmp; + ip_csum_t csum; + ip6_frag_hdr_t *inner_frag; + u32 inner_frag_id; + u32 inner_frag_offset; + u8 inner_frag_more; + u16 *inner_L4_checksum = 0; + int rv; + + ip4 = vlib_buffer_get_current (p); + ip_len = clib_net_to_host_u16 (ip4->length); + ASSERT (ip_len <= p->current_length); + + icmp = (icmp46_header_t *) (ip4 + 1); + if (icmp_to_icmp6_header (icmp, &inner_ip4)) + return -1; + + if (inner_ip4) + { + //We have 2 headers to translate. + //We need to make some room in the middle of the packet + if (PREDICT_FALSE (ip4_is_fragment (inner_ip4))) + { + //Here it starts getting really tricky + //We will add a fragmentation header in the inner packet + + if (!ip4_is_first_fragment (inner_ip4)) + { + //For now we do not handle unless it is the first fragment + //Ideally we should handle the case as we are in slow path already + return -1; + } + + vlib_buffer_advance (p, + -2 * (sizeof (*ip6) - sizeof (*ip4)) - + sizeof (*inner_frag)); + ip6 = vlib_buffer_get_current (p); + clib_memcpy (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4, + 20 + 8); + ip4 = + (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)); + icmp = (icmp46_header_t *) (ip4 + 1); + + inner_ip6 = + (ip6_header_t *) u8_ptr_add (inner_ip4, + sizeof (*ip4) - sizeof (*ip6) - + sizeof (*inner_frag)); + inner_frag = + (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, sizeof (*inner_ip6)); + ip6->payload_length = + u16_net_add (ip4->length, + sizeof (*ip6) - 2 * sizeof (*ip4) + + sizeof (*inner_frag)); + inner_frag_id = frag_id_4to6 (inner_ip4->fragment_id); + inner_frag_offset = ip4_get_fragment_offset (inner_ip4); + inner_frag_more = + ! !(inner_ip4->flags_and_fragment_offset & + clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)); + } + else + { + vlib_buffer_advance (p, -2 * (sizeof (*ip6) - sizeof (*ip4))); + ip6 = vlib_buffer_get_current (p); + clib_memcpy (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4, + 20 + 8); + ip4 = + (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)); + icmp = (icmp46_header_t *) u8_ptr_add (ip4, sizeof (*ip4)); + inner_ip6 = + (ip6_header_t *) u8_ptr_add (inner_ip4, + sizeof (*ip4) - sizeof (*ip6)); + ip6->payload_length = + u16_net_add (ip4->length, sizeof (*ip6) - 2 * sizeof (*ip4)); + inner_frag = NULL; + } + + if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_TCP)) + { + inner_L4_checksum = &((tcp_header_t *) (inner_ip4 + 1))->checksum; + *inner_L4_checksum = + ip_csum_fold (ip_csum_sub_even + (*inner_L4_checksum, + *((u64 *) (&inner_ip4->src_address)))); + } + else if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_UDP)) + { + inner_L4_checksum = &((udp_header_t *) (inner_ip4 + 1))->checksum; + if (!*inner_L4_checksum) + { + return -1; + } + *inner_L4_checksum = + ip_csum_fold (ip_csum_sub_even + (*inner_L4_checksum, + *((u64 *) (&inner_ip4->src_address)))); + } + else if (inner_ip4->protocol == IP_PROTOCOL_ICMP) + { + //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; + } + else + { + /* To shut up Coverity */ + 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 = + u16_net_add (inner_ip4->length, -sizeof (*inner_ip4)); + inner_ip6->hop_limit = inner_ip4->ttl; + inner_ip6->protocol = inner_ip4->protocol; + + if ((rv = inner_fn (inner_ip4, inner_ip6, inner_ctx)) != 0) + return rv; + + if (PREDICT_FALSE (inner_frag != NULL)) + { + inner_frag->next_hdr = inner_ip6->protocol; + inner_frag->identification = inner_frag_id; + inner_frag->rsv = 0; + inner_frag->fragment_offset_and_more = + ip6_frag_hdr_offset_and_more (inner_frag_offset, inner_frag_more); + inner_ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION; + inner_ip6->payload_length = + clib_host_to_net_u16 (clib_net_to_host_u16 + (inner_ip6->payload_length) + + sizeof (*inner_frag)); + } + + 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 + { + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6)); + ip6 = vlib_buffer_get_current (p); + ip6->payload_length = + clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) - + sizeof (*ip4)); + } + + //Translate outer IPv6 + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20)); + + ip6->hop_limit = ip4->ttl; + ip6->protocol = IP_PROTOCOL_ICMP6; + + if ((rv = fn (ip4, ip6, ctx)) != 0) + return rv; + + //Truncate when the packet exceeds the minimal IPv6 MTU + if (p->current_length > 1280) + { + ip6->payload_length = clib_host_to_net_u16 (1280 - sizeof (*ip6)); + p->current_length = 1280; //Looks too simple to be correct... + } + + //Recompute ICMP checksum + icmp->checksum = 0; + csum = ip_csum_with_carry (0, ip6->payload_length); + csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol)); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]); + csum = + ip_incremental_checksum (csum, icmp, + clib_net_to_host_u16 (ip6->payload_length)); + icmp->checksum = ~ip_csum_fold (csum); + + return 0; +} + +/** + * @brief Translate IPv4 fragmented packet to IPv6. + * + * @param p Buffer to translate. + * @param fn The function to translate header. + * @param ctx A context passed in the header translate function. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +ip4_to_ip6_fragmented (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) +{ + ip4_header_t *ip4; + ip6_header_t *ip6; + ip6_frag_hdr_t *frag; + int rv; + + ip4 = vlib_buffer_get_current (p); + frag = (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag)); + ip6 = + (ip6_header_t *) u8_ptr_add (ip4, + sizeof (*ip4) - sizeof (*frag) - + sizeof (*ip6)); + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag)); + + //We know that the protocol was one of ICMP, TCP or UDP + //because the first fragment was found and cached + frag->next_hdr = + (ip4->protocol == IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip4->protocol; + frag->identification = frag_id_4to6 (ip4->fragment_id); + frag->rsv = 0; + frag->fragment_offset_and_more = + ip6_frag_hdr_offset_and_more (ip4_get_fragment_offset (ip4), + clib_net_to_host_u16 + (ip4->flags_and_fragment_offset) & + IP4_HEADER_FLAG_MORE_FRAGMENTS); + + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20)); + ip6->payload_length = + clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) - + sizeof (*ip4) + sizeof (*frag)); + ip6->hop_limit = ip4->ttl; + ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION; + + if ((rv = fn (ip4, ip6, ctx)) != 0) + return rv; + + return 0; +} + +/** + * @brief Translate IPv4 UDP/TCP packet to IPv6. + * + * @param p Buffer to translate. + * @param fn The function to translate header. + * @param ctx A context passed in the header translate function. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) +{ + ip4_header_t *ip4; + ip6_header_t *ip6; + ip_csum_t csum; + u16 *checksum; + ip6_frag_hdr_t *frag; + u32 frag_id; + int rv; + + ip4 = vlib_buffer_get_current (p); + + if (ip4->protocol == IP_PROTOCOL_UDP) + { + udp_header_t *udp = ip4_next_header (ip4); + checksum = &udp->checksum; + + //UDP checksum is optional over IPv4 but mandatory for IPv6 + //We do not check udp->length sanity but use our safe computed value instead + if (PREDICT_FALSE (!checksum)) + { + u16 udp_len = clib_host_to_net_u16 (ip4->length) - sizeof (*ip4); + csum = ip_incremental_checksum (0, udp, udp_len); + csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (udp_len)); + csum = + ip_csum_with_carry (csum, clib_host_to_net_u16 (IP_PROTOCOL_UDP)); + csum = ip_csum_with_carry (csum, *((u64 *) (&ip4->src_address))); + *checksum = ~ip_csum_fold (csum); + } + } + else + { + tcp_header_t *tcp = ip4_next_header (ip4); + checksum = &tcp->checksum; + } + + csum = ip_csum_sub_even (*checksum, ip4->src_address.as_u32); + csum = ip_csum_sub_even (csum, ip4->dst_address.as_u32); + + // Deal with fragmented packets + if (PREDICT_FALSE (ip4->flags_and_fragment_offset & + clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS))) + { + ip6 = + (ip6_header_t *) u8_ptr_add (ip4, + sizeof (*ip4) - sizeof (*ip6) - + sizeof (*frag)); + frag = + (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag)); + frag_id = frag_id_4to6 (ip4->fragment_id); + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag)); + } + else + { + ip6 = (ip6_header_t *) (((u8 *) ip4) + sizeof (*ip4) - sizeof (*ip6)); + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6)); + frag = NULL; + } + + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20)); + ip6->payload_length = u16_net_add (ip4->length, -sizeof (*ip4)); + ip6->hop_limit = ip4->ttl; + ip6->protocol = ip4->protocol; + + if (PREDICT_FALSE (frag != NULL)) + { + frag->next_hdr = ip6->protocol; + frag->identification = frag_id; + frag->rsv = 0; + frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1); + ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION; + ip6->payload_length = u16_net_add (ip6->payload_length, sizeof (*frag)); + } + + 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 (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]); + *checksum = ip_csum_fold (csum); + + return 0; +} + +#endif /* __included_ip4_to_ip6_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg From 732036d677b84aa8eaea45f8059783e827622b77 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 8 Jun 2017 05:24:28 -0700 Subject: NAT64: ICMP error support Added ICMP error messages translation. Added check for multi thread (not supported yet, so init failed). Added API definition for custom NAT64 refix. Change-Id: Ice2f04631af63e594aecc09087a1cf59f3b676fb Signed-off-by: Matus Fabian --- src/plugins/snat/nat64.c | 13 +++++- src/plugins/snat/nat64_in2out.c | 75 +++++++++++++++++++++++++++++++--- src/plugins/snat/nat64_out2in.c | 84 +++++++++++++++++++++++++++++++++++--- src/plugins/snat/snat.api | 39 ++++++++++++++++++ src/plugins/snat/snat.c | 6 ++- src/vnet/ip/ip4_to_ip6.h | 13 +++--- test/test_snat.py | 90 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 297 insertions(+), 23 deletions(-) (limited to 'src/vnet/ip/ip4_to_ip6.h') diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c index 9b6b3c8a..ef745473 100644 --- a/src/plugins/snat/nat64.c +++ b/src/plugins/snat/nat64.c @@ -45,9 +45,19 @@ nat64_init (vlib_main_t * vm) { nat64_main_t *nm = &nat64_main; clib_error_t *error = 0; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + if (tm->n_vlib_mains > 1) + { + error = clib_error_return (0, "multi thread not supported"); + goto error; + } if (nat64_db_init (&nm->db)) - error = clib_error_return (0, "NAT64 DB init failed"); + { + error = clib_error_return (0, "NAT64 DB init failed"); + goto error; + } /* set session timeouts to default values */ nm->udp_timeout = SNAT_UDP_TIMEOUT; @@ -56,6 +66,7 @@ nat64_init (vlib_main_t * vm) nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; +error: return error; } diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c index d9d94a97..eba69326 100644 --- a/src/plugins/snat/nat64_in2out.c +++ b/src/plugins/snat/nat64_in2out.c @@ -221,9 +221,11 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) } else { - //TODO: ICMP error - clib_warning ("not ICMP echo request/reply, %u", icmp->type); - return -1; + if (!vec_len (nm->addr_pool)) + return -1; + + ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32; + ip4->dst_address.as_u32 = daddr.ip4.as_u32; } return 0; @@ -231,10 +233,71 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) static int nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, - void *ctx) + void *arg) { - //TODO: - return -1; + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_st_entry_t *ste; + nat64_db_bib_entry_t *bibe; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (proto == SNAT_PROTOCOL_ICMP) + { + icmp46_header_t *icmp = ip6_next_header (ip6); + u16 in_id = ((u16 *) (icmp))[2]; + + if (! + (icmp->type == ICMP4_echo_request + || icmp->type == ICMP4_echo_reply)) + return -1; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto, + fib_index, 1); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip4->dst_address.as_u32 = bibe->out_addr.as_u32; + ((u16 *) (icmp))[2] = bibe->out_port; + ip4->src_address.as_u32 = saddr.ip4.as_u32; + } + else + { + udp_header_t *udp = ip6_next_header (ip6); + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 1); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip4->dst_address.as_u32 = bibe->out_addr.as_u32; + udp->dst_port = bibe->out_port; + ip4->src_address.as_u32 = saddr.ip4.as_u32; + } + + return 0; } static uword diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c index 3eed9974..c11bbb5a 100644 --- a/src/plugins/snat/nat64_out2in.c +++ b/src/plugins/snat/nat64_out2in.c @@ -208,9 +208,12 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) } else { - //TODO: ICMP error - clib_warning ("not ICMP echo request/reply, %u", icmp->type); - return -1; + ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); + + ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0]; + ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1]; + ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; + ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; } return 0; @@ -218,10 +221,79 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) static int nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, - void *ctx) + void *arg) { - //TODO: - return -1; + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + ip6_address_t ip6_daddr; + u32 sw_if_index, fib_index; + snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol); + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + memcpy (&ip6_daddr, well_known_prefix, sizeof (ip6_daddr)); + ip6_daddr.as_u32[3] = ip4->dst_address.as_u32; + + if (proto == SNAT_PROTOCOL_ICMP) + { + icmp46_header_t *icmp = ip4_next_header (ip4); + u16 out_id = ((u16 *) (icmp))[2]; + + if (! + (icmp->type == ICMP6_echo_request + || icmp->type == ICMP6_echo_reply)) + return -1; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, out_id, 0, proto, + fib_index, 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0]; + ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1]; + 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; + } + else + { + udp_header_t *udp = ip4_next_header (ip4); + u16 dport = udp->dst_port; + u16 sport = udp->src_port; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0]; + ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1]; + 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; + } + + return 0; } static uword diff --git a/src/plugins/snat/snat.api b/src/plugins/snat/snat.api index e6289af2..62c96058 100644 --- a/src/plugins/snat/snat.api +++ b/src/plugins/snat/snat.api @@ -812,3 +812,42 @@ define nat64_st_details { u32 vrf_id; u8 proto; }; + +/** \brief Add/del NAT64 prefix + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param prefix - NAT64 prefix + @param prefix - NAT64 prefix length + @param vrf_id - VRF id of tenant + @param is_add - 1 if add, 0 if delete +*/ +autoreply define nat64_add_del_prefix { + u32 client_index; + u32 context; + u8 prefix[16]; + u8 prefix_len; + u32 vrf_id; + u8 is_add; +}; + +/** \brief Dump NAT64 prefix + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat64_prefix_dump { + u32 client_index; + u32 context; +}; + +/** \brief Dump NAT64 prefix details response + @param context - sender context, to match reply w/ request + @param prefix - NAT64 prefix + @param prefix - NAT64 prefix length + @param vrf_id - VRF id of tenant +*/ +define nat64_prefix_details { + u32 context; + u8 prefix[16]; + u8 prefix_len; + u32 vrf_id; +}; diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c index e95abf27..351f8dc7 100644 --- a/src/plugins/snat/snat.c +++ b/src/plugins/snat/snat.c @@ -725,7 +725,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im, static clib_error_t * snat_init (vlib_main_t * vm) { snat_main_t * sm = &snat_main; - clib_error_t * error = 0; + clib_error_t * error = 0, * error_nat64 = 0; ip4_main_t * im = &ip4_main; ip_lookup_main_t * lm = &im->lookup_main; uword *p; @@ -782,7 +782,9 @@ static clib_error_t * snat_init (vlib_main_t * vm) /* Init IPFIX logging */ snat_ipfix_logging_init(vm); - error = nat64_init(vm); + error_nat64 = nat64_init(vm); + if (error_nat64) + clib_warning("NAT64 init failed: %U", format_clib_error, error_nat64); return error; } diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h index 96b8bf1e..965d27c3 100644 --- a/src/vnet/ip/ip4_to_ip6.h +++ b/src/vnet/ip/ip4_to_ip6.h @@ -310,14 +310,11 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, else if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_UDP)) { inner_L4_checksum = &((udp_header_t *) (inner_ip4 + 1))->checksum; - if (!*inner_L4_checksum) - { - return -1; - } - *inner_L4_checksum = - ip_csum_fold (ip_csum_sub_even - (*inner_L4_checksum, - *((u64 *) (&inner_ip4->src_address)))); + if (*inner_L4_checksum) + *inner_L4_checksum = + ip_csum_fold (ip_csum_sub_even + (*inner_L4_checksum, + *((u64 *) (&inner_ip4->src_address)))); } else if (inner_ip4->protocol == IP_PROTOCOL_ICMP) { diff --git a/test/test_snat.py b/test/test_snat.py index 64fa3057..c6344a9e 100644 --- a/test/test_snat.py +++ b/test/test_snat.py @@ -8,6 +8,7 @@ from framework import VppTestCase, VppTestRunner, running_extended_tests 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 +from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6 from scapy.layers.l2 import Ether, ARP from scapy.data import IP_PROTOS from scapy.packet import bind_layers @@ -2635,6 +2636,95 @@ class TestNAT64(MethodHolder): ses_num_after_timeout = self.nat64_get_ses_num() self.assertNotEqual(ses_num_before_timeout, ses_num_after_timeout) + def test_icmp_error(self): + """ NAT64 ICMP Error message translation """ + self.tcp_port_in = 6303 + self.udp_port_in = 6304 + self.icmp_id_in = 6305 + + ses_num_start = self.nat64_get_ses_num() + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + # send some packets to create sessions + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture_ip4 = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture_ip4, packet_num=3, + nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture_ip6 = self.pg0.get_capture(len(pkts)) + ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) + self.verify_capture_in_ip6(capture_ip6, ip[IPv6].src, + self.pg0.remote_ip6) + + # in2out + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=ip[IPv6].src) / + ICMPv6DestUnreach(code=1) / + packet[IPv6] for packet in capture_ip6] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet[ICMP].type, 3) + self.assertEqual(packet[ICMP].code, 13) + inner = packet[IPerror] + self.assertEqual(inner.src, self.pg1.remote_ip4) + self.assertEqual(inner.dst, self.nat_addr) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].dport, self.tcp_port_out) + elif inner.haslayer(UDPerror): + self.assertEqual(inner[UDPerror].dport, self.udp_port_out) + else: + self.assertEqual(inner[ICMPerror].id, self.icmp_id_out) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=3, code=13) / + packet[IP] for packet in capture_ip4] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, ip.src) + self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) + icmp = packet[ICMPv6DestUnreach] + self.assertEqual(icmp.code, 1) + inner = icmp[IPerror6] + self.assertEqual(inner.src, self.pg0.remote_ip6) + self.assertEqual(inner.dst, ip.src) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].sport, self.tcp_port_in) + elif inner.haslayer(UDPerror): + self.assertEqual(inner[UDPerror].sport, self.udp_port_in) + else: + self.assertEqual(inner[ICMPv6EchoRequest].id, + self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + def nat64_get_ses_num(self): """ Return number of active NAT64 sessions. -- cgit 1.2.3-korg From 89223f462337b8d9d190ab36f8651e40597463ba Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Mon, 12 Jun 2017 02:29:39 -0700 Subject: NAT64: bug fix ICMP to ICMPv6 error message inner UDP packet translation delete ST entries when deleting static BIB entry Change-Id: I2a28631ac040e20827a692331506cd8254f70916 Signed-off-by: Matus Fabian --- src/plugins/snat/nat64_db.c | 21 ++++++++++++++++++++- src/vnet/ip/ip4_to_ip6.h | 14 +++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'src/vnet/ip/ip4_to_ip6.h') diff --git a/src/plugins/snat/nat64_db.c b/src/plugins/snat/nat64_db.c index d9989c96..d15761d2 100644 --- a/src/plugins/snat/nat64_db.c +++ b/src/plugins/snat/nat64_db.c @@ -107,6 +107,8 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) nat64_db_bib_entry_key_t bibe_key; clib_bihash_kv_24_8_t kv; nat64_db_bib_entry_t *bib; + u32 *ste_to_be_free = 0, *ste_index, bibe_index; + nat64_db_st_entry_t *st, *ste; switch (bibe->proto) { @@ -114,6 +116,7 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) #define _(N, i, n, s) \ case SNAT_PROTOCOL_##N: \ bib = db->bib._##n##_bib; \ + st = db->st._##n##_st; \ break; foreach_snat_protocol #undef _ @@ -123,6 +126,21 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) return; } + bibe_index = bibe - bib; + + /* delete ST entries for static BIB entry */ + if (bibe->is_static) + { + pool_foreach (ste, st, ( + { + if (ste->bibe_index == bibe_index) + vec_add1 (ste_to_be_free, ste - st);} + )); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index (st, ste_index[0])); + vec_free (ste_to_be_free); + } + /* delete hash lookup */ bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0]; bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1]; @@ -146,6 +164,7 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) /* delete from pool */ pool_put (bib, bibe); + } nat64_db_bib_entry_t * @@ -480,7 +499,7 @@ nad64_db_st_free_expired (nat64_db_t * db, u32 now) vec_add1 (ste_to_be_free, ste - st); \ })); \ vec_foreach (ste_index, ste_to_be_free) \ - pool_put_index (st, ste_index[0]); \ + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ vec_free (ste_to_be_free); \ ste_to_be_free = 0; foreach_snat_protocol diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h index 965d27c3..dad35230 100644 --- a/src/vnet/ip/ip4_to_ip6.h +++ b/src/vnet/ip/ip4_to_ip6.h @@ -367,11 +367,15 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, sizeof (*inner_frag)); } - 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); + /* 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 { -- cgit 1.2.3-korg From 029f3d2c1c6b04a6cfef17242cb36b304025fe23 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 15 Jun 2017 02:28:50 -0700 Subject: NAT64: Hairpinning (VPP-699) Change-Id: I83a6c277fa211ac2c2ca2d603650c992886af0a7 Signed-off-by: Matus Fabian --- src/plugins/snat/nat64.c | 73 +++++++-- src/plugins/snat/nat64_cli.c | 2 +- src/plugins/snat/nat64_in2out.c | 338 +++++++++++++++++++++++++++++++++++++++- src/plugins/snat/nat64_out2in.c | 35 ++++- src/plugins/snat/snat_api.c | 4 +- src/vnet/ip/ip4_to_ip6.h | 55 ++++--- src/vnet/ip/ip6_to_ip4.h | 22 +-- test/test_snat.py | 285 +++++++++++++++++++++++++++++++-- 8 files changed, 748 insertions(+), 66 deletions(-) (limited to 'src/vnet/ip/ip4_to_ip6.h') diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c index ef745473..1e8b7596 100644 --- a/src/plugins/snat/nat64.c +++ b/src/plugins/snat/nat64.c @@ -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; diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c index 106d9aee..e2e35338 100644 --- a/src/plugins/snat/nat64_cli.c +++ b/src/plugins/snat/nat64_cli.c @@ -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, diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c index eba69326..fd32bfc5 100644 --- a/src/plugins/snat/nat64_in2out.c +++ b/src/plugins/snat/nat64_in2out.c @@ -21,6 +21,15 @@ #include #include +/* *INDENT-OFF* */ +static u8 well_known_prefix[] = { + 0x00, 0x64, 0xff, 0x9b, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; +/* *INDENT-ON* */ + typedef struct { u32 sw_if_index; @@ -65,7 +74,8 @@ static char *nat64_in2out_error_strings[] = { typedef enum { - NAT64_IN2OUT_NEXT_LOOKUP, + NAT64_IN2OUT_NEXT_IP4_LOOKUP, + NAT64_IN2OUT_NEXT_IP6_LOOKUP, NAT64_IN2OUT_NEXT_DROP, NAT64_IN2OUT_N_NEXT, } nat64_in2out_next_t; @@ -76,6 +86,31 @@ typedef struct nat64_in2out_set_ctx_t_ vlib_main_t *vm; } nat64_in2out_set_ctx_t; +/** + * @brief Check whether is a hairpinning. + * + * If the destination IP address of the packet is an IPv4 address assigned to + * the NAT64 itself, then the packet is a hairpin packet. + * + * param dst_addr Destination address of the packet. + * + * @returns 1 if hairpinning, otherwise 0. + */ +static_always_inline int +is_hairpinning (ip6_address_t * dst_addr) +{ + nat64_main_t *nm = &nat64_main; + int i; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3]) + return 1; + } + + return 0; +} + static int nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) @@ -145,6 +180,18 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip4->dst_address.as_u32 = daddr.ip4.as_u32; + if (proto == SNAT_PROTOCOL_TCP) + { + u16 *checksum; + ip_csum_t csum; + tcp_header_t *tcp = ip6_next_header (ip6); + + checksum = &tcp->checksum; + csum = ip_csum_sub_even (*checksum, sport); + csum = ip_csum_add_even (csum, udp->src_port); + *checksum = ip_csum_fold (csum); + } + return 0; } @@ -279,6 +326,10 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, else { udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + u16 *checksum; + ip_csum_t csum; + u16 sport = udp->src_port; u16 dport = udp->dst_port; @@ -295,11 +346,267 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip4->dst_address.as_u32 = bibe->out_addr.as_u32; udp->dst_port = bibe->out_port; ip4->src_address.as_u32 = saddr.ip4.as_u32; + + if (proto == SNAT_PROTOCOL_TCP) + checksum = &tcp->checksum; + else + checksum = &udp->checksum; + csum = ip_csum_sub_even (*checksum, dport); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); } return 0; } +static int +nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + u16 *checksum; + ip_csum_t csum; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]); + 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]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); + + if (!bibe) + { + u16 out_port; + ip4_address_t out_addr; + if (nat64_alloc_out_addr_and_port + (fib_index, proto, &out_addr, &out_port)) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, + sport, clib_host_to_net_u16 (out_port), + fib_index, proto, 0); + if (!bibe) + return -1; + } + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, dport); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, vm); + + sport = udp->src_port = bibe->out_port; + memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t)); + saddr.ip6.as_u32[3] = ip6->src_address.as_u32[3] = bibe->out_addr.as_u32; + + saddr.ip6.as_u32[0] = 0; + saddr.ip6.as_u32[1] = 0; + saddr.ip6.as_u32[2] = 0; + daddr.ip6.as_u32[0] = 0; + daddr.ip6.as_u32[1] = 0; + daddr.ip6.as_u32[2] = 0; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0, + 0); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0); + + if (!bibe) + return -1; + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, + &saddr.ip4, sport); + } + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->dst_port = bibe->in_port; + + csum = ip_csum_add_even (csum, 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]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + return 0; +} + +static int +nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + icmp46_header_t *icmp = ip6_next_header (ip6); + ip6_header_t *inner_ip6; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + snat_protocol_t proto; + udp_header_t *udp; + tcp_header_t *tcp; + u16 *checksum, sport, dport; + ip_csum_t csum; + + if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) + return -1; + + inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); + + proto = ip_proto_to_snat_proto (inner_ip6->protocol); + + if (proto == SNAT_PROTOCOL_ICMP) + return -1; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = inner_ip6->src_address.as_u64[0]; + saddr.as_u64[1] = inner_ip6->src_address.as_u64[1]; + daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1]; + + udp = ip6_next_header (inner_ip6); + tcp = ip6_next_header (inner_ip6); + + sport = udp->src_port; + dport = udp->dst_port; + + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]); + 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]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 1); + if (!ste) + return -1; + + clib_warning (""); + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + dport = udp->dst_port = bibe->out_port; + memcpy (&inner_ip6->dst_address, well_known_prefix, sizeof (ip6_address_t)); + daddr.ip6.as_u32[3] = inner_ip6->dst_address.as_u32[3] = + bibe->out_addr.as_u32; + + saddr.ip6.as_u32[0] = 0; + saddr.ip6.as_u32[1] = 0; + saddr.ip6.as_u32[2] = 0; + daddr.ip6.as_u32[0] = 0; + daddr.ip6.as_u32[1] = 0; + daddr.ip6.as_u32[2] = 0; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0, + 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; + inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->src_port = bibe->in_port; + + 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]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + if (!vec_len (nm->addr_pool)) + return -1; + + memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t)); + ip6->src_address.as_u32[3] = nm->addr_pool[0].addr.as_u32; + ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; + ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; + + icmp->checksum = 0; + csum = ip_csum_with_carry (0, ip6->payload_length); + csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol)); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]); + csum = + ip_incremental_checksum (csum, icmp, + clib_net_to_host_u16 (ip6->payload_length)); + icmp->checksum = ~ip_csum_fold (csum); + + return 0; +} + static uword nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) @@ -343,7 +650,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, ctx0.b = b0; ctx0.vm = vm; - next0 = NAT64_IN2OUT_NEXT_LOOKUP; + next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP; if (PREDICT_FALSE (ip6_parse @@ -366,6 +673,18 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, if (proto0 == SNAT_PROTOCOL_ICMP) { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_icmp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + if (icmp6_to_icmp (b0, nat64_in2out_icmp_set_cb, &ctx0, nat64_in2out_inner_icmp_set_cb, &ctx0)) @@ -377,6 +696,18 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, } else { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + if (ip6_to_ip4_tcp_udp (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0)) { @@ -422,7 +753,8 @@ VLIB_REGISTER_NODE (nat64_in2out_node) = { /* edit / add dispositions here */ .next_nodes = { [NAT64_IN2OUT_NEXT_DROP] = "error-drop", - [NAT64_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", }, }; /* *INDENT-ON* */ diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c index c11bbb5a..0a5fbe5b 100644 --- a/src/plugins/snat/nat64_out2in.c +++ b/src/plugins/snat/nat64_out2in.c @@ -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; diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c index 2a7f115f..383df399 100644 --- a/src/plugins/snat/snat_api.c +++ b/src/plugins/snat/snat_api.c @@ -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); diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h index dad35230..cdd37072 100644 --- a/src/vnet/ip/ip4_to_ip6.h +++ b/src/vnet/ip/ip4_to_ip6.h @@ -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]); diff --git a/src/vnet/ip/ip6_to_ip4.h b/src/vnet/ip/ip6_to_ip4.h index 92f73ba1..7a0d5349 100644 --- a/src/vnet/ip/ip6_to_ip4.h +++ b/src/vnet/ip/ip6_to_ip4.h @@ -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 - csum = - ip_csum_sub_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6)); + //Recompute ICMP checksum + icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4; + inner_icmp->checksum = 0; 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); } diff --git a/test/test_snat.py b/test/test_snat.py index c2f9280d..ee689e6a 100644 --- a/test/test_snat.py +++ b/test/test_snat.py @@ -27,6 +27,17 @@ class MethodHolder(VppTestCase): def tearDown(self): super(MethodHolder, self).tearDown() + def check_ip_checksum(self, pkt): + """ + Check IP checksum of the packet + + :param pkt: Packet to check IP checksum + """ + new = pkt.__class__(str(pkt)) + del new['IP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['IP'].chksum, pkt['IP'].chksum) + def check_tcp_checksum(self, pkt): """ Check TCP checksum in IP packet @@ -38,6 +49,85 @@ class MethodHolder(VppTestCase): new = new.__class__(str(new)) self.assertEqual(new['TCP'].chksum, pkt['TCP'].chksum) + def check_udp_checksum(self, pkt): + """ + Check UDP checksum in IP packet + + :param pkt: Packet to check UDP checksum + """ + new = pkt.__class__(str(pkt)) + del new['UDP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['UDP'].chksum, pkt['UDP'].chksum) + + def check_icmp_errror_embedded(self, pkt): + """ + Check ICMP error embeded packet checksum + + :param pkt: Packet to check ICMP error embeded packet checksum + """ + if pkt.haslayer(IPerror): + new = pkt.__class__(str(pkt)) + del new['IPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['IPerror'].chksum, pkt['IPerror'].chksum) + + if pkt.haslayer(TCPerror): + new = pkt.__class__(str(pkt)) + del new['TCPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['TCPerror'].chksum, pkt['TCPerror'].chksum) + + if pkt.haslayer(UDPerror): + if pkt['UDPerror'].chksum != 0: + new = pkt.__class__(str(pkt)) + del new['UDPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['UDPerror'].chksum, + pkt['UDPerror'].chksum) + + if pkt.haslayer(ICMPerror): + del new['ICMPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPerror'].chksum, pkt['ICMPerror'].chksum) + + def check_icmp_checksum(self, pkt): + """ + Check ICMP checksum in IPv4 packet + + :param pkt: Packet to check ICMP checksum + """ + new = pkt.__class__(str(pkt)) + del new['ICMP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMP'].chksum, pkt['ICMP'].chksum) + if pkt.haslayer(IPerror): + self.check_icmp_errror_embedded(pkt) + + def check_icmpv6_checksum(self, pkt): + """ + Check ICMPv6 checksum in IPv4 packet + + :param pkt: Packet to check ICMPv6 checksum + """ + new = pkt.__class__(str(pkt)) + if pkt.haslayer(ICMPv6DestUnreach): + del new['ICMPv6DestUnreach'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6DestUnreach'].cksum, + pkt['ICMPv6DestUnreach'].cksum) + self.check_icmp_errror_embedded(pkt) + if pkt.haslayer(ICMPv6EchoRequest): + del new['ICMPv6EchoRequest'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6EchoRequest'].cksum, + pkt['ICMPv6EchoRequest'].cksum) + if pkt.haslayer(ICMPv6EchoReply): + del new['ICMPv6EchoReply'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6EchoReply'].cksum, + pkt['ICMPv6EchoReply'].cksum) + def create_stream_in(self, in_if, out_if, ttl=64): """ Create packet stream for inside network @@ -144,6 +234,7 @@ class MethodHolder(VppTestCase): self.assertEqual(packet_num, len(capture)) for packet in capture: try: + self.check_ip_checksum(packet) self.assertEqual(packet[IP].src, nat_ip) if dst_ip is not None: self.assertEqual(packet[IP].dst, dst_ip) @@ -154,6 +245,7 @@ class MethodHolder(VppTestCase): self.assertNotEqual( packet[TCP].sport, self.tcp_port_in) self.tcp_port_out = packet[TCP].sport + self.check_tcp_checksum(packet) elif packet.haslayer(UDP): if same_port: self.assertEqual(packet[UDP].sport, self.udp_port_in) @@ -167,6 +259,7 @@ class MethodHolder(VppTestCase): else: self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) self.icmp_id_out = packet[ICMP].id + self.check_icmp_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(outside network):", packet)) @@ -183,13 +276,16 @@ class MethodHolder(VppTestCase): self.assertEqual(packet_num, len(capture)) for packet in capture: try: + self.check_ip_checksum(packet) self.assertEqual(packet[IP].dst, in_if.remote_ip4) if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.check_tcp_checksum(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) else: self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.check_icmp_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(inside network):", packet)) @@ -211,11 +307,14 @@ class MethodHolder(VppTestCase): self.assertEqual(packet[IPv6].dst, dst_ip) if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.check_tcp_checksum(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.check_udp_checksum(packet) else: self.assertEqual(packet[ICMPv6EchoReply].id, self.icmp_id_in) + self.check_icmpv6_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(inside network):", packet)) @@ -2400,15 +2499,24 @@ class TestNAT64(MethodHolder): cls.icmp_id_out = 6305 cls.nat_addr = '10.0.0.3' cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr) + cls.vrf1_id = 10 + cls.vrf1_nat_addr = '10.0.10.3' + cls.vrf1_nat_addr_n = socket.inet_pton(socket.AF_INET, + cls.vrf1_nat_addr) - cls.create_pg_interfaces(range(2)) + cls.create_pg_interfaces(range(3)) cls.ip6_interfaces = list(cls.pg_interfaces[0:1]) + cls.ip6_interfaces.append(cls.pg_interfaces[2]) cls.ip4_interfaces = list(cls.pg_interfaces[1:2]) + cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id) + + cls.pg0.generate_remote_hosts(2) + for i in cls.ip6_interfaces: i.admin_up() i.config_ip6() - i.resolve_ndp() + i.configure_ipv6_neighbors() for i in cls.ip4_interfaces: i.admin_up() @@ -2540,8 +2648,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(3) - self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr, + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, dst_ip=self.pg1.remote_ip4) # out2in @@ -2549,7 +2657,7 @@ class TestNAT64(MethodHolder): self.pg1.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(3) + capture = self.pg0.get_capture(len(pkts)) ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) @@ -2558,8 +2666,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(3) - self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr, + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, dst_ip=self.pg1.remote_ip4) # out2in @@ -2567,14 +2675,34 @@ class TestNAT64(MethodHolder): self.pg1.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(3) - ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) + capture = self.pg0.get_capture(len(pkts)) self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) ses_num_end = self.nat64_get_ses_num() self.assertEqual(ses_num_end - ses_num_start, 3) + # tenant with specific VRF + self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n, + self.vrf1_nat_addr_n, + vrf_id=self.vrf1_id) + self.vapi.nat64_add_del_interface(self.pg2.sw_if_index) + + pkts = self.create_stream_in_ip6(self.pg2, self.pg1) + self.pg2.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg2.remote_ip6) + def test_static(self): """ NAT64 static translation test """ self.tcp_port_in = 60303 @@ -2612,8 +2740,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(3) - self.verify_capture_out(capture, packet_num=3, nat_ip=self.nat_addr, + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, dst_ip=self.pg1.remote_ip4, same_port=True) # out2in @@ -2621,7 +2749,7 @@ class TestNAT64(MethodHolder): self.pg1.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(3) + capture = self.pg0.get_capture(len(pkts)) ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) @@ -2643,7 +2771,7 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(3) + capture = self.pg1.get_capture(len(pkts)) ses_num_before_timeout = self.nat64_get_ses_num() @@ -2672,7 +2800,7 @@ class TestNAT64(MethodHolder): self.pg_enable_capture(self.pg_interfaces) self.pg_start() capture_ip4 = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture_ip4, packet_num=3, + self.verify_capture_out(capture_ip4, nat_ip=self.nat_addr, dst_ip=self.pg1.remote_ip4) @@ -2703,6 +2831,7 @@ class TestNAT64(MethodHolder): inner = packet[IPerror] self.assertEqual(inner.src, self.pg1.remote_ip4) self.assertEqual(inner.dst, self.nat_addr) + self.check_icmp_checksum(packet) if inner.haslayer(TCPerror): self.assertEqual(inner[TCPerror].dport, self.tcp_port_out) elif inner.haslayer(UDPerror): @@ -2731,6 +2860,7 @@ class TestNAT64(MethodHolder): inner = icmp[IPerror6] self.assertEqual(inner.src, self.pg0.remote_ip6) self.assertEqual(inner.dst, ip.src) + self.check_icmpv6_checksum(packet) if inner.haslayer(TCPerror): self.assertEqual(inner[TCPerror].sport, self.tcp_port_in) elif inner.haslayer(UDPerror): @@ -2742,6 +2872,132 @@ class TestNAT64(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise + def test_hairpinning(self): + """ NAT64 hairpinning """ + + client = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + server_tcp_in_port = 22 + server_tcp_out_port = 4022 + server_udp_in_port = 23 + server_udp_out_port = 4023 + client_tcp_in_port = 1234 + client_udp_in_port = 1235 + client_tcp_out_port = 0 + client_udp_out_port = 0 + ip = IPv6(src=''.join(['64:ff9b::', self.nat_addr])) + nat_addr_ip6 = ip.src + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + self.vapi.nat64_add_del_static_bib(server.ip6n, + self.nat_addr_n, + server_tcp_in_port, + server_tcp_out_port, + IP_PROTOS.tcp) + self.vapi.nat64_add_del_static_bib(server.ip6n, + self.nat_addr_n, + server_udp_in_port, + server_udp_out_port, + IP_PROTOS.udp) + + # client to server + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + UDP(sport=client_udp_in_port, dport=server_udp_out_port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, server.ip6) + if packet.haslayer(TCP): + self.assertNotEqual(packet[TCP].sport, client_tcp_in_port) + self.assertEqual(packet[TCP].dport, server_tcp_in_port) + self.check_tcp_checksum(packet) + client_tcp_out_port = packet[TCP].sport + else: + self.assertNotEqual(packet[UDP].sport, client_udp_in_port) + self.assertEqual(packet[UDP].dport, server_udp_in_port) + self.check_udp_checksum(packet) + client_udp_out_port = packet[UDP].sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to client + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=server.ip6, dst=nat_addr_ip6) / + TCP(sport=server_tcp_in_port, dport=client_tcp_out_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=server.ip6, dst=nat_addr_ip6) / + UDP(sport=server_udp_in_port, dport=client_udp_out_port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, client.ip6) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, server_tcp_out_port) + self.assertEqual(packet[TCP].dport, client_tcp_in_port) + self.check_tcp_checksum(packet) + else: + self.assertEqual(packet[UDP].sport, server_udp_out_port) + self.assertEqual(packet[UDP].dport, client_udp_in_port) + self.check_udp_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # ICMP error + pkts = [] + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + ICMPv6DestUnreach(code=1) / + packet[IPv6] for packet in capture] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, server.ip6) + icmp = packet[ICMPv6DestUnreach] + self.assertEqual(icmp.code, 1) + inner = icmp[IPerror6] + self.assertEqual(inner.src, server.ip6) + self.assertEqual(inner.dst, nat_addr_ip6) + self.check_icmpv6_checksum(packet) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].sport, server_tcp_in_port) + self.assertEqual(inner[TCPerror].dport, + client_tcp_out_port) + else: + self.assertEqual(inner[UDPerror].sport, server_udp_in_port) + self.assertEqual(inner[UDPerror].dport, + client_udp_out_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + def nat64_get_ses_num(self): """ Return number of active NAT64 sessions. @@ -2804,6 +3060,7 @@ class TestNAT64(MethodHolder): for addr in adresses: self.vapi.nat64_add_del_pool_addr_range(addr.address, addr.address, + vrf_id=addr.vrf_id, is_add=0) def tearDown(self): -- cgit 1.2.3-korg From f8cd5817442f3a191befb3242a2c0c9bdd927ce0 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 11 Jul 2017 03:55:02 -0700 Subject: NAT64: Fallback to 3-tuple key for non TCP/UDP sessions (VPP-884) Change-Id: I4cafc8291725feb499355092bd429433e649b5b2 Signed-off-by: Matus Fabian --- src/plugins/snat/nat64.c | 21 +- src/plugins/snat/nat64_cli.c | 99 +++++++--- src/plugins/snat/nat64_db.c | 113 +++++++---- src/plugins/snat/nat64_db.h | 28 ++- src/plugins/snat/nat64_in2out.c | 414 +++++++++++++++++++++++++++++++++++++--- src/plugins/snat/nat64_out2in.c | 91 +++++++-- src/plugins/snat/snat.c | 1 + src/plugins/snat/snat_api.c | 14 +- src/vnet/ip/ip4_to_ip6.h | 62 ++++++ src/vnet/ip/ip6_to_ip4.h | 61 ++++++ test/test_snat.py | 55 ++++-- 11 files changed, 807 insertions(+), 152 deletions(-) (limited to 'src/vnet/ip/ip4_to_ip6.h') diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c index 47809b05..bd915b59 100644 --- a/src/plugins/snat/nat64.c +++ b/src/plugins/snat/nat64.c @@ -125,7 +125,9 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) clib_bitmap_free (a->busy_##n##_port_bitmap); foreach_snat_protocol #undef _ - vec_del1 (nm->addr_pool, i); + /* Delete sessions using address */ + nat64_db_free_out_addr (&nm->db, &a->addr); + vec_del1 (nm->addr_pool, i); } /* Add/del external address to FIB */ @@ -362,7 +364,7 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr, addr.as_u64[1] = in_addr->as_u64[1]; bibe = nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port), - p, fib_index, 1); + proto, fib_index, 1); if (is_add) { @@ -389,8 +391,11 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr, foreach_snat_protocol #undef _ default: - clib_warning ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = out_addr->as_u32; + if (nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, fib_index, 0)) + return VNET_API_ERROR_INVALID_VALUE; } break; } @@ -398,7 +403,7 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr, nat64_db_bib_entry_create (&nm->db, in_addr, out_addr, clib_host_to_net_u16 (in_port), clib_host_to_net_u16 (out_port), fib_index, - p, 1); + proto, 1); if (!bibe) return VNET_API_ERROR_UNSPECIFIED; } @@ -511,7 +516,7 @@ nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm) nat64_main_t *nm = &nat64_main; u32 now = (u32) vlib_time_now (vm); - switch (ste->proto) + switch (ip_proto_to_snat_proto (ste->proto)) { case SNAT_PROTOCOL_ICMP: ste->expire = now + nm->icmp_timeout; @@ -539,6 +544,7 @@ nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm) ste->expire = now + nm->udp_timeout; return; default: + ste->expire = now + nm->udp_timeout; return; } } @@ -808,9 +814,6 @@ nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index) clib_warning ("invalid prefix length"); break; } - - clib_warning ("%U %U plen %u", format_ip6_address, ip6, format_ip4_address, - ip4, plen); } /** diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c index 32f671d5..d48cb724 100644 --- a/src/plugins/snat/nat64_cli.c +++ b/src/plugins/snat/nat64_cli.c @@ -303,7 +303,7 @@ nat64_add_del_static_bib_command_fn (vlib_main_t * ip4_address_t out_addr; u16 in_port = 0; u16 out_port = 0; - u32 vrf_id = 0; + u32 vrf_id = 0, protocol; snat_protocol_t proto = 0; u8 p = 0; int rv; @@ -327,6 +327,11 @@ nat64_add_del_static_bib_command_fn (vlib_main_t * ; else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) ; + else + if (unformat + (line_input, "%U %U %u", unformat_ip6_address, &in_addr, + unformat_ip4_address, &out_addr, &protocol)) + p = (u8) protocol; else if (unformat (line_input, "del")) is_add = 0; else @@ -337,19 +342,24 @@ nat64_add_del_static_bib_command_fn (vlib_main_t * } } - if (!in_port) + if (!p) { - error = clib_error_return (0, "inside port and address must be set"); - goto done; - } + if (!in_port) + { + error = + clib_error_return (0, "inside port and address must be set"); + goto done; + } - if (!out_port) - { - error = clib_error_return (0, "outside port and address must be set"); - goto done; - } + if (!out_port) + { + error = + clib_error_return (0, "outside port and address must be set"); + goto done; + } - p = snat_proto_to_ip_proto (proto); + p = snat_proto_to_ip_proto (proto); + } rv = nat64_add_del_static_bib_entry (&in_addr, &out_addr, in_port, out_port, p, @@ -391,12 +401,27 @@ nat64_cli_bib_walk (nat64_db_bib_entry_t * bibe, void *ctx) if (!fib) return -1; - vlib_cli_output (vm, " %U %u %U %u %U vrf %u %s %u sessions", - format_ip6_address, &bibe->in_addr, - clib_net_to_host_u16 (bibe->in_port), format_ip4_address, - &bibe->out_addr, clib_net_to_host_u16 (bibe->out_port), - format_snat_protocol, bibe->proto, fib->ft_table_id, - bibe->is_static ? "static" : "dynamic", bibe->ses_num); + switch (bibe->proto) + { + case IP_PROTOCOL_ICMP: + case IP_PROTOCOL_TCP: + case IP_PROTOCOL_UDP: + vlib_cli_output (vm, " %U %u %U %u protocol %U vrf %u %s %u sessions", + format_ip6_address, &bibe->in_addr, + clib_net_to_host_u16 (bibe->in_port), + format_ip4_address, &bibe->out_addr, + clib_net_to_host_u16 (bibe->out_port), + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), fib->ft_table_id, + bibe->is_static ? "static" : "dynamic", bibe->ses_num); + break; + default: + vlib_cli_output (vm, " %U %U protocol %u vrf %u %s %u sessions", + format_ip6_address, &bibe->in_addr, + format_ip4_address, &bibe->out_addr, + bibe->proto, fib->ft_table_id, + bibe->is_static ? "static" : "dynamic", bibe->ses_num); + } return 0; } @@ -407,7 +432,8 @@ nat64_show_bib_command_fn (vlib_main_t * vm, nat64_main_t *nm = &nat64_main; unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; - snat_protocol_t proto = 0; + u32 proto = ~0; + u8 p = 0; if (nm->is_disabled) return clib_error_return (0, @@ -417,6 +443,8 @@ nat64_show_bib_command_fn (vlib_main_t * vm, return 0; if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + p = snat_proto_to_ip_proto (proto); + else if (unformat (line_input, "unknown")) ; else { @@ -426,7 +454,7 @@ nat64_show_bib_command_fn (vlib_main_t * vm, } vlib_cli_output (vm, "NAT64 %U BIB:", format_snat_protocol, proto); - nat64_db_bib_walk (&nm->db, proto, nat64_cli_bib_walk, vm); + nat64_db_bib_walk (&nm->db, p, nat64_cli_bib_walk, vm); done: unformat_free (line_input); @@ -563,17 +591,18 @@ nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx) u32 vrf_id = fib->ft_table_id; - if (ste->proto == SNAT_PROTOCOL_ICMP) - vlib_cli_output (vm, " %U %U %u %U %U %u %U vrf %u", + if (ste->proto == IP_PROTOCOL_ICMP) + vlib_cli_output (vm, " %U %U %u %U %U %u protocol %U vrf %u", format_ip6_address, &bibe->in_addr, format_ip6_address, &ste->in_r_addr, clib_net_to_host_u16 (bibe->in_port), format_ip4_address, &bibe->out_addr, format_ip4_address, &ste->out_r_addr, clib_net_to_host_u16 (bibe->out_port), - format_snat_protocol, bibe->proto, vrf_id); - else - vlib_cli_output (vm, " %U %u %U %u %U %u %U %u %U vrf %u", + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), vrf_id); + else if (ste->proto == IP_PROTOCOL_TCP || ste->proto == IP_PROTOCOL_UDP) + vlib_cli_output (vm, " %U %u %U %u %U %u %U %u protcol %U vrf %u", format_ip6_address, &bibe->in_addr, clib_net_to_host_u16 (bibe->in_port), format_ip6_address, &ste->in_r_addr, @@ -582,7 +611,16 @@ nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx) clib_net_to_host_u16 (bibe->out_port), format_ip4_address, &ste->out_r_addr, clib_net_to_host_u16 (ste->r_port), - format_snat_protocol, bibe->proto, vrf_id); + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), vrf_id); + else + vlib_cli_output (vm, " %U %U %U %U protocol %u vrf %u", + format_ip6_address, &bibe->in_addr, + format_ip6_address, &ste->in_r_addr, + format_ip4_address, &bibe->out_addr, + format_ip4_address, &ste->out_r_addr, + bibe->proto, vrf_id); + return 0; } @@ -593,7 +631,8 @@ nat64_show_st_command_fn (vlib_main_t * vm, nat64_main_t *nm = &nat64_main; unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; - snat_protocol_t proto = 0; + u32 proto = ~0; + u8 p = 0; if (nm->is_disabled) return clib_error_return (0, @@ -603,6 +642,8 @@ nat64_show_st_command_fn (vlib_main_t * vm, return 0; if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + p = snat_proto_to_ip_proto (proto); + else if (unformat (line_input, "unknown")) ; else { @@ -613,7 +654,7 @@ nat64_show_st_command_fn (vlib_main_t * vm, vlib_cli_output (vm, "NAT64 %U session table:", format_snat_protocol, proto); - nat64_db_st_walk (&nm->db, proto, nat64_cli_st_walk, vm); + nat64_db_st_walk (&nm->db, p, nat64_cli_st_walk, vm); done: unformat_free (line_input); @@ -819,7 +860,7 @@ VLIB_CLI_COMMAND (nat64_add_del_static_bib_command, static) = { ?*/ VLIB_CLI_COMMAND (show_nat64_bib_command, static) = { .path = "show nat64 bib", - .short_help = "show nat64 bib tcp|udp|icmp", + .short_help = "show nat64 bib tcp|udp|icmp|unknown", .function = nat64_show_bib_command_fn, }; @@ -883,7 +924,7 @@ VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = { ?*/ VLIB_CLI_COMMAND (show_nat64_st_command, static) = { .path = "show nat64 session table", - .short_help = "show nat64 session table tcp|udp|icmp", + .short_help = "show nat64 session table tcp|udp|icmp|unknown", .function = nat64_show_st_command_fn, }; diff --git a/src/plugins/snat/nat64_db.c b/src/plugins/snat/nat64_db.c index d15761d2..b6e199c6 100644 --- a/src/plugins/snat/nat64_db.c +++ b/src/plugins/snat/nat64_db.c @@ -44,7 +44,7 @@ nat64_db_init (nat64_db_t * db) nat64_db_bib_entry_t * nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr, ip4_address_t * out_addr, u16 in_port, - u16 out_port, u32 fib_index, snat_protocol_t proto, + u16 out_port, u32 fib_index, u8 proto, u8 is_static) { nat64_db_bib_entry_t *bibe; @@ -52,7 +52,7 @@ nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr, clib_bihash_kv_24_8_t kv; /* create pool entry */ - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -64,8 +64,9 @@ nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", proto); - return 0; + pool_get (db->bib._unk_proto_bib, bibe); + kv.value = bibe - db->bib._unk_proto_bib; + break; } memset (bibe, 0, sizeof (*bibe)); bibe->in_addr.as_u64[0] = in_addr->as_u64[0]; @@ -110,7 +111,7 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) u32 *ste_to_be_free = 0, *ste_index, bibe_index; nat64_db_st_entry_t *st, *ste; - switch (bibe->proto) + switch (ip_proto_to_snat_proto (bibe->proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -122,8 +123,9 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", bibe->proto); - return; + bib = db->bib._unk_proto_bib; + st = db->st._unk_proto_st; + break; } bibe_index = bibe - bib; @@ -169,14 +171,14 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) nat64_db_bib_entry_t * nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, - snat_protocol_t proto, u32 fib_index, u8 is_ip6) + u8 proto, u32 fib_index, u8 is_ip6) { nat64_db_bib_entry_t *bibe = 0; nat64_db_bib_entry_key_t bibe_key; clib_bihash_kv_24_8_t kv, value; nat64_db_bib_entry_t *bib; - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -187,8 +189,8 @@ nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", proto); - return 0; + bib = db->bib._unk_proto_bib; + break; } bibe_key.addr.as_u64[0] = addr->as_u64[0]; @@ -210,12 +212,12 @@ nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, } void -nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto, +nat64_db_bib_walk (nat64_db_t * db, u8 proto, nat64_db_bib_walk_fn_t fn, void *ctx) { nat64_db_bib_entry_t *bib, *bibe; - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -226,8 +228,8 @@ nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol"); - return; + bib = db->bib._unk_proto_bib; + break; } /* *INDENT-OFF* */ @@ -240,12 +242,11 @@ nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto, } nat64_db_bib_entry_t * -nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto, - u32 bibe_index) +nat64_db_bib_entry_by_index (nat64_db_t * db, u8 proto, u32 bibe_index) { nat64_db_bib_entry_t *bib; - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -256,20 +257,20 @@ nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", proto); - return 0; + bib = db->bib._unk_proto_bib; + break; } return pool_elt_at_index (bib, bibe_index); } void -nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto, +nat64_db_st_walk (nat64_db_t * db, u8 proto, nat64_db_st_walk_fn_t fn, void *ctx) { nat64_db_st_entry_t *st, *ste; - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -280,8 +281,8 @@ nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol"); - return; + st = db->st._unk_proto_st; + break; } /* *INDENT-OFF* */ @@ -304,7 +305,7 @@ nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe, clib_bihash_kv_48_8_t kv; /* create pool entry */ - switch (bibe->proto) + switch (ip_proto_to_snat_proto (bibe->proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -317,8 +318,10 @@ nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", bibe->proto); - return 0; + pool_get (db->st._unk_proto_st, ste); + kv.value = ste - db->st._unk_proto_st; + bib = db->bib._unk_proto_bib; + break; } memset (ste, 0, sizeof (*ste)); ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0]; @@ -374,7 +377,7 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste) nat64_db_st_entry_key_t ste_key; clib_bihash_kv_48_8_t kv; - switch (ste->proto) + switch (ip_proto_to_snat_proto (ste->proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -386,8 +389,9 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste) #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", ste->proto); - return; + st = db->st._unk_proto_st; + bib = db->bib._unk_proto_bib; + break; } bibe = pool_elt_at_index (bib, ste->bibe_index); @@ -438,14 +442,14 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste) nat64_db_st_entry_t * nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr, ip46_address_t * r_addr, u16 l_port, u16 r_port, - snat_protocol_t proto, u32 fib_index, u8 is_ip6) + u8 proto, u32 fib_index, u8 is_ip6) { nat64_db_st_entry_t *ste = 0; nat64_db_st_entry_t *st; nat64_db_st_entry_key_t ste_key; clib_bihash_kv_48_8_t kv, value; - switch (proto) + switch (ip_proto_to_snat_proto (proto)) { /* *INDENT-OFF* */ #define _(N, i, n, s) \ @@ -456,8 +460,8 @@ nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr, #undef _ /* *INDENT-ON* */ default: - clib_warning ("unknown protocol %u", proto); - return ste; + st = db->st._unk_proto_st; + break; } memset (&ste_key, 0, sizeof (ste_key)); @@ -504,6 +508,47 @@ nad64_db_st_free_expired (nat64_db_t * db, u32 now) ste_to_be_free = 0; foreach_snat_protocol #undef _ + st = db->st._unk_proto_st; + pool_foreach (ste, st, ({ + if (ste->expire < now) + vec_add1 (ste_to_be_free, ste - st); + })); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); + vec_free (ste_to_be_free); +/* *INDENT-ON* */ +} + +void +nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr) +{ + u32 *ste_to_be_free = 0, *ste_index; + nat64_db_st_entry_t *st, *ste; + nat64_db_bib_entry_t *bibe; + +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + st = db->st._##n##_st; \ + pool_foreach (ste, st, ({ \ + bibe = pool_elt_at_index (db->bib._##n##_bib, ste->bibe_index); \ + if (bibe->out_addr.as_u32 == out_addr->as_u32) \ + vec_add1 (ste_to_be_free, ste - st); \ + })); \ + vec_foreach (ste_index, ste_to_be_free) \ + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ + vec_free (ste_to_be_free); \ + ste_to_be_free = 0; + foreach_snat_protocol +#undef _ + st = db->st._unk_proto_st; + pool_foreach (ste, st, ({ + bibe = pool_elt_at_index (db->bib._unk_proto_bib, ste->bibe_index); + if (bibe->out_addr.as_u32 == out_addr->as_u32) + vec_add1 (ste_to_be_free, ste - st); + })); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); + vec_free (ste_to_be_free); /* *INDENT-ON* */ } diff --git a/src/plugins/snat/nat64_db.h b/src/plugins/snat/nat64_db.h index caea3bf0..4511fb26 100644 --- a/src/plugins/snat/nat64_db.h +++ b/src/plugins/snat/nat64_db.h @@ -63,6 +63,7 @@ typedef struct foreach_snat_protocol #undef _ /* *INDENT-ON* */ + nat64_db_bib_entry_t *_unk_proto_bib; /* BIB lookup */ clib_bihash_24_8_t in2out; @@ -109,6 +110,7 @@ typedef struct foreach_snat_protocol #undef _ /* *INDENT-ON* */ + nat64_db_st_entry_t *_unk_proto_st; /* session lookup */ clib_bihash_48_8_t in2out; @@ -149,8 +151,7 @@ nat64_db_bib_entry_t *nat64_db_bib_entry_create (nat64_db_t * db, ip4_address_t * out_addr, u16 in_port, u16 out_port, u32 fib_index, - snat_protocol_t proto, - u8 is_static); + u8 proto, u8 is_static); /** * @brief Free NAT64 BIB entry. @@ -170,11 +171,11 @@ typedef int (*nat64_db_bib_walk_fn_t) (nat64_db_bib_entry_t * bibe, * @brief Walk NAT64 BIB. * * @param db NAT64 DB. - * @param proto BIB protocol (TCP/UDP/ICMP). + * @param proto L4 protocol. * @param fn The function to invoke on each entry visited. * @param ctx A context passed in the visit function. */ -void nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto, +void nat64_db_bib_walk (nat64_db_t * db, u8 proto, nat64_db_bib_walk_fn_t fn, void *ctx); /** @@ -192,7 +193,7 @@ void nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto, nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, - snat_protocol_t proto, + u8 proto, u32 fib_index, u8 is_ip6); /** @@ -205,8 +206,7 @@ nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db, * @return BIB entry if found. */ nat64_db_bib_entry_t *nat64_db_bib_entry_by_index (nat64_db_t * db, - snat_protocol_t proto, - u32 bibe_index); + u8 proto, u32 bibe_index); /** * @brief Create new NAT64 session table entry. * @@ -250,7 +250,7 @@ nat64_db_st_entry_t *nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr, ip46_address_t * r_addr, u16 l_port, u16 r_port, - snat_protocol_t proto, + u8 proto, u32 fib_index, u8 is_ip6); /** @@ -263,11 +263,11 @@ typedef int (*nat64_db_st_walk_fn_t) (nat64_db_st_entry_t * ste, void *ctx); * @brief Walk NAT64 session table. * * @param db NAT64 DB. - * @param proto Session table protocol (TCP/UDP/ICMP). + * @param proto L4 protocol. * @param fn The function to invoke on each entry visited. * @param ctx A context passed in the visit function. */ -void nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto, +void nat64_db_st_walk (nat64_db_t * db, u8 proto, nat64_db_st_walk_fn_t fn, void *ctx); /** @@ -278,6 +278,14 @@ void nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto, */ void nad64_db_st_free_expired (nat64_db_t * db, u32 now); +/** + * @brief Free sessions using specific outside address. + * + * @param db NAT64 DB. + * @param out_addr Outside address to match. + */ +void nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr); + #endif /* __included_nat64_db_h__ */ /* diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c index 126b076c..8c67fec2 100644 --- a/src/plugins/snat/nat64_in2out.c +++ b/src/plugins/snat/nat64_in2out.c @@ -25,6 +25,7 @@ typedef struct { u32 sw_if_index; u32 next_index; + u8 is_slow_path; } nat64_in2out_trace_t; static u8 * @@ -33,15 +34,19 @@ format_nat64_in2out_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 *); nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *); + char *tag; + + tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out"; s = - format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index, + format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index, t->next_index); return s; } vlib_node_registration_t nat64_in2out_node; +vlib_node_registration_t nat64_in2out_slowpath_node; #define foreach_nat64_in2out_error \ _(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ @@ -68,6 +73,7 @@ typedef enum NAT64_IN2OUT_NEXT_IP4_LOOKUP, NAT64_IN2OUT_NEXT_IP6_LOOKUP, NAT64_IN2OUT_NEXT_DROP, + NAT64_IN2OUT_NEXT_SLOWPATH, NAT64_IN2OUT_N_NEXT, } nat64_in2out_next_t; @@ -113,7 +119,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip46_address_t saddr, daddr; u32 sw_if_index, fib_index; udp_header_t *udp = ip6_next_header (ip6); - snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + u8 proto = ip6->protocol; u16 sport = udp->src_port; u16 dport = udp->dst_port; @@ -146,7 +152,8 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, u16 out_port; ip4_address_t out_addr; if (nat64_alloc_out_addr_and_port - (fib_index, proto, &out_addr, &out_port)) + (fib_index, ip_proto_to_snat_proto (proto), &out_addr, + &out_port)) return -1; bibe = @@ -172,7 +179,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; - if (proto == SNAT_PROTOCOL_TCP) + if (proto == IP_PROTOCOL_TCP) { u16 *checksum; ip_csum_t csum; @@ -212,12 +219,12 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) u16 in_id = ((u16 *) (icmp))[2]; ste = nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0, - SNAT_PROTOCOL_ICMP, fib_index, 1); + IP_PROTOCOL_ICMP, fib_index, 1); if (ste) { bibe = - nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP, + nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, ste->bibe_index); if (!bibe) return -1; @@ -226,7 +233,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) { bibe = nat64_db_bib_entry_find (&nm->db, &saddr, in_id, - SNAT_PROTOCOL_ICMP, fib_index, 1); + IP_PROTOCOL_ICMP, fib_index, 1); if (!bibe) { @@ -240,7 +247,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, in_id, clib_host_to_net_u16 (out_id), - fib_index, SNAT_PROTOCOL_ICMP, 0); + fib_index, IP_PROTOCOL_ICMP, 0); if (!bibe) return -1; } @@ -282,7 +289,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, nat64_db_bib_entry_t *bibe; ip46_address_t saddr, daddr; u32 sw_if_index, fib_index; - snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + u8 proto = ip6->protocol; sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; fib_index = @@ -293,10 +300,11 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, daddr.as_u64[0] = ip6->dst_address.as_u64[0]; daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - if (proto == SNAT_PROTOCOL_ICMP) + if (proto == IP_PROTOCOL_ICMP6) { icmp46_header_t *icmp = ip6_next_header (ip6); u16 in_id = ((u16 *) (icmp))[2]; + proto = IP_PROTOCOL_ICMP; if (! (icmp->type == ICMP4_echo_request @@ -341,7 +349,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, udp->dst_port = bibe->out_port; ip4->src_address.as_u32 = ste->out_r_addr.as_u32; - if (proto == SNAT_PROTOCOL_TCP) + if (proto == IP_PROTOCOL_TCP) checksum = &tcp->checksum; else checksum = &udp->checksum; @@ -353,6 +361,153 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, return 0; } +typedef struct unk_proto_st_walk_ctx_t_ +{ + ip6_address_t src_addr; + ip6_address_t dst_addr; + ip4_address_t out_addr; + u32 fib_index; + u8 proto; +} unk_proto_st_walk_ctx_t; + +static int +unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg) +{ + nat64_main_t *nm = &nat64_main; + unk_proto_st_walk_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + ip46_address_t saddr, daddr; + + if (ip46_address_is_equal (&ste->in_r_addr, &ctx->dst_addr)) + { + bibe = + nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); + if (!bibe) + return -1; + + if (ip46_address_is_equal (&bibe->in_addr, &ctx->src_addr) + && bibe->fib_index == ctx->fib_index) + { + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = bibe->out_addr.as_u32; + memset (&daddr, 0, sizeof (daddr)); + nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index); + + if (nat64_db_st_entry_find + (&nm->db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0)) + return -1; + + ctx->out_addr.as_u32 = bibe->out_addr.as_u32; + return 1; + } + } + + return 0; +} + +static int +nat64_in2out_unk_proto_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr, addr; + u32 sw_if_index, fib_index; + u8 proto = ip6->protocol; + int i; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, + 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); + + if (!bibe) + { + /* Choose same out address as for TCP/UDP session to same dst */ + unk_proto_st_walk_ctx_t ctx = { + .src_addr.as_u64[0] = ip6->src_address.as_u64[0], + .src_addr.as_u64[1] = ip6->src_address.as_u64[1], + .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], + .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], + .out_addr.as_u32 = 0, + .fib_index = fib_index, + .proto = proto, + }; + + nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, + &ctx); + + if (!ctx.out_addr.as_u32) + nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, + &ctx); + + /* Verify if out address is not already in use for protocol */ + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = ctx.out_addr.as_u32; + if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) + ctx.out_addr.as_u32 = 0; + + if (!ctx.out_addr.as_u32) + { + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; + if (!nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, 0, 0)) + break; + } + } + + if (!ctx.out_addr.as_u32) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, + &ctx.out_addr, 0, 0, fib_index, proto, + 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, 0); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip4->src_address.as_u32 = bibe->out_addr.as_u32; + ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; + + return 0; +} + + + static int nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, ip6_header_t * ip6) @@ -364,7 +519,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, u32 sw_if_index, fib_index; udp_header_t *udp = ip6_next_header (ip6); tcp_header_t *tcp = ip6_next_header (ip6); - snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + u8 proto = ip6->protocol; u16 sport = udp->src_port; u16 dport = udp->dst_port; u16 *checksum; @@ -379,7 +534,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, daddr.as_u64[0] = ip6->dst_address.as_u64[0]; daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - if (proto == SNAT_PROTOCOL_UDP) + if (proto == IP_PROTOCOL_UDP) checksum = &udp->checksum; else checksum = &tcp->checksum; @@ -411,7 +566,8 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, u16 out_port; ip4_address_t out_addr; if (nat64_alloc_out_addr_and_port - (fib_index, proto, &out_addr, &out_port)) + (fib_index, ip_proto_to_snat_proto (proto), &out_addr, + &out_port)) return -1; bibe = @@ -488,7 +644,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, ip6_header_t *inner_ip6; ip46_address_t saddr, daddr; u32 sw_if_index, fib_index; - snat_protocol_t proto; + u8 proto; udp_header_t *udp; tcp_header_t *tcp; u16 *checksum, sport, dport; @@ -499,9 +655,9 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); - proto = ip_proto_to_snat_proto (inner_ip6->protocol); + proto = inner_ip6->protocol; - if (proto == SNAT_PROTOCOL_ICMP) + if (proto == IP_PROTOCOL_ICMP6) return -1; sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; @@ -519,7 +675,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, sport = udp->src_port; dport = udp->dst_port; - if (proto == SNAT_PROTOCOL_UDP) + if (proto == IP_PROTOCOL_UDP) checksum = &udp->checksum; else checksum = &tcp->checksum; @@ -593,13 +749,144 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, return 0; } -static uword -nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) +static int +nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr, addr; + u32 sw_if_index, fib_index; + u8 proto = ip6->protocol; + int i; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, + 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); + + if (!bibe) + { + /* Choose same out address as for TCP/UDP session to same dst */ + unk_proto_st_walk_ctx_t ctx = { + .src_addr.as_u64[0] = ip6->src_address.as_u64[0], + .src_addr.as_u64[1] = ip6->src_address.as_u64[1], + .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], + .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], + .out_addr.as_u32 = 0, + .fib_index = fib_index, + .proto = proto, + }; + + nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, + &ctx); + + if (!ctx.out_addr.as_u32) + nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, + &ctx); + + /* Verify if out address is not already in use for protocol */ + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = ctx.out_addr.as_u32; + if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) + ctx.out_addr.as_u32 = 0; + + if (!ctx.out_addr.as_u32) + { + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; + if (!nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, 0, 0)) + break; + } + } + + if (!ctx.out_addr.as_u32) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, + &ctx.out_addr, 0, 0, fib_index, proto, + 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, 0); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, vm); + + nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index); + + memset (&saddr, 0, sizeof (saddr)); + memset (&daddr, 0, sizeof (daddr)); + saddr.ip4.as_u32 = bibe->out_addr.as_u32; + daddr.ip4.as_u32 = ste->out_r_addr.as_u32; + + ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, 0, 0); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, 0, 0); + + if (!bibe) + return -1; + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, + &saddr.ip4, 0); + } + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + + return 0; +} + +static inline uword +nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 is_slow_path) { u32 n_left_from, *from, *to_next; nat64_in2out_next_t next_index; u32 pkts_processed = 0; + u32 stats_node_index; + + stats_node_index = + is_slow_path ? nat64_in2out_slowpath_node.index : nat64_in2out_node.index; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -649,7 +936,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, } proto0 = ip_proto_to_snat_proto (l4_protocol0); - if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0))) + if (frag_offset0 != 0) { next0 = NAT64_IN2OUT_NEXT_DROP; b0->error = @@ -657,6 +944,41 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, goto trace0; } + if (is_slow_path) + { + if (PREDICT_TRUE (proto0 == ~0)) + { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_unk_proto_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + + if (ip6_to_ip4 (b0, nat64_in2out_unk_proto_set_cb, &ctx0)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + goto trace0; + } + else + { + if (PREDICT_FALSE (proto0 == ~0)) + { + next0 = NAT64_IN2OUT_NEXT_SLOWPATH; + goto trace0; + } + } + if (proto0 == SNAT_PROTOCOL_ICMP) { if (is_hairpinning (&ip60->dst_address)) @@ -680,7 +1002,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, goto trace0; } } - else + else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) { if (is_hairpinning (&ip60->dst_address)) { @@ -711,6 +1033,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; t->next_index = next0; + t->is_slow_path = is_slow_path; } pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP; @@ -721,32 +1044,71 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - vlib_node_increment_counter (vm, nat64_in2out_node.index, + vlib_node_increment_counter (vm, stats_node_index, NAT64_IN2OUT_ERROR_IN2OUT_PACKETS, pkts_processed); return frame->n_vectors; } +static uword +nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat64_in2out_node_fn_inline (vm, node, frame, 0); +} + /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat64_in2out_node) = { - .function = nat64_in2out_node_fn,.name = "nat64-in2out", + .function = nat64_in2out_node_fn, + .name = "nat64-in2out", .vector_size = sizeof (u32), .format_trace = format_nat64_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN (nat64_in2out_error_strings), .error_strings = nat64_in2out_error_strings, - .n_next_nodes = 2, + .n_next_nodes = NAT64_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { [NAT64_IN2OUT_NEXT_DROP] = "error-drop", [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", }, }; /* *INDENT-ON* */ VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn); +static uword +nat64_in2out_slowpath_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat64_in2out_node_fn_inline (vm, node, frame, 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = { + .function = nat64_in2out_slowpath_node_fn, + .name = "nat64-in2out-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_nat64_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat64_in2out_error_strings), + .error_strings = nat64_in2out_error_strings, + .n_next_nodes = NAT64_IN2OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [NAT64_IN2OUT_NEXT_DROP] = "error-drop", + [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_slowpath_node, + nat64_in2out_slowpath_node_fn); + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c index 755aa638..cd5b253a 100644 --- a/src/plugins/snat/nat64_out2in.c +++ b/src/plugins/snat/nat64_out2in.c @@ -88,7 +88,7 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, 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); + u8 proto = ip4->protocol; u16 dport = udp->dst_port; u16 sport = udp->src_port; u32 sw_if_index, fib_index; @@ -135,7 +135,7 @@ 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) + if (proto == IP_PROTOCOL_UDP) checksum = &udp->checksum; else checksum = &tcp->checksum; @@ -173,12 +173,12 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) u16 out_id = ((u16 *) (icmp))[2]; ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0, - SNAT_PROTOCOL_ICMP, fib_index, 0); + IP_PROTOCOL_ICMP, fib_index, 0); if (ste) { bibe = - nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP, + nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, ste->bibe_index); if (!bibe) return -1; @@ -187,7 +187,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) { bibe = nat64_db_bib_entry_find (&nm->db, &daddr, out_id, - SNAT_PROTOCOL_ICMP, fib_index, 0); + IP_PROTOCOL_ICMP, fib_index, 0); if (!bibe) return -1; @@ -231,7 +231,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, nat64_db_st_entry_t *ste; ip46_address_t saddr, daddr; u32 sw_if_index, fib_index; - snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol); + u8 proto = ip4->protocol; sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; fib_index = @@ -242,10 +242,11 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, memset (&daddr, 0, sizeof (daddr)); daddr.ip4.as_u32 = ip4->dst_address.as_u32; - if (proto == SNAT_PROTOCOL_ICMP) + if (proto == IP_PROTOCOL_ICMP6) { icmp46_header_t *icmp = ip4_next_header (ip4); u16 out_id = ((u16 *) (icmp))[2]; + proto = IP_PROTOCOL_ICMP; if (! (icmp->type == ICMP6_echo_request @@ -294,7 +295,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; udp->src_port = bibe->in_port; - if (proto == SNAT_PROTOCOL_UDP) + if (proto == IP_PROTOCOL_UDP) checksum = &udp->checksum; else checksum = &tcp->checksum; @@ -311,6 +312,62 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, return 0; } +static int +nat64_out2in_unk_proto_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + ip6_address_t ip6_saddr; + u32 sw_if_index, fib_index; + u8 proto = ip4->protocol; + + 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); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, fib_index, + 0); + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, fib_index, 0); + + if (!bibe) + return -1; + + nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, 0); + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; + ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + + return 0; +} + static uword nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) @@ -354,13 +411,6 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, next0 = NAT64_OUT2IN_NEXT_LOOKUP; proto0 = ip_proto_to_snat_proto (ip40->protocol); - if (PREDICT_FALSE (proto0 == ~0)) - { - next0 = NAT64_OUT2IN_NEXT_DROP; - b0->error = - node->errors[NAT64_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - goto trace0; - } if (proto0 == SNAT_PROTOCOL_ICMP) { @@ -373,7 +423,7 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, goto trace0; } } - else + else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) { if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0)) { @@ -382,6 +432,15 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, goto trace0; } } + else + { + if (ip4_to_ip6 (b0, nat64_out2in_unk_proto_set_cb, &ctx0)) + { + next0 = NAT64_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + } trace0: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c index f196b5c2..315cec8a 100644 --- a/src/plugins/snat/snat.c +++ b/src/plugins/snat/snat.c @@ -1398,6 +1398,7 @@ format_snat_protocol (u8 * s, va_list * args) #undef _ default: s = format (s, "unknown"); + return s; } s = format (s, "%s", t); return s; diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c index ee623d26..227074f9 100644 --- a/src/plugins/snat/snat_api.c +++ b/src/plugins/snat/snat_api.c @@ -1580,7 +1580,7 @@ nat64_api_bib_walk (nat64_db_bib_entry_t * bibe, void *arg) rmp->i_port = bibe->in_port; rmp->o_port = bibe->out_port; rmp->vrf_id = ntohl (fib->ft_table_id); - rmp->proto = snat_proto_to_ip_proto (bibe->proto); + rmp->proto = bibe->proto; rmp->is_static = bibe->is_static; rmp->ses_num = ntohl (bibe->ses_num); @@ -1594,7 +1594,6 @@ vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp) { unix_shared_memory_queue_t *q; nat64_main_t *nm = &nat64_main; - snat_protocol_t proto; if (nm->is_disabled) return; @@ -1608,9 +1607,7 @@ vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp) .context = mp->context, }; - proto = ip_proto_to_snat_proto (mp->proto); - - nat64_db_bib_walk (&nm->db, proto, nat64_api_bib_walk, &ctx); + nat64_db_bib_walk (&nm->db, mp->proto, nat64_api_bib_walk, &ctx); } static void * @@ -1729,7 +1726,7 @@ nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg) clib_memcpy (rmp->or_addr, &(ste->out_r_addr), 4); rmp->il_port = ste->r_port; rmp->vrf_id = ntohl (fib->ft_table_id); - rmp->proto = snat_proto_to_ip_proto (ste->proto); + rmp->proto = ste->proto; vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); @@ -1741,7 +1738,6 @@ vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp) { unix_shared_memory_queue_t *q; nat64_main_t *nm = &nat64_main; - snat_protocol_t proto; if (nm->is_disabled) return; @@ -1755,9 +1751,7 @@ vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp) .context = mp->context, }; - proto = ip_proto_to_snat_proto (mp->proto); - - nat64_db_st_walk (&nm->db, proto, nat64_api_st_walk, &ctx); + nat64_db_st_walk (&nm->db, mp->proto, nat64_api_st_walk, &ctx); } static void * diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h index cdd37072..6ffc562c 100644 --- a/src/vnet/ip/ip4_to_ip6.h +++ b/src/vnet/ip/ip4_to_ip6.h @@ -586,6 +586,68 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) return 0; } +/** + * @brief Translate IPv4 packet to IPv6 (IP header only). + * + * @param p Buffer to translate. + * @param fn The function to translate header. + * @param ctx A context passed in the header translate function. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +ip4_to_ip6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) +{ + ip4_header_t *ip4; + ip6_header_t *ip6; + ip6_frag_hdr_t *frag; + u32 frag_id; + int rv; + + ip4 = vlib_buffer_get_current (p); + + // Deal with fragmented packets + if (PREDICT_FALSE (ip4->flags_and_fragment_offset & + clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS))) + { + ip6 = + (ip6_header_t *) u8_ptr_add (ip4, + sizeof (*ip4) - sizeof (*ip6) - + sizeof (*frag)); + frag = + (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag)); + frag_id = frag_id_4to6 (ip4->fragment_id); + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag)); + } + else + { + ip6 = (ip6_header_t *) (((u8 *) ip4) + sizeof (*ip4) - sizeof (*ip6)); + vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6)); + frag = NULL; + } + + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20)); + ip6->payload_length = u16_net_add (ip4->length, -sizeof (*ip4)); + ip6->hop_limit = ip4->ttl; + ip6->protocol = ip4->protocol; + + if (PREDICT_FALSE (frag != NULL)) + { + frag->next_hdr = ip6->protocol; + frag->identification = frag_id; + frag->rsv = 0; + frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1); + ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION; + ip6->payload_length = u16_net_add (ip6->payload_length, sizeof (*frag)); + } + + if ((rv = fn (ip4, ip6, ctx)) != 0) + return rv; + + return 0; +} + #endif /* __included_ip4_to_ip6_h__ */ /* diff --git a/src/vnet/ip/ip6_to_ip4.h b/src/vnet/ip/ip6_to_ip4.h index 7a0d5349..c14b46c4 100644 --- a/src/vnet/ip/ip6_to_ip4.h +++ b/src/vnet/ip/ip6_to_ip4.h @@ -562,6 +562,67 @@ no_csum: return 0; } +/** + * @brief Translate IPv6 packet to IPv4 (IP header only). + * + * @param p Buffer to translate. + * @param fn The function to translate header. + * @param ctx A context passed in the header translate function. + * + * @returns 0 on success, non-zero value otherwise. + */ +always_inline int +ip6_to_ip4 (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx) +{ + ip6_header_t *ip6; + ip4_header_t *ip4; + u16 fragment_id; + u16 flags; + u16 frag_offset; + u8 l4_protocol; + u16 l4_offset; + int rv; + + ip6 = vlib_buffer_get_current (p); + + if (ip6_parse + (ip6, p->current_length, &l4_protocol, &l4_offset, &frag_offset)) + return -1; + + ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4)); + + vlib_buffer_advance (p, l4_offset - sizeof (*ip4)); + + if (PREDICT_FALSE (frag_offset)) + { + //Only the first fragment + ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset); + fragment_id = frag_id_6to4 (hdr->identification); + flags = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS); + } + else + { + fragment_id = 0; + flags = 0; + } + + if ((rv = fn (ip6, ip4, ctx)) != 0) + return rv; + + ip4->ip_version_and_header_length = + IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS; + ip4->tos = ip6_translate_tos (ip6); + ip4->length = u16_net_add (ip6->payload_length, + sizeof (*ip4) + sizeof (*ip6) - l4_offset); + ip4->fragment_id = fragment_id; + ip4->flags_and_fragment_offset = flags; + ip4->ttl = ip6->hop_limit; + ip4->protocol = l4_protocol; + ip4->checksum = ip4_header_checksum (ip4); + + return 0; +} + #endif /* __included_ip6_to_ip4_h__ */ /* diff --git a/test/test_snat.py b/test/test_snat.py index 9f5377eb..8fd05fa8 100644 --- a/test/test_snat.py +++ b/test/test_snat.py @@ -3497,7 +3497,7 @@ class TestNAT64(MethodHolder): vrf1_pref64_len) self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6) - def _test_unknown_proto(self): + def test_unknown_proto(self): """ NAT64 translate packet with unknown protocol """ self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, @@ -3516,7 +3516,7 @@ class TestNAT64(MethodHolder): p = self.pg1.get_capture(1) p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=self.pg0.remote_ip6, dst=remote_ip6) / + IPv6(src=self.pg0.remote_ip6, dst=remote_ip6, nh=47) / GRE() / IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / TCP(sport=1234, dport=1234)) @@ -3547,13 +3547,13 @@ class TestNAT64(MethodHolder): packet = p[0] try: self.assertEqual(packet[IPv6].src, remote_ip6) - self.assertEqual(packet[IPv6].dst, self.pgi0.remote_ip6) - self.assertTrue(packet.haslayer(GRE)) + self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(packet[IPv6].nh, 47) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise - def _test_hairpinning_unknown_proto(self): + def test_hairpinning_unknown_proto(self): """ NAT64 translate packet with unknown protocol - hairpinning """ client = self.pg0.remote_hosts[0] @@ -3561,23 +3561,40 @@ class TestNAT64(MethodHolder): server_tcp_in_port = 22 server_tcp_out_port = 4022 client_tcp_in_port = 1234 - client_udp_in_port = 1235 - nat_addr_ip6 = self.compose_ip6(self.nat_addr, '64:ff9b::', 96) - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) + client_tcp_out_port = 1235 + server_nat_ip = "10.0.0.100" + client_nat_ip = "10.0.0.110" + server_nat_ip_n = socket.inet_pton(socket.AF_INET, server_nat_ip) + client_nat_ip_n = socket.inet_pton(socket.AF_INET, client_nat_ip) + server_nat_ip6 = self.compose_ip6(server_nat_ip, '64:ff9b::', 96) + client_nat_ip6 = self.compose_ip6(client_nat_ip, '64:ff9b::', 96) + + self.vapi.nat64_add_del_pool_addr_range(server_nat_ip_n, + client_nat_ip_n) self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) self.vapi.nat64_add_del_static_bib(server.ip6n, - self.nat_addr_n, + server_nat_ip_n, server_tcp_in_port, server_tcp_out_port, IP_PROTOS.tcp) + self.vapi.nat64_add_del_static_bib(server.ip6n, + server_nat_ip_n, + 0, + 0, + IP_PROTOS.gre) + + self.vapi.nat64_add_del_static_bib(client.ip6n, + client_nat_ip_n, + client_tcp_in_port, + client_tcp_out_port, + IP_PROTOS.tcp) + # client to server p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=nat_addr_ip6) / + IPv6(src=client.ip6, dst=server_nat_ip6) / TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) @@ -3585,7 +3602,7 @@ class TestNAT64(MethodHolder): p = self.pg0.get_capture(1) p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=nat_addr_ip6) / + IPv6(src=client.ip6, dst=server_nat_ip6, nh=IP_PROTOS.gre) / GRE() / IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / TCP(sport=1234, dport=1234)) @@ -3595,16 +3612,16 @@ class TestNAT64(MethodHolder): p = self.pg0.get_capture(1) packet = p[0] try: - self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].src, client_nat_ip6) self.assertEqual(packet[IPv6].dst, server.ip6) - self.assertTrue(packet.haslayer(GRE)) + self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise # server to client p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=server.ip6, dst=nat_addr_ip6) / + IPv6(src=server.ip6, dst=client_nat_ip6, nh=IP_PROTOS.gre) / GRE() / IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / TCP(sport=1234, dport=1234)) @@ -3614,9 +3631,9 @@ class TestNAT64(MethodHolder): p = self.pg0.get_capture(1) packet = p[0] try: - self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].src, server_nat_ip6) self.assertEqual(packet[IPv6].dst, client.ip6) - self.assertTrue(packet.haslayer(GRE)) + self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -3702,9 +3719,11 @@ class TestNAT64(MethodHolder): self.logger.info(self.vapi.cli("show nat64 bib tcp")) self.logger.info(self.vapi.cli("show nat64 bib udp")) self.logger.info(self.vapi.cli("show nat64 bib icmp")) + self.logger.info(self.vapi.cli("show nat64 bib unknown")) self.logger.info(self.vapi.cli("show nat64 session table tcp")) self.logger.info(self.vapi.cli("show nat64 session table udp")) self.logger.info(self.vapi.cli("show nat64 session table icmp")) + self.logger.info(self.vapi.cli("show nat64 session table unknown")) self.clear_nat64() if __name__ == '__main__': -- cgit 1.2.3-korg