+}
+
+static_always_inline void
+cnat_translation_icmp6 (ip6_header_t * outer_ip6, udp_header_t * outer_udp,
+ ip6_address_t outer_new_addr[VLIB_N_DIR],
+ u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip)
+{
+ icmp46_header_t *icmp = (icmp46_header_t *) outer_udp;
+ ip6_address_t new_addr[VLIB_N_DIR];
+ ip6_address_t old_addr[VLIB_N_DIR];
+ ip6_address_t outer_old_addr[VLIB_N_DIR];
+ u16 new_port[VLIB_N_DIR];
+ u16 old_port[VLIB_N_DIR];
+ ip_csum_t sum, inner_l4_sum, inner_l4_old_sum;
+
+ if (!icmp6_type_is_error_message (icmp->type))
+ return;
+
+ ip6_header_t *ip6 = (ip6_header_t *) (icmp + 2);
+ udp_header_t *udp = (udp_header_t *) (ip6 + 1);
+ tcp_header_t *tcp = (tcp_header_t *) udp;
+
+ /* Swap inner ports */
+ ip6_address_copy (&new_addr[VLIB_RX], &outer_new_addr[VLIB_TX]);
+ ip6_address_copy (&new_addr[VLIB_TX], &outer_new_addr[VLIB_RX]);
+ new_port[VLIB_TX] = outer_new_port[VLIB_RX];
+ new_port[VLIB_RX] = outer_new_port[VLIB_TX];
+
+ ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
+ ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
+ old_port[VLIB_RX] = udp->src_port;
+ old_port[VLIB_TX] = udp->dst_port;
+
+ sum = icmp->checksum;
+ /* Translate outer ip */
+ ip6_address_copy (&outer_old_addr[VLIB_TX], &outer_ip6->dst_address);
+ ip6_address_copy (&outer_old_addr[VLIB_RX], &outer_ip6->src_address);
+ if (!snat_outer_ip)
+ ip6_address_copy (&outer_new_addr[VLIB_RX], &outer_ip6->src_address);
+ cnat_ip6_translate_l3 (outer_ip6, outer_new_addr);
+ if (has_ip6_address (&outer_new_addr[VLIB_TX]))
+ {
+ sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
+ }
+
+ if (has_ip6_address (&outer_new_addr[VLIB_RX]))
+ {
+ sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
+ }
+
+ if (ip6->protocol == IP_PROTOCOL_TCP)
+ {
+ inner_l4_old_sum = inner_l4_sum = tcp->checksum;
+ cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
+ tcp->checksum = ip_csum_fold (inner_l4_sum);
+ }
+ else if (ip6->protocol == IP_PROTOCOL_UDP)
+ {
+ inner_l4_old_sum = inner_l4_sum = udp->checksum;
+ cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
+ udp->checksum = ip_csum_fold (inner_l4_sum);
+ }
+ else
+ return;
+
+ /* UDP/TCP checksum changed */
+ sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
+ ip4_header_t /* cheat */ ,
+ checksum);
+
+ /* UDP/TCP Ports changed */
+ if (old_port[VLIB_TX] && new_port[VLIB_TX])
+ sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
+ ip4_header_t /* cheat */ ,
+ length /* changed member */ );
+
+ if (old_port[VLIB_RX] && new_port[VLIB_RX])
+ sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
+ ip4_header_t /* cheat */ ,
+ length /* changed member */ );
+
+
+ cnat_ip6_translate_l3 (ip6, new_addr);
+ /* IP src/dst addr changed */
+ if (has_ip6_address (&new_addr[VLIB_TX]))
+ {
+ sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
+ }
+
+ if (has_ip6_address (&new_addr[VLIB_RX]))
+ {
+ sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
+ sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
+ }
+
+ icmp->checksum = ip_csum_fold (sum);