NAT64: Move IPv6-IPv4 virtual reassembly code from MAP-T to common library (VPP-708)
Change-Id: I9ad636f80bf109ffac9ca1b6d80d5f2c31f2076a
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h
new file mode 100644
index 0000000..96b8bf1
--- /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 <vnet/ip/ip.h>
+
+
+/**
+ * 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:
+ */
diff --git a/src/vnet/ip/ip6_to_ip4.h b/src/vnet/ip/ip6_to_ip4.h
new file mode 100644
index 0000000..f5d5688
--- /dev/null
+++ b/src/vnet/ip/ip6_to_ip4.h
@@ -0,0 +1,571 @@
+/*
+ * 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 IPv6 to IPv4 translation
+ */
+#ifndef __included_ip6_to_ip4_h__
+#define __included_ip6_to_ip4_h__
+
+#include <vnet/ip/ip.h>
+
+/**
+ * IPv6 to IPv4 set call back function type
+ */
+typedef int (*ip6_to_ip4_set_fn_t) (ip6_header_t * ip6, ip4_header_t * ip4,
+ void *ctx);
+
+/* *INDENT-OFF* */
+static u8 icmp6_to_icmp_updater_pointer_table[] =
+ { 0, 1, ~0, ~0,
+ 2, 2, 9, 8,
+ 12, 12, 12, 12,
+ 12, 12, 12, 12,
+ 12, 12, 12, 12,
+ 12, 12, 12, 12,
+ 24, 24, 24, 24,
+ 24, 24, 24, 24,
+ 24, 24, 24, 24,
+ 24, 24, 24, 24
+ };
+/* *INDENT-ON* */
+
+#define frag_id_6to4(id) ((id) ^ ((id) >> 16))
+
+/**
+ * @brief Parse some useful information from IPv6 header.
+ *
+ * @param ip6 IPv6 header.
+ * @param buff_len Buffer length.
+ * @param l4_protocol L4 protocol number.
+ * @param l4_offset L4 header offset.
+ * @param frag_hdr_offset Fragment header offset if present, 0 otherwise.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+static_always_inline int
+ip6_parse (const ip6_header_t * ip6, u32 buff_len,
+ u8 * l4_protocol, u16 * l4_offset, u16 * frag_hdr_offset)
+{
+ if (ip6->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION)
+ {
+ *l4_protocol = ((ip6_frag_hdr_t *) (ip6 + 1))->next_hdr;
+ *frag_hdr_offset = sizeof (*ip6);
+ *l4_offset = sizeof (*ip6) + sizeof (ip6_frag_hdr_t);
+ }
+ else
+ {
+ *l4_protocol = ip6->protocol;
+ *frag_hdr_offset = 0;
+ *l4_offset = sizeof (*ip6);
+ }
+
+ return (buff_len < (*l4_offset + 4)) ||
+ (clib_net_to_host_u16 (ip6->payload_length) <
+ (*l4_offset + 4 - sizeof (*ip6)));
+}
+
+/**
+ * @brief Get TCP/UDP port number or ICMP id from IPv6 packet.
+ *
+ * @param ip6 IPv6 header.
+ * @param sender 1 get sender port, 0 get receiver port.
+ * @param buffer_len Buffer length.
+ *
+ * @returns Port number on success, 0 otherwise.
+ */
+always_inline u16
+ip6_get_port (ip6_header_t * ip6, u8 sender, u16 buffer_len)
+{
+ u8 l4_protocol;
+ u16 l4_offset;
+ u16 frag_offset;
+ u8 *l4;
+
+ if (ip6_parse (ip6, buffer_len, &l4_protocol, &l4_offset, &frag_offset))
+ return 0;
+
+ if (frag_offset &&
+ ip6_frag_hdr_offset (((ip6_frag_hdr_t *)
+ u8_ptr_add (ip6, frag_offset))))
+ return 0; //Can't deal with non-first fragment for now
+
+ l4 = u8_ptr_add (ip6, l4_offset);
+ if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP)
+ {
+ return (sender) ? ((udp_header_t *) (l4))->src_port : ((udp_header_t
+ *)
+ (l4))->dst_port;
+ }
+ else if (l4_protocol == IP_PROTOCOL_ICMP6)
+ {
+ icmp46_header_t *icmp = (icmp46_header_t *) (l4);
+ if (icmp->type == ICMP6_echo_request)
+ {
+ return (sender) ? ((u16 *) (icmp))[2] : -1;
+ }
+ else if (icmp->type == ICMP6_echo_reply)
+ {
+ return (sender) ? -1 : ((u16 *) (icmp))[2];
+ }
+ }
+ return 0;
+}
+
+/**
+ * @brief Convert type and code value from ICMP6 to ICMP4.
+ *
+ * @param icmp ICMP header.
+ * @param inner_ip6 Inner IPv6 header if present, 0 otherwise.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+static_always_inline int
+icmp6_to_icmp_header (icmp46_header_t * icmp, ip6_header_t ** inner_ip6)
+{
+ *inner_ip6 = NULL;
+ switch (icmp->type)
+ {
+ case ICMP6_echo_request:
+ icmp->type = ICMP4_echo_request;
+ break;
+ case ICMP6_echo_reply:
+ icmp->type = ICMP4_echo_reply;
+ break;
+ case ICMP6_destination_unreachable:
+ *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
+
+ switch (icmp->code)
+ {
+ case ICMP6_destination_unreachable_no_route_to_destination: //0
+ case ICMP6_destination_unreachable_beyond_scope_of_source_address: //2
+ case ICMP6_destination_unreachable_address_unreachable: //3
+ icmp->type = ICMP4_destination_unreachable;
+ icmp->code =
+ ICMP4_destination_unreachable_destination_unreachable_host;
+ break;
+ case ICMP6_destination_unreachable_destination_administratively_prohibited: //1
+ icmp->type =
+ ICMP4_destination_unreachable;
+ icmp->code =
+ ICMP4_destination_unreachable_communication_administratively_prohibited;
+ break;
+ case ICMP6_destination_unreachable_port_unreachable:
+ icmp->type = ICMP4_destination_unreachable;
+ icmp->code = ICMP4_destination_unreachable_port_unreachable;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case ICMP6_packet_too_big:
+ *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
+
+ icmp->type = ICMP4_destination_unreachable;
+ icmp->code = 4;
+ {
+ u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
+ advertised_mtu -= 20;
+ //FIXME: = minimum(advertised MTU-20, MTU_of_IPv4_nexthop, (MTU_of_IPv6_nexthop)-20)
+ ((u16 *) (icmp))[3] = clib_host_to_net_u16 (advertised_mtu);
+ }
+ break;
+
+ case ICMP6_time_exceeded:
+ *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
+
+ icmp->type = ICMP4_time_exceeded;
+ break;
+
+ case ICMP6_parameter_problem:
+ *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
+
+ switch (icmp->code)
+ {
+ case ICMP6_parameter_problem_erroneous_header_field:
+ icmp->type = ICMP4_parameter_problem;
+ icmp->code = ICMP4_parameter_problem_pointer_indicates_error;
+ u32 pointer = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
+ if (pointer >= 40)
+ return -1;
+
+ ((u8 *) (icmp + 1))[0] =
+ icmp6_to_icmp_updater_pointer_table[pointer];
+ break;
+ case ICMP6_parameter_problem_unrecognized_next_header:
+ icmp->type = ICMP4_destination_unreachable;
+ icmp->code = ICMP4_destination_unreachable_port_unreachable;
+ break;
+ case ICMP6_parameter_problem_unrecognized_option:
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * @brief Translate TOS value from IPv6 to IPv4.
+ *
+ * @param ip6 IPv6 header.
+ *
+ * @returns IPv4 TOS value.
+ */
+static_always_inline u8
+ip6_translate_tos (const ip6_header_t * ip6)
+{
+ return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
+ & 0x0ff00000) >> 20;
+}
+
+/**
+ * @brief Translate ICMP6 packet to ICMP4.
+ *
+ * @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
+icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
+ ip6_to_ip4_set_fn_t inner_fn, void *inner_ctx)
+{
+ ip6_header_t *ip6, *inner_ip6;
+ ip4_header_t *ip4, *inner_ip4;
+ u32 ip6_pay_len;
+ icmp46_header_t *icmp;
+ ip_csum_t csum;
+ int rv;
+
+ ip6 = vlib_buffer_get_current (p);
+ ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length);
+ icmp = (icmp46_header_t *) (ip6 + 1);
+ ASSERT (ip6_pay_len + sizeof (*ip6) <= p->current_length);
+
+ //No extensions headers allowed here
+ if (ip6->protocol != IP_PROTOCOL_ICMP6)
+ return -1;
+
+ //There are no fragmented ICMP messages, so no extension header for now
+ if (icmp6_to_icmp_header (icmp, &inner_ip6))
+ return -1;
+
+ if (inner_ip6)
+ {
+ u16 *inner_L4_checksum, inner_l4_offset, inner_frag_offset,
+ inner_frag_id;
+ u8 *inner_l4, inner_protocol;
+
+ //We have two headers to translate
+ // FROM
+ // [ IPv6 ]<- ext ->[IC][ IPv6 ]<- ext ->[L4 header ...
+ // Handled cases:
+ // [ IPv6 ][IC][ IPv6 ][L4 header ...
+ // [ IPv6 ][IC][ IPv6 ][Fr][L4 header ...
+ // TO
+ // [ IPv4][IC][ IPv4][L4 header ...
+
+ if (ip6_parse (inner_ip6, ip6_pay_len - 8,
+ &inner_protocol, &inner_l4_offset, &inner_frag_offset))
+ return -1;
+
+ inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset);
+ inner_ip4 =
+ (ip4_header_t *) u8_ptr_add (inner_l4, -sizeof (*inner_ip4));
+ if (inner_frag_offset)
+ {
+ ip6_frag_hdr_t *inner_frag =
+ (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, inner_frag_offset);
+ inner_frag_id = frag_id_6to4 (inner_frag->identification);
+ }
+ else
+ {
+ inner_frag_id = 0;
+ }
+
+ //Do the translation of the inner packet
+ if (inner_protocol == IP_PROTOCOL_TCP)
+ {
+ inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 16);
+ }
+ else if (inner_protocol == IP_PROTOCOL_UDP)
+ {
+ inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 6);
+ }
+ 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;
+ }
+ else
+ {
+ return -1;
+ }
+
+ csum = *inner_L4_checksum;
+ csum = ip_csum_sub_even (csum, 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]);
+
+ if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0)
+ return rv;
+
+ inner_ip4->ip_version_and_header_length =
+ IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
+ inner_ip4->tos = ip6_translate_tos (inner_ip6);
+ inner_ip4->length =
+ u16_net_add (inner_ip6->payload_length,
+ sizeof (*ip4) + sizeof (*ip6) - inner_l4_offset);
+ inner_ip4->fragment_id = inner_frag_id;
+ inner_ip4->flags_and_fragment_offset =
+ clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
+ inner_ip4->ttl = inner_ip6->hop_limit;
+ inner_ip4->protocol = inner_protocol;
+ inner_ip4->checksum = ip4_header_checksum (inner_ip4);
+
+ 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));
+ csum =
+ ip_csum_sub_even (csum, inner_ip4->length - sizeof (*inner_ip4));
+ }
+ else
+ {
+ //Update to new pseudo-header
+ 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);
+
+ //Move up icmp header
+ ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8);
+ clib_memcpy (u8_ptr_add (inner_l4, -sizeof (*ip4) - 8), icmp, 8);
+ icmp = (icmp46_header_t *) u8_ptr_add (inner_l4, -sizeof (*ip4) - 8);
+ }
+ else
+ {
+ //Only one header to translate
+ ip4 = (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
+ }
+
+ vlib_buffer_advance (p, (u32) (((u8 *) ip4) - ((u8 *) ip6)));
+
+ 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->fragment_id = 0;
+ ip4->flags_and_fragment_offset = 0;
+ ip4->ttl = ip6->hop_limit;
+ ip4->protocol = IP_PROTOCOL_ICMP;
+ //TODO fix the length depending on offset length
+ ip4->length = u16_net_add (ip6->payload_length,
+ (inner_ip6 ==
+ NULL) ? sizeof (*ip4) : (2 * sizeof (*ip4) -
+ sizeof (*ip6)));
+ ip4->checksum = ip4_header_checksum (ip4);
+
+ //Recompute ICMP checksum
+ icmp->checksum = 0;
+ csum =
+ ip_incremental_checksum (0, icmp,
+ clib_net_to_host_u16 (ip4->length) -
+ sizeof (*ip4));
+ icmp->checksum = ~ip_csum_fold (csum);
+
+ return 0;
+}
+
+/**
+ * @brief Translate IPv6 fragmented packet to IPv4.
+ *
+ * @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_fragmented (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx)
+{
+ ip6_header_t *ip6;
+ ip6_frag_hdr_t *frag;
+ ip4_header_t *ip4;
+ u16 frag_id;
+ u8 frag_more;
+ 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;
+
+ frag = (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset);
+ ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4));
+ vlib_buffer_advance (p, l4_offset - sizeof (*ip4));
+
+ frag_id = frag_id_6to4 (frag->identification);
+ frag_more = ip6_frag_hdr_more (frag);
+ frag_offset = ip6_frag_hdr_offset (frag);
+
+ 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) - l4_offset + sizeof (*ip6));
+ ip4->fragment_id = frag_id;
+ ip4->flags_and_fragment_offset =
+ clib_host_to_net_u16 (frag_offset |
+ (frag_more ? IP4_HEADER_FLAG_MORE_FRAGMENTS : 0));
+ ip4->ttl = ip6->hop_limit;
+ ip4->protocol =
+ (l4_protocol == IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : l4_protocol;
+ ip4->checksum = ip4_header_checksum (ip4);
+
+ return 0;
+}
+
+/**
+ * @brief Translate IPv6 UDP/TCP packet to IPv4.
+ *
+ * @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_tcp_udp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx,
+ u8 udp_checksum)
+{
+ ip6_header_t *ip6;
+ u16 *checksum;
+ ip_csum_t csum;
+ 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;
+
+ if (l4_protocol == IP_PROTOCOL_TCP)
+ {
+ tcp_header_t *tcp = ip6_next_header (ip6);
+ checksum = &tcp->checksum;
+ }
+ else
+ {
+ udp_header_t *udp = ip6_next_header (ip6);
+ checksum = &udp->checksum;
+ //UDP checksum is optional over IPv4
+ if (!udp_checksum)
+ goto no_csum;
+ }
+
+ 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]);
+
+no_csum:
+ 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);
+
+ //UDP checksum is optional over IPv4
+ if (!udp_checksum && l4_protocol == IP_PROTOCOL_UDP)
+ {
+ *checksum = 0;
+ }
+ else
+ {
+ csum = ip_csum_add_even (csum, ip4->dst_address.as_u32);
+ csum = ip_csum_add_even (csum, ip4->src_address.as_u32);
+ *checksum = ip_csum_fold (csum);
+ }
+
+ return 0;
+}
+
+#endif /* __included_ip6_to_ip4_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/map/ip4_map.c b/src/vnet/map/ip4_map.c
index e39b6f1..6a3bdd5 100644
--- a/src/vnet/map/ip4_map.c
+++ b/src/vnet/map/ip4_map.c
@@ -19,6 +19,7 @@
#include "map.h"
#include "../ip/ip_frag.h"
+#include <vnet/ip/ip4_to_ip6.h>
vlib_node_registration_t ip4_map_reass_node;
@@ -62,52 +63,6 @@
t->cached ? "cached" : "forwarded");
}
-/*
- * ip4_map_get_port
- */
-u16
-ip4_map_get_port (ip4_header_t * ip, map_dir_e dir)
-{
- /* Find port information */
- if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
- (ip->protocol == IP_PROTOCOL_UDP)))
- {
- udp_header_t *udp = (void *) (ip + 1);
- return (dir == MAP_SENDER ? udp->src_port : udp->dst_port);
- }
- else if (ip->protocol == IP_PROTOCOL_ICMP)
- {
- /*
- * 1) ICMP Echo request or Echo reply
- * 2) ICMP Error with inner packet being UDP or TCP
- * 3) ICMP Error with inner packet being ICMP Echo request or Echo reply
- */
- 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) >= 56)
- { // IP + ICMP + IP + L4 header
- ip4_header_t *icmp_ip = (ip4_header_t *) (icmp + 2);
- if (PREDICT_TRUE ((icmp_ip->protocol == IP_PROTOCOL_TCP) ||
- (icmp_ip->protocol == IP_PROTOCOL_UDP)))
- {
- udp_header_t *udp = (void *) (icmp_ip + 1);
- return (dir == MAP_SENDER ? udp->dst_port : udp->src_port);
- }
- else if (icmp_ip->protocol == IP_PROTOCOL_ICMP)
- {
- icmp46_header_t *inner_icmp = (void *) (icmp_ip + 1);
- if (inner_icmp->type == ICMP4_echo_request
- || inner_icmp->type == ICMP4_echo_reply)
- return (*((u16 *) (inner_icmp + 1)));
- }
- }
- }
- return (0);
-}
-
static_always_inline u16
ip4_map_port_and_security_check (map_domain_t * d, ip4_header_t * ip,
u32 * next, u8 * error)
@@ -124,7 +79,7 @@
{
return 0;
}
- port = ip4_map_get_port (ip, MAP_RECEIVER);
+ port = ip4_get_port (ip, 0);
if (port)
{
/* Verify that port is not among the well-known ports */
@@ -626,9 +581,7 @@
cached = 1;
}
}
- else
- if ((port0 =
- ip4_get_port (ip40, MAP_RECEIVER, p0->current_length)) < 0)
+ else if ((port0 = ip4_get_port (ip40, 0)) == 0)
{
// Could not find port. We'll free the reassembly.
error0 = MAP_ERROR_BAD_PROTOCOL;
diff --git a/src/vnet/map/ip4_map_t.c b/src/vnet/map/ip4_map_t.c
index 5f2bcbf..b89840c 100644
--- a/src/vnet/map/ip4_map_t.c
+++ b/src/vnet/map/ip4_map_t.c
@@ -15,6 +15,7 @@
#include "map.h"
#include "../ip/ip_frag.h"
+#include <vnet/ip/ip4_to_ip6.h>
#define IP4_MAP_T_DUAL_LOOP 1
@@ -63,18 +64,6 @@
}) ip4_mapt_pseudo_header_t;
/* *INDENT-ON* */
-#define frag_id_4to6(id) (id)
-
-//TODO: Find the right place in memory for this.
-/* *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* */
-
static_always_inline int
ip4_map_fragment_cache (ip4_header_t * ip4, u16 port)
@@ -110,360 +99,41 @@
return ret;
}
-
-/* Statelessly translates an ICMP packet into ICMPv6.
- *
- * Warning: The checksum will need to be recomputed.
- *
- */
-static_always_inline int
-ip4_icmp_to_icmp6_in_place (icmp46_header_t * icmp, u32 icmp_len,
- i32 * receiver_port, ip4_header_t ** inner_ip4)
+typedef struct
{
- *inner_ip4 = NULL;
- switch (icmp->type)
- {
- case ICMP4_echo_reply:
- *receiver_port = ((u16 *) icmp)[2];
- icmp->type = ICMP6_echo_reply;
- break;
- case ICMP4_echo_request:
- *receiver_port = ((u16 *) icmp)[2];
- icmp->type = ICMP6_echo_request;
- break;
- case ICMP4_destination_unreachable:
- *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
- *receiver_port = ip4_get_port (*inner_ip4, MAP_SENDER, icmp_len - 8);
+ map_domain_t *d;
+ u16 recv_port;
+} icmp_to_icmp6_ctx_t;
- 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)
+static int
+ip4_to_ip6_set_icmp_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
+{
+ icmp_to_icmp6_ctx_t *ctx = arg;
- //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;
+ ip4_map_t_embedded_address (ctx->d, &ip6->src_address, &ip4->src_address);
+ ip6->dst_address.as_u64[0] =
+ map_get_pfx_net (ctx->d, ip4->dst_address.as_u32, ctx->recv_port);
+ ip6->dst_address.as_u64[1] =
+ map_get_sfx_net (ctx->d, ip4->dst_address.as_u32, ctx->recv_port);
- 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);
- *receiver_port = ip4_get_port (*inner_ip4, MAP_SENDER, icmp_len - 8);
- icmp->type = ICMP6_time_exceeded;
- //icmp->code = icmp->code //unchanged
- break;
-
- case ICMP4_parameter_problem:
- *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
- *receiver_port = ip4_get_port (*inner_ip4, MAP_SENDER, icmp_len - 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 dropping the packet
- return -1;
- }
- break;
-
- default:
- //All other types cause dropping the packet
- return -1;
- break;
- }
return 0;
}
-static_always_inline void
-_ip4_map_t_icmp (map_domain_t * d, vlib_buffer_t * p, u8 * error)
+static int
+ip4_to_ip6_set_inner_icmp_cb (ip4_header_t * ip4, ip6_header_t * ip6,
+ void *arg)
{
- ip4_header_t *ip4, *inner_ip4;
- ip6_header_t *ip6, *inner_ip6;
- u32 ip_len;
- icmp46_header_t *icmp;
- i32 recv_port;
- ip_csum_t csum;
- u16 *inner_L4_checksum = 0;
- ip6_frag_hdr_t *inner_frag;
- u32 inner_frag_id;
- u32 inner_frag_offset;
- u8 inner_frag_more;
+ icmp_to_icmp6_ctx_t *ctx = arg;
- ip4 = vlib_buffer_get_current (p);
- ip_len = clib_net_to_host_u16 (ip4->length);
- ASSERT (ip_len <= p->current_length);
+ //Note that the source address is within the domain
+ //while the destination address is the one outside the domain
+ ip4_map_t_embedded_address (ctx->d, &ip6->dst_address, &ip4->dst_address);
+ ip6->src_address.as_u64[0] =
+ map_get_pfx_net (ctx->d, ip4->src_address.as_u32, ctx->recv_port);
+ ip6->src_address.as_u64[1] =
+ map_get_sfx_net (ctx->d, ip4->src_address.as_u32, ctx->recv_port);
- icmp = (icmp46_header_t *) (ip4 + 1);
- if (ip4_icmp_to_icmp6_in_place (icmp, ip_len - sizeof (*ip4),
- &recv_port, &inner_ip4))
- {
- *error = MAP_ERROR_ICMP;
- return;
- }
-
- if (recv_port < 0)
- {
- // In case of 1:1 mapping, we don't care about the port
- if (d->ea_bits_len == 0 && d->rules)
- {
- recv_port = 0;
- }
- else
- {
- *error = MAP_ERROR_ICMP;
- return;
- }
- }
-
- 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
- *error = MAP_ERROR_FRAGMENTED;
- return;
- }
-
- 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)
- {
- //The inner packet was first translated, and therefore came from IPv6.
- //As the packet was an IPv6 packet, the UDP checksum can't be NULL
- *error = MAP_ERROR_ICMP;
- return;
- }
- *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 ip4_icmp_to_icmp6_in_place
- 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 ();
- }
-
- //FIXME: Security check with the port found in the inner packet
-
- csum = *inner_L4_checksum; //Initial checksum of the inner L4 header
- //FIXME: Shouldn't we remove ip addresses from there ?
-
- 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;
-
- //Note that the source address is within the domain
- //while the destination address is the one outside the domain
- ip4_map_t_embedded_address (d, &inner_ip6->dst_address,
- &inner_ip4->dst_address);
- inner_ip6->src_address.as_u64[0] =
- map_get_pfx_net (d, inner_ip4->src_address.as_u32, recv_port);
- inner_ip6->src_address.as_u64[1] =
- map_get_sfx_net (d, inner_ip4->src_address.as_u32, recv_port);
-
- 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;
-
- ip4_map_t_embedded_address (d, &ip6->src_address, &ip4->src_address);
- ip6->dst_address.as_u64[0] =
- map_get_pfx_net (d, ip4->dst_address.as_u32, recv_port);
- ip6->dst_address.as_u64[1] =
- map_get_sfx_net (d, ip4->dst_address.as_u32, recv_port);
-
- //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...
- }
-
- //TODO: We could do an easy diff-checksum for echo requests/replies
- //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;
}
static uword
@@ -491,6 +161,8 @@
u8 error0;
map_domain_t *d0;
u16 len0;
+ icmp_to_icmp6_ctx_t ctx0;
+ ip4_header_t *ip40;
next0 = IP4_MAPT_ICMP_NEXT_IP6_LOOKUP;
pi0 = to_next[0] = from[0];
@@ -508,7 +180,27 @@
d0 =
pool_elt_at_index (map_main.domains,
vnet_buffer (p0)->map_t.map_domain_index);
- _ip4_map_t_icmp (d0, p0, &error0);
+
+ ip40 = vlib_buffer_get_current (p0);
+ ctx0.recv_port = ip4_get_port (ip40, 1);
+ ctx0.d = d0;
+ if (ctx0.recv_port == 0)
+ {
+ // In case of 1:1 mapping, we don't care about the port
+ if (!(d0->ea_bits_len == 0 && d0->rules))
+ {
+ error0 = MAP_ERROR_ICMP;
+ goto err0;
+ }
+ }
+
+ if (icmp_to_icmp6
+ (p0, ip4_to_ip6_set_icmp_cb, &ctx0,
+ ip4_to_ip6_set_inner_icmp_cb, &ctx0))
+ {
+ error0 = MAP_ERROR_ICMP;
+ goto err0;
+ }
if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
{
@@ -517,12 +209,14 @@
vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
next0 = IP4_MAPT_ICMP_NEXT_IP6_FRAG;
}
+ err0:
if (PREDICT_TRUE (error0 == MAP_ERROR_NONE))
{
vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
thread_index,
- vnet_buffer (p0)->map_t.
- map_domain_index, 1, len0);
+ vnet_buffer (p0)->
+ map_t.map_domain_index, 1,
+ len0);
}
else
{
@@ -538,6 +232,19 @@
return frame->n_vectors;
}
+static int
+ip4_to_ip6_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *ctx)
+{
+ ip4_mapt_pseudo_header_t *pheader = ctx;
+
+ ip6->dst_address.as_u64[0] = pheader->daddr.as_u64[0];
+ ip6->dst_address.as_u64[1] = pheader->daddr.as_u64[1];
+ ip6->src_address.as_u64[0] = pheader->saddr.as_u64[0];
+ ip6->src_address.as_u64[1] = pheader->saddr.as_u64[1];
+
+ return 0;
+}
+
static uword
ip4_map_t_fragmented (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
@@ -546,6 +253,8 @@
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip4_map_t_fragmented_node.index);
while (n_left_from > 0)
{
@@ -555,9 +264,6 @@
{
u32 pi0;
vlib_buffer_t *p0;
- ip4_header_t *ip40;
- ip6_header_t *ip60;
- ip6_frag_hdr_t *frag0;
ip4_mapt_pseudo_header_t *pheader0;
ip4_mapt_fragmented_next_t next0;
@@ -574,50 +280,21 @@
pheader0 = vlib_buffer_get_current (p0);
vlib_buffer_advance (p0, sizeof (*pheader0));
- //Accessing ip4 header
- ip40 = vlib_buffer_get_current (p0);
- frag0 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip40,
- sizeof (*ip40) - sizeof (*frag0));
- ip60 =
- (ip6_header_t *) u8_ptr_add (ip40,
- sizeof (*ip40) - sizeof (*frag0) -
- sizeof (*ip60));
- vlib_buffer_advance (p0,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
-
- //We know that the protocol was one of ICMP, TCP or UDP
- //because the first fragment was found and cached
- frag0->next_hdr =
- (ip40->protocol ==
- IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip40->protocol;
- frag0->identification = frag_id_4to6 (ip40->fragment_id);
- frag0->rsv = 0;
- frag0->fragment_offset_and_more =
- ip6_frag_hdr_offset_and_more (ip4_get_fragment_offset (ip40),
- clib_net_to_host_u16
- (ip40->flags_and_fragment_offset) &
- IP4_HEADER_FLAG_MORE_FRAGMENTS);
-
- ip60->ip_version_traffic_class_and_flow_label =
- clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
- ip60->payload_length =
- clib_host_to_net_u16 (clib_net_to_host_u16 (ip40->length) -
- sizeof (*ip40) + sizeof (*frag0));
- ip60->hop_limit = ip40->ttl;
- ip60->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
- ip60->dst_address.as_u64[0] = pheader0->daddr.as_u64[0];
- ip60->dst_address.as_u64[1] = pheader0->daddr.as_u64[1];
- ip60->src_address.as_u64[0] = pheader0->saddr.as_u64[0];
- ip60->src_address.as_u64[1] = pheader0->saddr.as_u64[1];
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ if (ip4_to_ip6_fragmented (p0, ip4_to_ip6_set_cb, pheader0))
{
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
- next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG;
+ p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
+ next0 = IP4_MAPT_FRAGMENTED_NEXT_DROP;
+ }
+ else
+ {
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP6_FRAG_NEXT_IP6_LOOKUP;
+ next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -637,6 +314,9 @@
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip4_map_t_tcp_udp_node.index);
+
while (n_left_from > 0)
{
@@ -647,12 +327,6 @@
{
u32 pi0, pi1;
vlib_buffer_t *p0, *p1;
- ip4_header_t *ip40, *ip41;
- ip6_header_t *ip60, *ip61;
- ip_csum_t csum0, csum1;
- u16 *checksum0, *checksum1;
- ip6_frag_hdr_t *frag0, *frag1;
- u32 frag_id0, frag_id1;
ip4_mapt_pseudo_header_t *pheader0, *pheader1;
ip4_mapt_tcp_udp_next_t next0, next1;
@@ -674,183 +348,40 @@
vlib_buffer_advance (p0, sizeof (*pheader0));
vlib_buffer_advance (p1, sizeof (*pheader1));
- //Accessing ip4 header
- ip40 = vlib_buffer_get_current (p0);
- ip41 = vlib_buffer_get_current (p1);
- checksum0 =
- (u16 *) u8_ptr_add (ip40,
- vnet_buffer (p0)->map_t.checksum_offset);
- checksum1 =
- (u16 *) u8_ptr_add (ip41,
- vnet_buffer (p1)->map_t.checksum_offset);
-
- //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
- (!*checksum0 && ip40->protocol == IP_PROTOCOL_UDP))
+ if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
{
- u16 udp_len =
- clib_host_to_net_u16 (ip40->length) - sizeof (*ip40);
- udp_header_t *udp =
- (udp_header_t *) u8_ptr_add (ip40, sizeof (*ip40));
- ip_csum_t csum;
- 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 *) (&ip40->src_address)));
- *checksum0 = ~ip_csum_fold (csum);
- }
- if (PREDICT_FALSE
- (!*checksum1 && ip41->protocol == IP_PROTOCOL_UDP))
- {
- u16 udp_len =
- clib_host_to_net_u16 (ip41->length) - sizeof (*ip40);
- udp_header_t *udp =
- (udp_header_t *) u8_ptr_add (ip41, sizeof (*ip40));
- ip_csum_t csum;
- 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 *) (&ip41->src_address)));
- *checksum1 = ~ip_csum_fold (csum);
- }
-
- csum0 = ip_csum_sub_even (*checksum0, ip40->src_address.as_u32);
- csum1 = ip_csum_sub_even (*checksum1, ip41->src_address.as_u32);
- csum0 = ip_csum_sub_even (csum0, ip40->dst_address.as_u32);
- csum1 = ip_csum_sub_even (csum1, ip41->dst_address.as_u32);
-
- // Deal with fragmented packets
- if (PREDICT_FALSE (ip40->flags_and_fragment_offset &
- clib_host_to_net_u16
- (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
- {
- ip60 =
- (ip6_header_t *) u8_ptr_add (ip40,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
- frag0 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip40,
- sizeof (*ip40) -
- sizeof (*frag0));
- frag_id0 = frag_id_4to6 (ip40->fragment_id);
- vlib_buffer_advance (p0,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
+ p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- ip60 =
- (ip6_header_t *) (((u8 *) ip40) + sizeof (*ip40) -
- sizeof (*ip60));
- vlib_buffer_advance (p0, sizeof (*ip40) - sizeof (*ip60));
- frag0 = NULL;
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP6_FRAG_NEXT_IP6_LOOKUP;
+ next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
+ }
}
- if (PREDICT_FALSE (ip41->flags_and_fragment_offset &
- clib_host_to_net_u16
- (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
+ if (ip4_to_ip6_tcp_udp (p1, ip4_to_ip6_set_cb, pheader1))
{
- ip61 =
- (ip6_header_t *) u8_ptr_add (ip41,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
- frag1 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip41,
- sizeof (*ip40) -
- sizeof (*frag0));
- frag_id1 = frag_id_4to6 (ip41->fragment_id);
- vlib_buffer_advance (p1,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
+ p1->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next1 = IP4_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- ip61 =
- (ip6_header_t *) (((u8 *) ip41) + sizeof (*ip40) -
- sizeof (*ip60));
- vlib_buffer_advance (p1, sizeof (*ip40) - sizeof (*ip60));
- frag1 = NULL;
- }
-
- ip60->ip_version_traffic_class_and_flow_label =
- clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
- ip61->ip_version_traffic_class_and_flow_label =
- clib_host_to_net_u32 ((6 << 28) + (ip41->tos << 20));
- ip60->payload_length = u16_net_add (ip40->length, -sizeof (*ip40));
- ip61->payload_length = u16_net_add (ip41->length, -sizeof (*ip40));
- ip60->hop_limit = ip40->ttl;
- ip61->hop_limit = ip41->ttl;
- ip60->protocol = ip40->protocol;
- ip61->protocol = ip41->protocol;
-
- if (PREDICT_FALSE (frag0 != NULL))
- {
- frag0->next_hdr = ip60->protocol;
- frag0->identification = frag_id0;
- frag0->rsv = 0;
- frag0->fragment_offset_and_more =
- ip6_frag_hdr_offset_and_more (0, 1);
- ip60->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
- ip60->payload_length =
- u16_net_add (ip60->payload_length, sizeof (*frag0));
- }
-
- if (PREDICT_FALSE (frag1 != NULL))
- {
- frag1->next_hdr = ip61->protocol;
- frag1->identification = frag_id1;
- frag1->rsv = 0;
- frag1->fragment_offset_and_more =
- ip6_frag_hdr_offset_and_more (0, 1);
- ip61->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
- ip61->payload_length =
- u16_net_add (ip61->payload_length, sizeof (*frag0));
- }
-
- //Finally copying the address
- ip60->dst_address.as_u64[0] = pheader0->daddr.as_u64[0];
- ip61->dst_address.as_u64[0] = pheader1->daddr.as_u64[0];
- ip60->dst_address.as_u64[1] = pheader0->daddr.as_u64[1];
- ip61->dst_address.as_u64[1] = pheader1->daddr.as_u64[1];
- ip60->src_address.as_u64[0] = pheader0->saddr.as_u64[0];
- ip61->src_address.as_u64[0] = pheader1->saddr.as_u64[0];
- ip60->src_address.as_u64[1] = pheader0->saddr.as_u64[1];
- ip61->src_address.as_u64[1] = pheader1->saddr.as_u64[1];
-
- csum0 = ip_csum_add_even (csum0, ip60->src_address.as_u64[0]);
- csum1 = ip_csum_add_even (csum1, ip61->src_address.as_u64[0]);
- csum0 = ip_csum_add_even (csum0, ip60->src_address.as_u64[1]);
- csum1 = ip_csum_add_even (csum1, ip61->src_address.as_u64[1]);
- csum0 = ip_csum_add_even (csum0, ip60->dst_address.as_u64[0]);
- csum1 = ip_csum_add_even (csum1, ip61->dst_address.as_u64[0]);
- csum0 = ip_csum_add_even (csum0, ip60->dst_address.as_u64[1]);
- csum1 = ip_csum_add_even (csum1, ip61->dst_address.as_u64[1]);
- *checksum0 = ip_csum_fold (csum0);
- *checksum1 = ip_csum_fold (csum1);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
- {
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
- next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
- }
-
- if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
- {
- vnet_buffer (p1)->ip_frag.header_offset = 0;
- vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
- vnet_buffer (p1)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
- next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
+ if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p1)->ip_frag.header_offset = 0;
+ vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
+ vnet_buffer (p1)->ip_frag.next_index =
+ IP6_FRAG_NEXT_IP6_LOOKUP;
+ next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -863,12 +394,6 @@
{
u32 pi0;
vlib_buffer_t *p0;
- ip4_header_t *ip40;
- ip6_header_t *ip60;
- ip_csum_t csum0;
- u16 *checksum0;
- ip6_frag_hdr_t *frag0;
- u32 frag_id0;
ip4_mapt_pseudo_header_t *pheader0;
ip4_mapt_tcp_udp_next_t next0;
@@ -885,102 +410,23 @@
pheader0 = vlib_buffer_get_current (p0);
vlib_buffer_advance (p0, sizeof (*pheader0));
- //Accessing ip4 header
- ip40 = vlib_buffer_get_current (p0);
- checksum0 =
- (u16 *) u8_ptr_add (ip40,
- vnet_buffer (p0)->map_t.checksum_offset);
-
- //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
- (!*checksum0 && ip40->protocol == IP_PROTOCOL_UDP))
+ if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
{
- u16 udp_len =
- clib_host_to_net_u16 (ip40->length) - sizeof (*ip40);
- udp_header_t *udp =
- (udp_header_t *) u8_ptr_add (ip40, sizeof (*ip40));
- ip_csum_t csum;
- 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 *) (&ip40->src_address)));
- *checksum0 = ~ip_csum_fold (csum);
- }
-
- csum0 = ip_csum_sub_even (*checksum0, ip40->src_address.as_u32);
- csum0 = ip_csum_sub_even (csum0, ip40->dst_address.as_u32);
-
- // Deal with fragmented packets
- if (PREDICT_FALSE (ip40->flags_and_fragment_offset &
- clib_host_to_net_u16
- (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
- {
- ip60 =
- (ip6_header_t *) u8_ptr_add (ip40,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
- frag0 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip40,
- sizeof (*ip40) -
- sizeof (*frag0));
- frag_id0 = frag_id_4to6 (ip40->fragment_id);
- vlib_buffer_advance (p0,
- sizeof (*ip40) - sizeof (*ip60) -
- sizeof (*frag0));
+ p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- ip60 =
- (ip6_header_t *) (((u8 *) ip40) + sizeof (*ip40) -
- sizeof (*ip60));
- vlib_buffer_advance (p0, sizeof (*ip40) - sizeof (*ip60));
- frag0 = NULL;
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP6_FRAG_NEXT_IP6_LOOKUP;
+ next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
+ }
}
-
- ip60->ip_version_traffic_class_and_flow_label =
- clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
- ip60->payload_length = u16_net_add (ip40->length, -sizeof (*ip40));
- ip60->hop_limit = ip40->ttl;
- ip60->protocol = ip40->protocol;
-
- if (PREDICT_FALSE (frag0 != NULL))
- {
- frag0->next_hdr = ip60->protocol;
- frag0->identification = frag_id0;
- frag0->rsv = 0;
- frag0->fragment_offset_and_more =
- ip6_frag_hdr_offset_and_more (0, 1);
- ip60->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
- ip60->payload_length =
- u16_net_add (ip60->payload_length, sizeof (*frag0));
- }
-
- //Finally copying the address
- ip60->dst_address.as_u64[0] = pheader0->daddr.as_u64[0];
- ip60->dst_address.as_u64[1] = pheader0->daddr.as_u64[1];
- ip60->src_address.as_u64[0] = pheader0->saddr.as_u64[0];
- ip60->src_address.as_u64[1] = pheader0->saddr.as_u64[1];
-
- csum0 = ip_csum_add_even (csum0, ip60->src_address.as_u64[0]);
- csum0 = ip_csum_add_even (csum0, ip60->src_address.as_u64[1]);
- csum0 = ip_csum_add_even (csum0, ip60->dst_address.as_u64[0]);
- csum0 = ip_csum_add_even (csum0, ip60->dst_address.as_u64[1]);
- *checksum0 = ip_csum_fold (csum0);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
- {
- //Send to fragmentation node if necessary
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
- next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
- }
-
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next, pi0,
next0);
@@ -1159,10 +605,10 @@
{
vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
thread_index,
- vnet_buffer (p0)->map_t.
- map_domain_index, 1,
- clib_net_to_host_u16 (ip40->
- length));
+ vnet_buffer (p0)->
+ map_t.map_domain_index, 1,
+ clib_net_to_host_u16
+ (ip40->length));
}
if (PREDICT_TRUE
@@ -1170,10 +616,10 @@
{
vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
thread_index,
- vnet_buffer (p1)->map_t.
- map_domain_index, 1,
- clib_net_to_host_u16 (ip41->
- length));
+ vnet_buffer (p1)->
+ map_t.map_domain_index, 1,
+ clib_net_to_host_u16
+ (ip41->length));
}
next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
@@ -1253,10 +699,10 @@
{
vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
thread_index,
- vnet_buffer (p0)->map_t.
- map_domain_index, 1,
- clib_net_to_host_u16 (ip40->
- length));
+ vnet_buffer (p0)->
+ map_t.map_domain_index, 1,
+ clib_net_to_host_u16
+ (ip40->length));
}
next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
diff --git a/src/vnet/map/ip6_map.c b/src/vnet/map/ip6_map.c
index 63ada96..720d13c 100644
--- a/src/vnet/map/ip6_map.c
+++ b/src/vnet/map/ip6_map.c
@@ -15,6 +15,8 @@
#include "map.h"
#include "../ip/ip_frag.h"
+#include <vnet/ip/ip4_to_ip6.h>
+#include <vnet/ip/ip6_to_ip4.h>
enum ip6_map_next_e
{
@@ -125,7 +127,7 @@
{
if (!ip4_is_fragment (ip4))
{
- u16 port = ip4_map_get_port (ip4, MAP_SENDER);
+ u16 port = ip4_get_port (ip4, 1);
if (port)
{
if (mm->sec_check)
@@ -243,8 +245,9 @@
{
d0 =
ip6_map_get_domain (vnet_buffer (p0)->ip.adj_index[VLIB_TX],
- (ip4_address_t *) & ip40->src_address.
- as_u32, &map_domain_index0, &error0);
+ (ip4_address_t *) & ip40->
+ src_address.as_u32, &map_domain_index0,
+ &error0);
}
else if (ip60->protocol == IP_PROTOCOL_ICMP6 &&
clib_net_to_host_u16 (ip60->payload_length) >
@@ -270,8 +273,9 @@
{
d1 =
ip6_map_get_domain (vnet_buffer (p1)->ip.adj_index[VLIB_TX],
- (ip4_address_t *) & ip41->src_address.
- as_u32, &map_domain_index1, &error1);
+ (ip4_address_t *) & ip41->
+ src_address.as_u32, &map_domain_index1,
+ &error1);
}
else if (ip61->protocol == IP_PROTOCOL_ICMP6 &&
clib_net_to_host_u16 (ip61->payload_length) >
@@ -454,8 +458,9 @@
{
d0 =
ip6_map_get_domain (vnet_buffer (p0)->ip.adj_index[VLIB_TX],
- (ip4_address_t *) & ip40->src_address.
- as_u32, &map_domain_index0, &error0);
+ (ip4_address_t *) & ip40->
+ src_address.as_u32, &map_domain_index0,
+ &error0);
}
else if (ip60->protocol == IP_PROTOCOL_ICMP6 &&
clib_net_to_host_u16 (ip60->payload_length) >
@@ -891,9 +896,7 @@
cached = 1;
}
}
- else
- if ((port0 =
- ip4_get_port (ip40, MAP_SENDER, p0->current_length)) < 0)
+ else if ((port0 = ip4_get_port (ip40, 1)) == 0)
{
// Could not find port from first fragment. Stop reassembling.
error0 = MAP_ERROR_BAD_PROTOCOL;
diff --git a/src/vnet/map/ip6_map_t.c b/src/vnet/map/ip6_map_t.c
index 9915167..b173bb2 100644
--- a/src/vnet/map/ip6_map_t.c
+++ b/src/vnet/map/ip6_map_t.c
@@ -15,6 +15,8 @@
#include "map.h"
#include "../ip/ip_frag.h"
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/ip/ip4_to_ip6.h>
#define IP6_MAP_T_DUAL_LOOP
@@ -94,347 +96,55 @@
return ret;
}
-static_always_inline u8
-ip6_translate_tos (const ip6_header_t * ip6)
+typedef struct
{
-#ifdef IP6_MAP_T_OVERRIDE_TOS
- return IP6_MAP_T_OVERRIDE_TOS;
-#else
- return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label)
- & 0x0ff00000) >> 20;
-#endif
-}
+ map_domain_t *d;
+ u16 sender_port;
+} icmp6_to_icmp_ctx_t;
-//TODO: Find right place in memory for that
-/* *INDENT-OFF* */
-static u8 icmp6_to_icmp_updater_pointer_table[] =
- { 0, 1, ~0, ~0,
- 2, 2, 9, 8,
- 12, 12, 12, 12,
- 12, 12, 12, 12,
- 12, 12, 12, 12,
- 12, 12, 12, 12,
- 24, 24, 24, 24,
- 24, 24, 24, 24,
- 24, 24, 24, 24,
- 24, 24, 24, 24
- };
-/* *INDENT-ON* */
-
-static_always_inline int
-ip6_icmp_to_icmp6_in_place (icmp46_header_t * icmp, u32 icmp_len,
- i32 * sender_port, ip6_header_t ** inner_ip6)
+static int
+ip6_to_ip4_set_icmp_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
{
- *inner_ip6 = NULL;
- switch (icmp->type)
- {
- case ICMP6_echo_request:
- *sender_port = ((u16 *) icmp)[2];
- icmp->type = ICMP4_echo_request;
- break;
- case ICMP6_echo_reply:
- *sender_port = ((u16 *) icmp)[2];
- icmp->type = ICMP4_echo_reply;
- break;
- case ICMP6_destination_unreachable:
- *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
- *sender_port = ip6_get_port (*inner_ip6, MAP_RECEIVER, icmp_len);
-
- switch (icmp->code)
- {
- case ICMP6_destination_unreachable_no_route_to_destination: //0
- case ICMP6_destination_unreachable_beyond_scope_of_source_address: //2
- case ICMP6_destination_unreachable_address_unreachable: //3
- icmp->type = ICMP4_destination_unreachable;
- icmp->code =
- ICMP4_destination_unreachable_destination_unreachable_host;
- break;
- case ICMP6_destination_unreachable_destination_administratively_prohibited: //1
- icmp->type =
- ICMP4_destination_unreachable;
- icmp->code =
- ICMP4_destination_unreachable_communication_administratively_prohibited;
- break;
- case ICMP6_destination_unreachable_port_unreachable:
- icmp->type = ICMP4_destination_unreachable;
- icmp->code = ICMP4_destination_unreachable_port_unreachable;
- break;
- default:
- return -1;
- }
- break;
- case ICMP6_packet_too_big:
- *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
- *sender_port = ip6_get_port (*inner_ip6, MAP_RECEIVER, icmp_len);
-
- icmp->type = ICMP4_destination_unreachable;
- icmp->code = 4;
- {
- u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
- advertised_mtu -= 20;
- //FIXME: = minimum(advertised MTU-20, MTU_of_IPv4_nexthop, (MTU_of_IPv6_nexthop)-20)
- ((u16 *) (icmp))[3] = clib_host_to_net_u16 (advertised_mtu);
- }
- break;
-
- case ICMP6_time_exceeded:
- *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
- *sender_port = ip6_get_port (*inner_ip6, MAP_RECEIVER, icmp_len);
-
- icmp->type = ICMP4_time_exceeded;
- break;
-
- case ICMP6_parameter_problem:
- *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
- *sender_port = ip6_get_port (*inner_ip6, MAP_RECEIVER, icmp_len);
-
- switch (icmp->code)
- {
- case ICMP6_parameter_problem_erroneous_header_field:
- icmp->type = ICMP4_parameter_problem;
- icmp->code = ICMP4_parameter_problem_pointer_indicates_error;
- u32 pointer = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
- if (pointer >= 40)
- return -1;
-
- ((u8 *) (icmp + 1))[0] =
- icmp6_to_icmp_updater_pointer_table[pointer];
- break;
- case ICMP6_parameter_problem_unrecognized_next_header:
- icmp->type = ICMP4_destination_unreachable;
- icmp->code = ICMP4_destination_unreachable_port_unreachable;
- break;
- case ICMP6_parameter_problem_unrecognized_option:
- default:
- return -1;
- }
- break;
- default:
- return -1;
- break;
- }
- return 0;
-}
-
-static_always_inline void
-_ip6_map_t_icmp (map_domain_t * d, vlib_buffer_t * p, u8 * error)
-{
- ip6_header_t *ip6, *inner_ip6;
- ip4_header_t *ip4, *inner_ip4;
- u32 ip6_pay_len;
- icmp46_header_t *icmp;
- i32 sender_port;
- ip_csum_t csum;
- u32 ip4_sadr, inner_ip4_dadr;
-
- ip6 = vlib_buffer_get_current (p);
- ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length);
- icmp = (icmp46_header_t *) (ip6 + 1);
- ASSERT (ip6_pay_len + sizeof (*ip6) <= p->current_length);
-
- if (ip6->protocol != IP_PROTOCOL_ICMP6)
- {
- //No extensions headers allowed here
- //TODO: SR header
- *error = MAP_ERROR_MALFORMED;
- return;
- }
-
- //There are no fragmented ICMP messages, so no extension header for now
-
- if (ip6_icmp_to_icmp6_in_place
- (icmp, ip6_pay_len, &sender_port, &inner_ip6))
- {
- //TODO: In case of 1:1 mapping it is not necessary to have the sender port
- *error = MAP_ERROR_ICMP;
- return;
- }
-
- if (sender_port < 0)
- {
- // In case of 1:1 mapping, we don't care about the port
- if (d->ea_bits_len == 0 && d->rules)
- {
- sender_port = 0;
- }
- else
- {
- *error = MAP_ERROR_ICMP;
- return;
- }
- }
+ icmp6_to_icmp_ctx_t *ctx = arg;
+ u32 ip4_sadr;
//Security check
//Note that this prevents an intermediate IPv6 router from answering the request
ip4_sadr = map_get_ip4 (&ip6->src_address);
- if (ip6->src_address.as_u64[0] != map_get_pfx_net (d, ip4_sadr, sender_port)
- || ip6->src_address.as_u64[1] != map_get_sfx_net (d, ip4_sadr,
- sender_port))
- {
- *error = MAP_ERROR_SEC_CHECK;
- return;
- }
+ if (ip6->src_address.as_u64[0] !=
+ map_get_pfx_net (ctx->d, ip4_sadr, ctx->sender_port)
+ || ip6->src_address.as_u64[1] != map_get_sfx_net (ctx->d, ip4_sadr,
+ ctx->sender_port))
+ return -1;
- if (inner_ip6)
- {
- u16 *inner_L4_checksum, inner_l4_offset, inner_frag_offset,
- inner_frag_id;
- u8 *inner_l4, inner_protocol;
-
- //We have two headers to translate
- // FROM
- // [ IPv6 ]<- ext ->[IC][ IPv6 ]<- ext ->[L4 header ...
- // Handled cases:
- // [ IPv6 ][IC][ IPv6 ][L4 header ...
- // [ IPv6 ][IC][ IPv6 ][Fr][L4 header ...
- // TO
- // [ IPv4][IC][ IPv4][L4 header ...
-
- //TODO: This was already done deep in ip6_icmp_to_icmp6_in_place
- //We shouldn't have to do it again
- if (ip6_parse (inner_ip6, ip6_pay_len - 8,
- &inner_protocol, &inner_l4_offset, &inner_frag_offset))
- {
- *error = MAP_ERROR_MALFORMED;
- return;
- }
-
- inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset);
- inner_ip4 =
- (ip4_header_t *) u8_ptr_add (inner_l4, -sizeof (*inner_ip4));
- if (inner_frag_offset)
- {
- ip6_frag_hdr_t *inner_frag =
- (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, inner_frag_offset);
- inner_frag_id = frag_id_6to4 (inner_frag->identification);
- }
- else
- {
- inner_frag_id = 0;
- }
-
- //Do the translation of the inner packet
- if (inner_protocol == IP_PROTOCOL_TCP)
- {
- inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 16);
- }
- else if (inner_protocol == IP_PROTOCOL_UDP)
- {
- inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 6);
- }
- 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;
- }
- else
- {
- *error = MAP_ERROR_BAD_PROTOCOL;
- return;
- }
-
- csum = *inner_L4_checksum;
- csum = ip_csum_sub_even (csum, 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]);
-
- //Sanity check of the outer destination address
- if (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])
- {
- *error = MAP_ERROR_SEC_CHECK;
- return;
- }
-
- //Security check of inner packet
- inner_ip4_dadr = map_get_ip4 (&inner_ip6->dst_address);
- if (inner_ip6->dst_address.as_u64[0] !=
- map_get_pfx_net (d, inner_ip4_dadr, sender_port)
- || inner_ip6->dst_address.as_u64[1] != map_get_sfx_net (d,
- inner_ip4_dadr,
- sender_port))
- {
- *error = MAP_ERROR_SEC_CHECK;
- return;
- }
-
- inner_ip4->dst_address.as_u32 = inner_ip4_dadr;
- inner_ip4->src_address.as_u32 =
- ip6_map_t_embedded_address (d, &inner_ip6->src_address);
- inner_ip4->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- inner_ip4->tos = ip6_translate_tos (inner_ip6);
- inner_ip4->length =
- u16_net_add (inner_ip6->payload_length,
- sizeof (*ip4) + sizeof (*ip6) - inner_l4_offset);
- inner_ip4->fragment_id = inner_frag_id;
- inner_ip4->flags_and_fragment_offset =
- clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
- inner_ip4->ttl = inner_ip6->hop_limit;
- inner_ip4->protocol = inner_protocol;
- inner_ip4->checksum = ip4_header_checksum (inner_ip4);
-
- 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));
- csum =
- ip_csum_sub_even (csum, inner_ip4->length - sizeof (*inner_ip4));
- }
- else
- {
- //Update to new pseudo-header
- 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);
-
- //Move up icmp header
- ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8);
- clib_memcpy (u8_ptr_add (inner_l4, -sizeof (*ip4) - 8), icmp, 8);
- icmp = (icmp46_header_t *) u8_ptr_add (inner_l4, -sizeof (*ip4) - 8);
- }
- else
- {
- //Only one header to translate
- ip4 = (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
- }
- vlib_buffer_advance (p, (u32) (((u8 *) ip4) - ((u8 *) ip6)));
-
- ip4->dst_address.as_u32 = ip6_map_t_embedded_address (d, &ip6->dst_address);
+ ip4->dst_address.as_u32 =
+ ip6_map_t_embedded_address (ctx->d, &ip6->dst_address);
ip4->src_address.as_u32 = ip4_sadr;
- ip4->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip4->tos = ip6_translate_tos (ip6);
- ip4->fragment_id = 0;
- ip4->flags_and_fragment_offset = 0;
- ip4->ttl = ip6->hop_limit;
- ip4->protocol = IP_PROTOCOL_ICMP;
- //TODO fix the length depending on offset length
- ip4->length = u16_net_add (ip6->payload_length,
- (inner_ip6 ==
- NULL) ? sizeof (*ip4) : (2 * sizeof (*ip4) -
- sizeof (*ip6)));
- ip4->checksum = ip4_header_checksum (ip4);
- //TODO: We could do an easy diff-checksum for echo requests/replies
- //Recompute ICMP checksum
- icmp->checksum = 0;
- csum =
- ip_incremental_checksum (0, icmp,
- clib_net_to_host_u16 (ip4->length) -
- sizeof (*ip4));
- icmp->checksum = ~ip_csum_fold (csum);
+ return 0;
+}
+
+static int
+ip6_to_ip4_set_inner_icmp_cb (ip6_header_t * ip6, ip4_header_t * ip4,
+ void *arg)
+{
+ icmp6_to_icmp_ctx_t *ctx = arg;
+ u32 inner_ip4_dadr;
+
+ //Security check of inner packet
+ inner_ip4_dadr = map_get_ip4 (&ip6->dst_address);
+ if (ip6->dst_address.as_u64[0] !=
+ map_get_pfx_net (ctx->d, inner_ip4_dadr, ctx->sender_port)
+ || ip6->dst_address.as_u64[1] != map_get_sfx_net (ctx->d,
+ inner_ip4_dadr,
+ ctx->sender_port))
+ return -1;
+
+ ip4->dst_address.as_u32 = inner_ip4_dadr;
+ ip4->src_address.as_u32 =
+ ip6_map_t_embedded_address (ctx->d, &ip6->src_address);
+
+ return 0;
}
static uword
@@ -462,6 +172,8 @@
ip6_mapt_icmp_next_t next0;
map_domain_t *d0;
u16 len0;
+ icmp6_to_icmp_ctx_t ctx0;
+ ip6_header_t *ip60;
pi0 = to_next[0] = from[0];
from += 1;
@@ -472,14 +184,30 @@
next0 = IP6_MAPT_ICMP_NEXT_IP4_LOOKUP;
p0 = vlib_get_buffer (vm, pi0);
- len0 =
- clib_net_to_host_u16 (((ip6_header_t *)
- vlib_buffer_get_current
- (p0))->payload_length);
+ ip60 = vlib_buffer_get_current (p0);
+ len0 = clib_net_to_host_u16 (ip60->payload_length);
d0 =
pool_elt_at_index (map_main.domains,
vnet_buffer (p0)->map_t.map_domain_index);
- _ip6_map_t_icmp (d0, p0, &error0);
+ ctx0.sender_port = ip6_get_port (ip60, 0, len0);
+ ctx0.d = d0;
+ if (ctx0.sender_port == 0)
+ {
+ // In case of 1:1 mapping, we don't care about the port
+ if (!(d0->ea_bits_len == 0 && d0->rules))
+ {
+ error0 = MAP_ERROR_ICMP;
+ goto err0;
+ }
+ }
+
+ if (icmp6_to_icmp
+ (p0, ip6_to_ip4_set_icmp_cb, d0, ip6_to_ip4_set_inner_icmp_cb,
+ d0))
+ {
+ error0 = MAP_ERROR_ICMP;
+ goto err0;
+ }
if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
{
@@ -489,7 +217,7 @@
vnet_buffer (p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
next0 = IP6_MAPT_ICMP_NEXT_IP4_FRAG;
}
-
+ err0:
if (PREDICT_TRUE (error0 == MAP_ERROR_NONE))
{
vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_RX,
@@ -513,6 +241,17 @@
return frame->n_vectors;
}
+static int
+ip6_to_ip4_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *ctx)
+{
+ vlib_buffer_t *p = ctx;
+
+ ip4->dst_address.as_u32 = vnet_buffer (p)->map_t.v6.daddr;
+ ip4->src_address.as_u32 = vnet_buffer (p)->map_t.v6.saddr;
+
+ return 0;
+}
+
static uword
ip6_map_t_fragmented (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
@@ -521,6 +260,8 @@
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip6_map_t_fragmented_node.index);
while (n_left_from > 0)
{
@@ -531,11 +272,6 @@
{
u32 pi0, pi1;
vlib_buffer_t *p0, *p1;
- ip6_header_t *ip60, *ip61;
- ip6_frag_hdr_t *frag0, *frag1;
- ip4_header_t *ip40, *ip41;
- u16 frag_id0, frag_offset0, frag_id1, frag_offset1;
- u8 frag_more0, frag_more1;
u32 next0, next1;
pi0 = to_next[0] = from[0];
@@ -549,94 +285,41 @@
next1 = IP6_MAPT_TCP_UDP_NEXT_IP4_LOOKUP;
p0 = vlib_get_buffer (vm, pi0);
p1 = vlib_get_buffer (vm, pi1);
- ip60 = vlib_buffer_get_current (p0);
- ip61 = vlib_buffer_get_current (p1);
- frag0 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.frag_offset);
- frag1 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip61,
- vnet_buffer (p1)->map_t.
- v6.frag_offset);
- ip40 =
- (ip4_header_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.l4_offset - sizeof (*ip40));
- ip41 =
- (ip4_header_t *) u8_ptr_add (ip61,
- vnet_buffer (p1)->map_t.
- v6.l4_offset - sizeof (*ip40));
- vlib_buffer_advance (p0,
- vnet_buffer (p0)->map_t.v6.l4_offset -
- sizeof (*ip40));
- vlib_buffer_advance (p1,
- vnet_buffer (p1)->map_t.v6.l4_offset -
- sizeof (*ip40));
- frag_id0 = frag_id_6to4 (frag0->identification);
- frag_id1 = frag_id_6to4 (frag1->identification);
- frag_more0 = ip6_frag_hdr_more (frag0);
- frag_more1 = ip6_frag_hdr_more (frag1);
- frag_offset0 = ip6_frag_hdr_offset (frag0);
- frag_offset1 = ip6_frag_hdr_offset (frag1);
-
- ip40->dst_address.as_u32 = vnet_buffer (p0)->map_t.v6.daddr;
- ip41->dst_address.as_u32 = vnet_buffer (p1)->map_t.v6.daddr;
- ip40->src_address.as_u32 = vnet_buffer (p0)->map_t.v6.saddr;
- ip41->src_address.as_u32 = vnet_buffer (p1)->map_t.v6.saddr;
- ip40->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip41->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip40->tos = ip6_translate_tos (ip60);
- ip41->tos = ip6_translate_tos (ip61);
- ip40->length = u16_net_add (ip60->payload_length,
- sizeof (*ip40) -
- vnet_buffer (p0)->map_t.v6.l4_offset +
- sizeof (*ip60));
- ip41->length =
- u16_net_add (ip61->payload_length,
- sizeof (*ip40) -
- vnet_buffer (p1)->map_t.v6.l4_offset +
- sizeof (*ip60));
- ip40->fragment_id = frag_id0;
- ip41->fragment_id = frag_id1;
- ip40->flags_and_fragment_offset =
- clib_host_to_net_u16 (frag_offset0 |
- (frag_more0 ? IP4_HEADER_FLAG_MORE_FRAGMENTS
- : 0));
- ip41->flags_and_fragment_offset =
- clib_host_to_net_u16 (frag_offset1 |
- (frag_more1 ? IP4_HEADER_FLAG_MORE_FRAGMENTS
- : 0));
- ip40->ttl = ip60->hop_limit;
- ip41->ttl = ip61->hop_limit;
- ip40->protocol =
- (vnet_buffer (p0)->map_t.v6.l4_protocol ==
- IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : vnet_buffer (p0)->
- map_t.v6.l4_protocol;
- ip41->protocol =
- (vnet_buffer (p1)->map_t.v6.l4_protocol ==
- IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : vnet_buffer (p1)->
- map_t.v6.l4_protocol;
- ip40->checksum = ip4_header_checksum (ip40);
- ip41->checksum = ip4_header_checksum (ip41);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ if (ip6_to_ip4_fragmented (p0, ip6_to_ip4_set_cb, p0))
{
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next0 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
+ next0 = IP6_MAPT_FRAGMENTED_NEXT_DROP;
+ }
+ else
+ {
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next0 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ }
}
- if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
+ if (ip6_to_ip4_fragmented (p1, ip6_to_ip4_set_cb, p1))
{
- vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
- vnet_buffer (p1)->ip_frag.header_offset = 0;
- vnet_buffer (p1)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next1 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ p1->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
+ next1 = IP6_MAPT_FRAGMENTED_NEXT_DROP;
+ }
+ else
+ {
+ if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
+ vnet_buffer (p1)->ip_frag.header_offset = 0;
+ vnet_buffer (p1)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next1 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -649,12 +332,6 @@
{
u32 pi0;
vlib_buffer_t *p0;
- ip6_header_t *ip60;
- ip6_frag_hdr_t *frag0;
- ip4_header_t *ip40;
- u16 frag_id0;
- u8 frag_more0;
- u16 frag_offset0;
u32 next0;
pi0 = to_next[0] = from[0];
@@ -665,51 +342,23 @@
next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_LOOKUP;
p0 = vlib_get_buffer (vm, pi0);
- ip60 = vlib_buffer_get_current (p0);
- frag0 =
- (ip6_frag_hdr_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.frag_offset);
- ip40 =
- (ip4_header_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.l4_offset - sizeof (*ip40));
- vlib_buffer_advance (p0,
- vnet_buffer (p0)->map_t.v6.l4_offset -
- sizeof (*ip40));
- frag_id0 = frag_id_6to4 (frag0->identification);
- frag_more0 = ip6_frag_hdr_more (frag0);
- frag_offset0 = ip6_frag_hdr_offset (frag0);
-
- ip40->dst_address.as_u32 = vnet_buffer (p0)->map_t.v6.daddr;
- ip40->src_address.as_u32 = vnet_buffer (p0)->map_t.v6.saddr;
- ip40->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip40->tos = ip6_translate_tos (ip60);
- ip40->length = u16_net_add (ip60->payload_length,
- sizeof (*ip40) -
- vnet_buffer (p0)->map_t.v6.l4_offset +
- sizeof (*ip60));
- ip40->fragment_id = frag_id0;
- ip40->flags_and_fragment_offset =
- clib_host_to_net_u16 (frag_offset0 |
- (frag_more0 ? IP4_HEADER_FLAG_MORE_FRAGMENTS
- : 0));
- ip40->ttl = ip60->hop_limit;
- ip40->protocol =
- (vnet_buffer (p0)->map_t.v6.l4_protocol ==
- IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : vnet_buffer (p0)->
- map_t.v6.l4_protocol;
- ip40->checksum = ip4_header_checksum (ip40);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ if (ip6_to_ip4_fragmented (p0, ip6_to_ip4_set_cb, p0))
{
- //Send to fragmentation node if necessary
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next0 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
+ next0 = IP6_MAPT_FRAGMENTED_NEXT_DROP;
+ }
+ else
+ {
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next0 = IP6_MAPT_FRAGMENTED_NEXT_IP4_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -726,6 +375,9 @@
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip6_map_t_tcp_udp_node.index);
+
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
@@ -738,11 +390,6 @@
{
u32 pi0, pi1;
vlib_buffer_t *p0, *p1;
- ip6_header_t *ip60, *ip61;
- ip_csum_t csum0, csum1;
- ip4_header_t *ip40, *ip41;
- u16 fragment_id0, flags0, *checksum0,
- fragment_id1, flags1, *checksum1;
ip6_mapt_tcp_udp_next_t next0, next1;
pi0 = to_next[0] = from[0];
@@ -756,118 +403,41 @@
p0 = vlib_get_buffer (vm, pi0);
p1 = vlib_get_buffer (vm, pi1);
- ip60 = vlib_buffer_get_current (p0);
- ip61 = vlib_buffer_get_current (p1);
- ip40 =
- (ip4_header_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.l4_offset - sizeof (*ip40));
- ip41 =
- (ip4_header_t *) u8_ptr_add (ip61,
- vnet_buffer (p1)->map_t.
- v6.l4_offset - sizeof (*ip40));
- vlib_buffer_advance (p0,
- vnet_buffer (p0)->map_t.v6.l4_offset -
- sizeof (*ip40));
- vlib_buffer_advance (p1,
- vnet_buffer (p1)->map_t.v6.l4_offset -
- sizeof (*ip40));
- checksum0 =
- (u16 *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.checksum_offset);
- checksum1 =
- (u16 *) u8_ptr_add (ip61,
- vnet_buffer (p1)->map_t.checksum_offset);
- csum0 = ip_csum_sub_even (*checksum0, ip60->src_address.as_u64[0]);
- csum1 = ip_csum_sub_even (*checksum1, ip61->src_address.as_u64[0]);
- csum0 = ip_csum_sub_even (csum0, ip60->src_address.as_u64[1]);
- csum1 = ip_csum_sub_even (csum1, ip61->src_address.as_u64[1]);
- csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[0]);
- csum1 = ip_csum_sub_even (csum0, ip61->dst_address.as_u64[0]);
- csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[1]);
- csum1 = ip_csum_sub_even (csum1, ip61->dst_address.as_u64[1]);
- csum0 = ip_csum_add_even (csum0, vnet_buffer (p0)->map_t.v6.daddr);
- csum1 = ip_csum_add_even (csum1, vnet_buffer (p1)->map_t.v6.daddr);
- csum0 = ip_csum_add_even (csum0, vnet_buffer (p0)->map_t.v6.saddr);
- csum1 = ip_csum_add_even (csum1, vnet_buffer (p1)->map_t.v6.saddr);
- *checksum0 = ip_csum_fold (csum0);
- *checksum1 = ip_csum_fold (csum1);
-
- if (PREDICT_FALSE (vnet_buffer (p0)->map_t.v6.frag_offset))
+ if (ip6_to_ip4_tcp_udp (p0, ip6_to_ip4_set_cb, p0, 1))
{
- ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip60,
- vnet_buffer
- (p0)->
- map_t.
- v6.frag_offset);
- fragment_id0 = frag_id_6to4 (hdr->identification);
- flags0 = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
+ p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next0 = IP6_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- fragment_id0 = 0;
- flags0 = 0;
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
+ }
}
- if (PREDICT_FALSE (vnet_buffer (p1)->map_t.v6.frag_offset))
+ if (ip6_to_ip4_tcp_udp (p1, ip6_to_ip4_set_cb, p1, 1))
{
- ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip61,
- vnet_buffer
- (p1)->
- map_t.
- v6.frag_offset);
- fragment_id1 = frag_id_6to4 (hdr->identification);
- flags1 = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
+ p1->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next1 = IP6_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- fragment_id1 = 0;
- flags1 = 0;
- }
-
- ip40->dst_address.as_u32 = vnet_buffer (p0)->map_t.v6.daddr;
- ip41->dst_address.as_u32 = vnet_buffer (p1)->map_t.v6.daddr;
- ip40->src_address.as_u32 = vnet_buffer (p0)->map_t.v6.saddr;
- ip41->src_address.as_u32 = vnet_buffer (p1)->map_t.v6.saddr;
- ip40->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip41->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip40->tos = ip6_translate_tos (ip60);
- ip41->tos = ip6_translate_tos (ip61);
- ip40->length = u16_net_add (ip60->payload_length,
- sizeof (*ip40) + sizeof (*ip60) -
- vnet_buffer (p0)->map_t.v6.l4_offset);
- ip41->length =
- u16_net_add (ip61->payload_length,
- sizeof (*ip40) + sizeof (*ip60) -
- vnet_buffer (p1)->map_t.v6.l4_offset);
- ip40->fragment_id = fragment_id0;
- ip41->fragment_id = fragment_id1;
- ip40->flags_and_fragment_offset = flags0;
- ip41->flags_and_fragment_offset = flags1;
- ip40->ttl = ip60->hop_limit;
- ip41->ttl = ip61->hop_limit;
- ip40->protocol = vnet_buffer (p0)->map_t.v6.l4_protocol;
- ip41->protocol = vnet_buffer (p1)->map_t.v6.l4_protocol;
- ip40->checksum = ip4_header_checksum (ip40);
- ip41->checksum = ip4_header_checksum (ip41);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
- {
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
- }
-
- if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
- {
- vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
- vnet_buffer (p1)->ip_frag.header_offset = 0;
- vnet_buffer (p1)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next1 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
+ if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
+ vnet_buffer (p1)->ip_frag.header_offset = 0;
+ vnet_buffer (p1)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next1 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
@@ -880,12 +450,6 @@
{
u32 pi0;
vlib_buffer_t *p0;
- ip6_header_t *ip60;
- u16 *checksum0;
- ip_csum_t csum0;
- ip4_header_t *ip40;
- u16 fragment_id0;
- u16 flags0;
ip6_mapt_tcp_udp_next_t next0;
pi0 = to_next[0] = from[0];
@@ -896,65 +460,23 @@
next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_LOOKUP;
p0 = vlib_get_buffer (vm, pi0);
- ip60 = vlib_buffer_get_current (p0);
- ip40 =
- (ip4_header_t *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.
- v6.l4_offset - sizeof (*ip40));
- vlib_buffer_advance (p0,
- vnet_buffer (p0)->map_t.v6.l4_offset -
- sizeof (*ip40));
- checksum0 =
- (u16 *) u8_ptr_add (ip60,
- vnet_buffer (p0)->map_t.checksum_offset);
- //TODO: This can probably be optimized
- csum0 = ip_csum_sub_even (*checksum0, ip60->src_address.as_u64[0]);
- csum0 = ip_csum_sub_even (csum0, ip60->src_address.as_u64[1]);
- csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[0]);
- csum0 = ip_csum_sub_even (csum0, ip60->dst_address.as_u64[1]);
- csum0 = ip_csum_add_even (csum0, vnet_buffer (p0)->map_t.v6.daddr);
- csum0 = ip_csum_add_even (csum0, vnet_buffer (p0)->map_t.v6.saddr);
- *checksum0 = ip_csum_fold (csum0);
-
- if (PREDICT_FALSE (vnet_buffer (p0)->map_t.v6.frag_offset))
+ if (ip6_to_ip4_tcp_udp (p0, ip6_to_ip4_set_cb, p0, 1))
{
- //Only the first fragment
- ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip60,
- vnet_buffer
- (p0)->
- map_t.
- v6.frag_offset);
- fragment_id0 = frag_id_6to4 (hdr->identification);
- flags0 = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
+ p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
+ next0 = IP6_MAPT_TCP_UDP_NEXT_DROP;
}
else
{
- fragment_id0 = 0;
- flags0 = 0;
- }
-
- ip40->dst_address.as_u32 = vnet_buffer (p0)->map_t.v6.daddr;
- ip40->src_address.as_u32 = vnet_buffer (p0)->map_t.v6.saddr;
- ip40->ip_version_and_header_length =
- IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
- ip40->tos = ip6_translate_tos (ip60);
- ip40->length = u16_net_add (ip60->payload_length,
- sizeof (*ip40) + sizeof (*ip60) -
- vnet_buffer (p0)->map_t.v6.l4_offset);
- ip40->fragment_id = fragment_id0;
- ip40->flags_and_fragment_offset = flags0;
- ip40->ttl = ip60->hop_limit;
- ip40->protocol = vnet_buffer (p0)->map_t.v6.l4_protocol;
- ip40->checksum = ip4_header_checksum (ip40);
-
- if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
- {
- //Send to fragmentation node if necessary
- vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
- vnet_buffer (p0)->ip_frag.header_offset = 0;
- vnet_buffer (p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP4_LOOKUP;
- next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
+ if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
+ {
+ //Send to fragmentation node if necessary
+ vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
+ vnet_buffer (p0)->ip_frag.header_offset = 0;
+ vnet_buffer (p0)->ip_frag.next_index =
+ IP4_FRAG_NEXT_IP4_LOOKUP;
+ next0 = IP6_MAPT_TCP_UDP_NEXT_IP4_FRAG;
+ }
}
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
diff --git a/src/vnet/map/map.c b/src/vnet/map/map.c
index 6a707df..8579cdf 100644
--- a/src/vnet/map/map.c
+++ b/src/vnet/map/map.c
@@ -76,91 +76,6 @@
*/
-i32
-ip4_get_port (ip4_header_t * ip, map_dir_e dir, u16 buffer_len)
-{
- //TODO: use buffer length
- if (ip->ip_version_and_header_length != 0x45 ||
- ip4_get_fragment_offset (ip))
- return -1;
-
- if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
- (ip->protocol == IP_PROTOCOL_UDP)))
- {
- udp_header_t *udp = (void *) (ip + 1);
- return (dir == MAP_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 (dir == MAP_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 -1;
-}
-
-i32
-ip6_get_port (ip6_header_t * ip6, map_dir_e dir, u16 buffer_len)
-{
- u8 l4_protocol;
- u16 l4_offset;
- u16 frag_offset;
- u8 *l4;
-
- if (ip6_parse (ip6, buffer_len, &l4_protocol, &l4_offset, &frag_offset))
- return -1;
-
- //TODO: Use buffer length
-
- if (frag_offset &&
- ip6_frag_hdr_offset (((ip6_frag_hdr_t *)
- u8_ptr_add (ip6, frag_offset))))
- return -1; //Can't deal with non-first fragment for now
-
- l4 = u8_ptr_add (ip6, l4_offset);
- if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP)
- {
- return (dir ==
- MAP_SENDER) ? ((udp_header_t *) (l4))->src_port : ((udp_header_t
- *)
- (l4))->dst_port;
- }
- else if (l4_protocol == IP_PROTOCOL_ICMP6)
- {
- icmp46_header_t *icmp = (icmp46_header_t *) (l4);
- if (icmp->type == ICMP6_echo_request)
- {
- return (dir == MAP_SENDER) ? ((u16 *) (icmp))[2] : -1;
- }
- else if (icmp->type == ICMP6_echo_reply)
- {
- return (dir == MAP_SENDER) ? -1 : ((u16 *) (icmp))[2];
- }
- }
- return -1;
-}
-
int
map_create_domain (ip4_address_t * ip4_prefix,
diff --git a/src/vnet/map/map.h b/src/vnet/map/map.h
index 644e80f..208a58e 100644
--- a/src/vnet/map/map.h
+++ b/src/vnet/map/map.h
@@ -25,12 +25,6 @@
#define MAP_SKIP_IP6_LOOKUP 1
-typedef enum
-{
- MAP_SENDER,
- MAP_RECEIVER
-} map_dir_e;
-
int map_create_domain (ip4_address_t * ip4_prefix, u8 ip4_prefix_len,
ip6_address_t * ip6_prefix, u8 ip6_prefix_len,
ip6_address_t * ip6_src, u8 ip6_src_len,
@@ -40,9 +34,6 @@
int map_add_del_psid (u32 map_domain_index, u16 psid, ip6_address_t * tep,
u8 is_add);
u8 *format_map_trace (u8 * s, va_list * args);
-i32 ip4_get_port (ip4_header_t * ip, map_dir_e dir, u16 buffer_len);
-i32 ip6_get_port (ip6_header_t * ip6, map_dir_e dir, u16 buffer_len);
-u16 ip4_map_get_port (ip4_header_t * ip, map_dir_e dir);
typedef enum __attribute__ ((__packed__))
{
@@ -518,30 +509,10 @@
int map_ip6_reass_conf_buffers(u32 buffers);
#define MAP_IP6_REASS_CONF_BUFFERS_MAX (0xffffffff)
-static_always_inline
-int ip6_parse(const ip6_header_t *ip6, u32 buff_len,
- u8 *l4_protocol, u16 *l4_offset, u16 *frag_hdr_offset)
-{
- if (ip6->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION) {
- *l4_protocol = ((ip6_frag_hdr_t *)(ip6 + 1))->next_hdr;
- *frag_hdr_offset = sizeof(*ip6);
- *l4_offset = sizeof(*ip6) + sizeof(ip6_frag_hdr_t);
- } else {
- *l4_protocol = ip6->protocol;
- *frag_hdr_offset = 0;
- *l4_offset = sizeof(*ip6);
- }
-
- return (buff_len < (*l4_offset + 4)) ||
- (clib_net_to_host_u16(ip6->payload_length) < (*l4_offset + 4 - sizeof(*ip6)));
-}
-
#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
-#define frag_id_6to4(id) ((id) ^ ((id) >> 16))
-
static_always_inline void
ip4_map_t_embedded_address (map_domain_t *d,
ip6_address_t *ip6, const ip4_address_t *ip4)