diff options
Diffstat (limited to 'src/plugins/cnat/cnat_node.h')
-rw-r--r-- | src/plugins/cnat/cnat_node.h | 485 |
1 files changed, 244 insertions, 241 deletions
diff --git a/src/plugins/cnat/cnat_node.h b/src/plugins/cnat/cnat_node.h index 246fdb8ba57..d81f6745bc4 100644 --- a/src/plugins/cnat/cnat_node.h +++ b/src/plugins/cnat/cnat_node.h @@ -19,6 +19,7 @@ #include <vlibmemory/api.h> #include <vnet/dpo/load_balance.h> #include <vnet/dpo/load_balance_map.h> +#include <vnet/ip/ip_psh_cksum.h> #include <cnat/cnat_session.h> #include <cnat/cnat_client.h> @@ -169,86 +170,92 @@ cmp_ip6_address (const ip6_address_t * a1, const ip6_address_t * a2) * Inline translation functions */ -static_always_inline u8 -has_ip6_address (ip6_address_t * a) +static_always_inline u16 +ip4_pseudo_header_cksum2 (ip4_header_t *ip4, ip4_address_t address[VLIB_N_DIR]) { - return ((0 != a->as_u64[0]) || (0 != a->as_u64[1])); + ip4_psh_t psh = { 0 }; + psh.src = address[VLIB_RX]; + psh.dst = address[VLIB_TX]; + psh.proto = ip4->protocol; + psh.l4len = clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) - + sizeof (ip4_header_t)); + return ~(clib_ip_csum ((u8 *) &psh, sizeof (ip4_psh_t))); } static_always_inline void -cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp, - ip_csum_t * sum, +cnat_ip4_translate_l4 (ip4_header_t *ip4, udp_header_t *udp, ip_csum_t *sum, ip4_address_t new_addr[VLIB_N_DIR], - u16 new_port[VLIB_N_DIR]) + u16 new_port[VLIB_N_DIR], u32 oflags) { u16 old_port[VLIB_N_DIR]; - ip4_address_t old_addr[VLIB_N_DIR]; + old_port[VLIB_TX] = udp->dst_port; + old_port[VLIB_RX] = udp->src_port; - /* Fastpath no checksum */ - if (PREDICT_TRUE (0 == *sum)) + udp->dst_port = new_port[VLIB_TX]; + udp->src_port = new_port[VLIB_RX]; + + if (oflags & + (VNET_BUFFER_OFFLOAD_F_TCP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)) { - udp->dst_port = new_port[VLIB_TX]; - udp->src_port = new_port[VLIB_RX]; + *sum = ip4_pseudo_header_cksum2 (ip4, new_addr); return; } - old_port[VLIB_TX] = udp->dst_port; - old_port[VLIB_RX] = udp->src_port; - old_addr[VLIB_TX] = ip4->dst_address; - old_addr[VLIB_RX] = ip4->src_address; + *sum = ip_csum_update (*sum, ip4->dst_address.as_u32, + new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address); + *sum = ip_csum_update (*sum, ip4->src_address.as_u32, + new_addr[VLIB_RX].as_u32, ip4_header_t, src_address); - if (new_addr[VLIB_TX].as_u32) + *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX], + udp_header_t, dst_port); + *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX], + udp_header_t, src_port); +} + +static_always_inline void +cnat_ip4_translate_sctp (ip4_header_t *ip4, sctp_header_t *sctp, + u16 new_port[VLIB_N_DIR]) +{ + /* Fastpath no checksum */ + if (PREDICT_TRUE (0 == sctp->checksum)) { - *sum = - ip_csum_update (*sum, old_addr[VLIB_TX].as_u32, - new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address); + sctp->dst_port = new_port[VLIB_TX]; + sctp->src_port = new_port[VLIB_RX]; + return; } + if (new_port[VLIB_TX]) - { - udp->dst_port = 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 (new_addr[VLIB_RX].as_u32) - { - *sum = - ip_csum_update (*sum, old_addr[VLIB_RX].as_u32, - new_addr[VLIB_RX].as_u32, ip4_header_t, src_address); - } + sctp->dst_port = new_port[VLIB_TX]; if (new_port[VLIB_RX]) - { - udp->src_port = new_port[VLIB_RX]; - *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX], - ip4_header_t /* cheat */ , - length /* changed member */ ); - } + sctp->src_port = new_port[VLIB_RX]; + + sctp->checksum = 0; + sctp->checksum = clib_host_to_little_u32 (~clib_crc32c_with_init ( + (u8 *) sctp, ntohs (ip4->length) - sizeof (ip4_header_t), + ~0 /* init value */)); } static_always_inline void -cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR]) +cnat_ip4_translate_l3 (ip4_header_t *ip4, ip4_address_t new_addr[VLIB_N_DIR], + u32 oflags) { ip4_address_t old_addr[VLIB_N_DIR]; ip_csum_t sum; - old_addr[VLIB_TX] = ip4->dst_address; old_addr[VLIB_RX] = ip4->src_address; + ip4->dst_address = new_addr[VLIB_TX]; + ip4->src_address = new_addr[VLIB_RX]; + + // We always compute the IP checksum even if oflags & + // VNET_BUFFER_OFFLOAD_F_IP_CKSUM is set as this is relatively inexpensive + // and will allow avoiding issues in driver that do not behave properly + // downstream. sum = ip4->checksum; - if (new_addr[VLIB_TX].as_u32) - { - ip4->dst_address = new_addr[VLIB_TX]; - sum = - ip_csum_update (sum, old_addr[VLIB_TX].as_u32, + sum = ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address); - } - if (new_addr[VLIB_RX].as_u32) - { - ip4->src_address = new_addr[VLIB_RX]; - sum = - ip_csum_update (sum, old_addr[VLIB_RX].as_u32, + sum = ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32, ip4_header_t, src_address); - } ip4->checksum = ip_csum_fold (sum); } @@ -257,48 +264,40 @@ cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index) { cnat_main_t *cm = &cnat_main; if (PREDICT_FALSE (tcp_fin (tcp))) - { - cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT); - } + cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT); if (PREDICT_FALSE (tcp_rst (tcp))) - { - cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT); - } + cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT); if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp))) - { - cnat_timestamp_set_lifetime (index, cm->tcp_max_age); - } + cnat_timestamp_set_lifetime (index, cm->tcp_max_age); } static_always_inline void -cnat_translation_icmp4_echo (ip4_header_t * ip4, icmp46_header_t * icmp, +cnat_translation_icmp4_echo (ip4_header_t *ip4, icmp46_header_t *icmp, ip4_address_t new_addr[VLIB_N_DIR], - u16 new_port[VLIB_N_DIR]) + u16 new_port[VLIB_N_DIR], u32 oflags) { ip_csum_t sum; u16 old_port; cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1); - cnat_ip4_translate_l3 (ip4, new_addr); + cnat_ip4_translate_l3 (ip4, new_addr, oflags); old_port = echo->identifier; echo->identifier = new_port[VLIB_RX]; sum = icmp->checksum; - sum = ip_csum_update (sum, old_port, new_port[VLIB_RX], - ip4_header_t /* cheat */ , - length /* changed member */ ); + sum = + ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t, src_port); icmp->checksum = ip_csum_fold (sum); } static_always_inline void -cnat_translation_icmp4_error (ip4_header_t * outer_ip4, - icmp46_header_t * icmp, +cnat_translation_icmp4_error (ip4_header_t *outer_ip4, icmp46_header_t *icmp, ip4_address_t outer_new_addr[VLIB_N_DIR], - u16 outer_new_port[VLIB_N_DIR], - u8 snat_outer_ip) + u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip, + u32 oflags) { ip4_address_t new_addr[VLIB_N_DIR]; ip4_address_t old_addr[VLIB_N_DIR]; @@ -327,18 +326,20 @@ cnat_translation_icmp4_error (ip4_header_t * outer_ip4, /* translate outer ip. */ if (!snat_outer_ip) outer_new_addr[VLIB_RX] = outer_ip4->src_address; - cnat_ip4_translate_l3 (outer_ip4, outer_new_addr); + cnat_ip4_translate_l3 (outer_ip4, outer_new_addr, oflags); if (ip4->protocol == IP_PROTOCOL_TCP) { inner_l4_old_sum = inner_l4_sum = tcp->checksum; - cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port); + cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port, + 0 /* flags */); tcp->checksum = ip_csum_fold (inner_l4_sum); } else if (ip4->protocol == IP_PROTOCOL_UDP) { inner_l4_old_sum = inner_l4_sum = udp->checksum; - cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port); + cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port, + 0 /* flags */); udp->checksum = ip_csum_fold (inner_l4_sum); } else @@ -351,37 +352,30 @@ cnat_translation_icmp4_error (ip4_header_t * outer_ip4, /* 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 */ ); + udp_header_t, dst_port); 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 */ ); - + udp_header_t, src_port); - cnat_ip4_translate_l3 (ip4, new_addr); + cnat_ip4_translate_l3 (ip4, new_addr, 0 /* oflags */); ip_csum_t new_ip_sum = ip4->checksum; /* IP checksum changed */ sum = ip_csum_update (sum, old_ip_sum, new_ip_sum, ip4_header_t, checksum); /* IP src/dst addr changed */ - if (new_addr[VLIB_TX].as_u32) - sum = - ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32, - ip4_header_t, dst_address); + sum = ip_csum_update (sum, old_addr[VLIB_TX].as_u32, + new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address); - if (new_addr[VLIB_RX].as_u32) - sum = - ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32, - ip4_header_t, src_address); + sum = ip_csum_update (sum, old_addr[VLIB_RX].as_u32, + new_addr[VLIB_RX].as_u32, ip4_header_t, src_address); icmp->checksum = ip_csum_fold (sum); } static_always_inline void -cnat_translation_ip4 (const cnat_session_t * session, - ip4_header_t * ip4, udp_header_t * udp) +cnat_translation_ip4 (const cnat_session_t *session, ip4_header_t *ip4, + udp_header_t *udp, u32 oflags) { tcp_header_t *tcp = (tcp_header_t *) udp; ip4_address_t new_addr[VLIB_N_DIR]; @@ -395,17 +389,23 @@ cnat_translation_ip4 (const cnat_session_t * session, if (ip4->protocol == IP_PROTOCOL_TCP) { ip_csum_t sum = tcp->checksum; - cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port); + cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port, oflags); tcp->checksum = ip_csum_fold (sum); - cnat_ip4_translate_l3 (ip4, new_addr); + cnat_ip4_translate_l3 (ip4, new_addr, oflags); cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index); } else if (ip4->protocol == IP_PROTOCOL_UDP) { ip_csum_t sum = udp->checksum; - cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port); + cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port, oflags); udp->checksum = ip_csum_fold (sum); - cnat_ip4_translate_l3 (ip4, new_addr); + cnat_ip4_translate_l3 (ip4, new_addr, oflags); + } + else if (ip4->protocol == IP_PROTOCOL_SCTP) + { + sctp_header_t *sctp = (sctp_header_t *) udp; + cnat_ip4_translate_sctp (ip4, sctp, new_port); + cnat_ip4_translate_l3 (ip4, new_addr, oflags); } else if (ip4->protocol == IP_PROTOCOL_ICMP) { @@ -417,74 +417,65 @@ cnat_translation_ip4 (const cnat_session_t * session, (ip4->src_address.as_u32 == session->key.cs_ip[VLIB_RX].ip4.as_u32); cnat_translation_icmp4_error (ip4, icmp, new_addr, new_port, - snat_outer_ip); + snat_outer_ip, oflags); } else if (icmp_type_is_echo (icmp->type)) - cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port); + cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port, oflags); } } static_always_inline void cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR]) { - if (has_ip6_address (&new_addr[VLIB_TX])) - ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]); - if (has_ip6_address (&new_addr[VLIB_RX])) - ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]); + ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]); + ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]); +} + +static_always_inline u16 +ip6_pseudo_header_cksum2 (ip6_header_t *ip6, ip6_address_t address[VLIB_N_DIR]) +{ + ip6_psh_t psh = { 0 }; + psh.src = address[VLIB_RX]; + psh.dst = address[VLIB_TX]; + psh.l4len = ip6->payload_length; + psh.proto = clib_host_to_net_u32 ((u32) ip6->protocol); + return ~(clib_ip_csum ((u8 *) &psh, sizeof (ip6_psh_t))); } static_always_inline void -cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp, - ip_csum_t * sum, +cnat_ip6_translate_l4 (ip6_header_t *ip6, udp_header_t *udp, ip_csum_t *sum, ip6_address_t new_addr[VLIB_N_DIR], - u16 new_port[VLIB_N_DIR]) + u16 new_port[VLIB_N_DIR], u32 oflags) { u16 old_port[VLIB_N_DIR]; - ip6_address_t old_addr[VLIB_N_DIR]; + old_port[VLIB_TX] = udp->dst_port; + old_port[VLIB_RX] = udp->src_port; - /* Fastpath no checksum */ - if (PREDICT_TRUE (0 == *sum)) + udp->dst_port = new_port[VLIB_TX]; + udp->src_port = new_port[VLIB_RX]; + + if (oflags & + (VNET_BUFFER_OFFLOAD_F_TCP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)) { - udp->dst_port = new_port[VLIB_TX]; - udp->src_port = new_port[VLIB_RX]; + *sum = ip6_pseudo_header_cksum2 (ip6, new_addr); return; } - old_port[VLIB_TX] = udp->dst_port; - old_port[VLIB_RX] = udp->src_port; - ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address); - ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address); + *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, ip6->dst_address.as_u64[0]); + *sum = ip_csum_sub_even (*sum, ip6->dst_address.as_u64[1]); - 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]); - } + *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, ip6->src_address.as_u64[0]); + *sum = ip_csum_sub_even (*sum, ip6->src_address.as_u64[1]); - if (new_port[VLIB_TX]) - { - udp->dst_port = 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 (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]); - } + *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX], + udp_header_t, dst_port); - if (new_port[VLIB_RX]) - { - udp->src_port = new_port[VLIB_RX]; - *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX], - ip4_header_t /* cheat */ , - length /* changed member */ ); - } + *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX], + udp_header_t, src_port); } static_always_inline void @@ -503,26 +494,20 @@ cnat_translation_icmp6_echo (ip6_header_t * ip6, icmp46_header_t * icmp, sum = icmp->checksum; cnat_ip6_translate_l3 (ip6, new_addr); - 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]); - } + 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]); + + 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]); echo->identifier = new_port[VLIB_RX]; - sum = ip_csum_update (sum, old_port, new_port[VLIB_RX], - ip4_header_t /* cheat */ , - length /* changed member */ ); + sum = + ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t, src_port); icmp->checksum = ip_csum_fold (sum); } @@ -566,79 +551,64 @@ cnat_translation_icmp6_error (ip6_header_t * outer_ip6, 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]); - } + 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]); + + 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]); /* Translate inner TCP / UDP */ 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); + cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port, + 0 /* oflags */); 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); + cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port, + 0 /* oflags */); 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 */ , + sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t, 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 */ ); + sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX], + udp_header_t, dst_port); + sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX], + udp_header_t, src_port); 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]); - } + 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]); - } + 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); } static_always_inline void -cnat_translation_ip6 (const cnat_session_t * session, - ip6_header_t * ip6, udp_header_t * udp) +cnat_translation_ip6 (const cnat_session_t *session, ip6_header_t *ip6, + udp_header_t *udp, u32 oflags) { tcp_header_t *tcp = (tcp_header_t *) udp; ip6_address_t new_addr[VLIB_N_DIR]; @@ -652,7 +622,7 @@ cnat_translation_ip6 (const cnat_session_t * session, if (ip6->protocol == IP_PROTOCOL_TCP) { ip_csum_t sum = tcp->checksum; - cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port); + cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port, oflags); tcp->checksum = ip_csum_fold (sum); cnat_ip6_translate_l3 (ip6, new_addr); cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index); @@ -660,7 +630,7 @@ cnat_translation_ip6 (const cnat_session_t * session, else if (ip6->protocol == IP_PROTOCOL_UDP) { ip_csum_t sum = udp->checksum; - cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port); + cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port, oflags); udp->checksum = ip_csum_fold (sum); cnat_ip6_translate_l3 (ip6, new_addr); } @@ -743,6 +713,18 @@ cnat_session_make_key (vlib_buffer_t *b, ip_address_family_t af, session->key.cs_port[VLIB_RX] = udp->src_port; session->key.cs_port[VLIB_TX] = udp->dst_port; } + else if (ip4->protocol == IP_PROTOCOL_SCTP) + { + sctp_header_t *sctp; + sctp = (sctp_header_t *) (ip4 + 1); + ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX], + &ip4->dst_address); + ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX], + &ip4->src_address); + session->key.cs_proto = ip4->protocol; + session->key.cs_port[VLIB_RX] = sctp->src_port; + session->key.cs_port[VLIB_TX] = sctp->dst_port; + } else goto error; } @@ -837,20 +819,74 @@ cnat_load_balance (const cnat_translation_t *ct, ip_address_family_t af, * rsession_location is the location the (return) session will be * matched at */ + +static_always_inline void +cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx) +{ + cnat_bihash_kv_t *bkey = (cnat_bihash_kv_t *) session; + + session->value.cs_ts_index = cnat_timestamp_new (ctx->now); + cnat_bihash_add_del (&cnat_session_db, bkey, 1); +} + static_always_inline void -cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx, - cnat_session_location_t rsession_location, - u8 rsession_flags) +cnat_rsession_create (cnat_session_t *session, cnat_node_ctx_t *ctx, + cnat_session_location_t rsession_location, + cnat_session_flag_t rsession_flags) { cnat_client_t *cc; cnat_bihash_kv_t rkey; cnat_session_t *rsession = (cnat_session_t *) & rkey; cnat_bihash_kv_t *bkey = (cnat_bihash_kv_t *) session; - cnat_bihash_kv_t rvalue; - int rv; + int rv, n_retries = 0; + static u32 sport_seed = 0; - session->value.cs_ts_index = cnat_timestamp_new (ctx->now); - cnat_bihash_add_del (&cnat_session_db, bkey, 1); + cnat_timestamp_inc_refcnt (session->value.cs_ts_index); + + /* First create the return session */ + ip46_address_copy (&rsession->key.cs_ip[VLIB_RX], + &session->value.cs_ip[VLIB_TX]); + ip46_address_copy (&rsession->key.cs_ip[VLIB_TX], + &session->value.cs_ip[VLIB_RX]); + rsession->key.cs_proto = session->key.cs_proto; + rsession->key.cs_loc = rsession_location; + rsession->key.__cs_pad = 0; + rsession->key.cs_af = ctx->af; + rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX]; + rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX]; + + ip46_address_copy (&rsession->value.cs_ip[VLIB_RX], + &session->key.cs_ip[VLIB_TX]); + ip46_address_copy (&rsession->value.cs_ip[VLIB_TX], + &session->key.cs_ip[VLIB_RX]); + rsession->value.cs_ts_index = session->value.cs_ts_index; + rsession->value.cs_lbi = INDEX_INVALID; + rsession->value.flags = rsession_flags | CNAT_SESSION_IS_RETURN; + rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX]; + rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX]; + +retry_add_ression: + rv = cnat_bihash_add_del (&cnat_session_db, &rkey, + 2 /* add but don't overwrite */); + if (rv) + { + if (!(rsession_flags & CNAT_SESSION_RETRY_SNAT)) + return; + + /* return session add failed pick an new random src port */ + rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX] = + random_u32 (&sport_seed); + if (n_retries++ < 100) + goto retry_add_ression; + else + { + clib_warning ("Could not find a free port after 100 tries"); + /* translate this packet, but don't create state */ + return; + } + } + + cnat_bihash_add_del (&cnat_session_db, bkey, 1 /* add */); if (!(rsession_flags & CNAT_SESSION_FLAG_NO_CLIENT)) { @@ -894,39 +930,6 @@ cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx, } } - /* create the reverse flow key */ - ip46_address_copy (&rsession->key.cs_ip[VLIB_RX], - &session->value.cs_ip[VLIB_TX]); - ip46_address_copy (&rsession->key.cs_ip[VLIB_TX], - &session->value.cs_ip[VLIB_RX]); - rsession->key.cs_proto = session->key.cs_proto; - rsession->key.cs_loc = rsession_location; - rsession->key.__cs_pad = 0; - rsession->key.cs_af = ctx->af; - rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX]; - rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX]; - - /* First search for existing reverse session */ - rv = cnat_bihash_search_i2 (&cnat_session_db, &rkey, &rvalue); - if (!rv) - { - /* Reverse session already exists - cleanup before creating for refcnts */ - cnat_session_t *found_rsession = (cnat_session_t *) & rvalue; - cnat_session_free (found_rsession); - } - /* add the reverse flow */ - ip46_address_copy (&rsession->value.cs_ip[VLIB_RX], - &session->key.cs_ip[VLIB_TX]); - ip46_address_copy (&rsession->value.cs_ip[VLIB_TX], - &session->key.cs_ip[VLIB_RX]); - rsession->value.cs_ts_index = session->value.cs_ts_index; - rsession->value.cs_lbi = INDEX_INVALID; - rsession->value.flags = rsession_flags | CNAT_SESSION_IS_RETURN; - rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX]; - rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX]; - - cnat_bihash_add_del (&cnat_session_db, &rkey, 1); } always_inline uword |