- 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;
-
- 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 (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;