diff options
author | John Lo <loj@cisco.com> | 2017-09-18 00:20:05 -0400 |
---|---|---|
committer | Dave Barach <openvpp@barachs.net> | 2017-09-19 10:46:50 +0000 |
commit | f4215a65cb5858b276b0b096273270e7c017209d (patch) | |
tree | 3e7cebdf5513d432894be4d6d575d6ac0f542978 | |
parent | 057704ebb51a006fbfd18dba570df1ca504b4277 (diff) |
Implement VXLAN encap tx checksum offload
Checksum offload is implemented in VXLAN encap over both IPv4 and
IPv6. It is enabled, however, only for VXLAN over IPv6 because UDP
checksum is needed only for IPv6 and optional for IPv4.
Change-Id: Ib879f4f6da7346ba5e079d321c1dfd630f5058b8
Signed-off-by: John Lo <loj@cisco.com>
-rw-r--r-- | src/vnet/vxlan/encap.c | 147 |
1 files changed, 108 insertions, 39 deletions
diff --git a/src/vnet/vxlan/encap.c b/src/vnet/vxlan/encap.c index 4cfbbc233c0..87e75e5db2d 100644 --- a/src/vnet/vxlan/encap.c +++ b/src/vnet/vxlan/encap.c @@ -69,7 +69,7 @@ always_inline uword vxlan_encap_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, - u32 is_ip4) + u8 is_ip4, u8 csum_offload) { u32 n_left_from, next_index, * from, * to_next; vxlan_main_t * vxm = &vxlan_main; @@ -200,20 +200,36 @@ vxlan_encap_inline (vlib_main_t * vm, copy_dst_last1[0] = copy_src_last1[0]; /* Fix the IP4 checksum and length */ - sum0 = ip4_0->checksum; - new_l0 = /* old_l0 always 0, see the rewrite setup */ - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, - length /* changed member */); - ip4_0->checksum = ip_csum_fold (sum0); - ip4_0->length = new_l0; - sum1 = ip4_1->checksum; - new_l1 = /* old_l1 always 0, see the rewrite setup */ - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); - sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, - length /* changed member */); - ip4_1->checksum = ip_csum_fold (sum1); - ip4_1->length = new_l1; + if (csum_offload) + { + ip4_0->length = clib_host_to_net_u16 + (vlib_buffer_length_in_chain (vm, b0)); + b0->flags |= + VNET_BUFFER_F_OFFLOAD_IP_CKSUM | VNET_BUFFER_F_IS_IP4; + vnet_buffer (b0)->l3_hdr_offset = (u8 *) ip4_0 - b0->data; + ip4_1->length = clib_host_to_net_u16 + (vlib_buffer_length_in_chain (vm, b1)); + b1->flags |= + VNET_BUFFER_F_OFFLOAD_IP_CKSUM | VNET_BUFFER_F_IS_IP4; + vnet_buffer (b1)->l3_hdr_offset = (u8 *) ip4_1 - b1->data; + } + else + { + sum0 = ip4_0->checksum; + new_l0 = /* old_l0 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, + length /* changed member */); + ip4_0->checksum = ip_csum_fold (sum0); + ip4_0->length = new_l0; + sum1 = ip4_1->checksum; + new_l1 = /* old_l1 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); + sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, + length /* changed member */); + ip4_1->checksum = ip_csum_fold (sum1); + ip4_1->length = new_l1; + } /* Fix UDP length and set source port */ udp0 = (udp_header_t *)(ip4_0+1); @@ -226,6 +242,15 @@ vxlan_encap_inline (vlib_main_t * vm, - sizeof (*ip4_1)); udp1->length = new_l1; udp1->src_port = flow_hash1; + + /* UDP checksum only if checksum offload is used */ + if (csum_offload) + { + b0->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b0)->l4_hdr_offset = (u8 *) udp0 - b0->data; + b1->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b1)->l4_hdr_offset = (u8 *) udp1 - b1->data; + } } else /* ipv6 */ { @@ -269,16 +294,28 @@ vxlan_encap_inline (vlib_main_t * vm, udp1->src_port = flow_hash1; /* IPv6 UDP checksum is mandatory */ - udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, - ip6_0, &bogus); - ASSERT(bogus == 0); - if (udp0->checksum == 0) - udp0->checksum = 0xffff; - udp1->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b1, - ip6_1, &bogus); - ASSERT(bogus == 0); - if (udp1->checksum == 0) - udp1->checksum = 0xffff; + if (csum_offload) + { + b0->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b0)->l3_hdr_offset = (u8 *) ip6_0 - b0->data; + vnet_buffer (b0)->l4_hdr_offset = (u8 *) udp0 - b0->data; + b1->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b1)->l3_hdr_offset = (u8 *) ip6_1 - b1->data; + vnet_buffer (b1)->l4_hdr_offset = (u8 *) udp1 - b1->data; + } + else + { + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b0, ip6_0, &bogus); + ASSERT(bogus == 0); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + udp1->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b1, ip6_1, &bogus); + ASSERT(bogus == 0); + if (udp1->checksum == 0) + udp1->checksum = 0xffff; + } } pkts_encapsulated += 2; @@ -398,13 +435,24 @@ vxlan_encap_inline (vlib_main_t * vm, copy_dst_last0[0] = copy_src_last0[0]; /* Fix the IP4 checksum and length */ - sum0 = ip4_0->checksum; - new_l0 = /* old_l0 always 0, see the rewrite setup */ - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, - length /* changed member */); - ip4_0->checksum = ip_csum_fold (sum0); - ip4_0->length = new_l0; + if (csum_offload) + { + ip4_0->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + b0->flags |= + VNET_BUFFER_F_OFFLOAD_IP_CKSUM | VNET_BUFFER_F_IS_IP4; + vnet_buffer (b0)->l3_hdr_offset = (u8 *) ip4_0 - b0->data; + } + else + { + sum0 = ip4_0->checksum; + new_l0 = /* old_l0 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, + length /* changed member */); + ip4_0->checksum = ip_csum_fold (sum0); + ip4_0->length = new_l0; + } /* Fix UDP length and set source port */ udp0 = (udp_header_t *)(ip4_0+1); @@ -412,6 +460,13 @@ vxlan_encap_inline (vlib_main_t * vm, - sizeof (*ip4_0)); udp0->length = new_l0; udp0->src_port = flow_hash0; + + /* UDP checksum only if checksum offload is used */ + if (csum_offload) + { + b0->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b0)->l4_hdr_offset = (u8 *) udp0 - b0->data; + } } else /* ip6 path */ @@ -441,11 +496,20 @@ vxlan_encap_inline (vlib_main_t * vm, udp0->src_port = flow_hash0; /* IPv6 UDP checksum is mandatory */ - udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, - ip6_0, &bogus); - ASSERT(bogus == 0); - if (udp0->checksum == 0) - udp0->checksum = 0xffff; + if (csum_offload) + { + b0->flags |= VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; + vnet_buffer (b0)->l3_hdr_offset = (u8 *) ip6_0 - b0->data; + vnet_buffer (b0)->l4_hdr_offset = (u8 *) udp0 - b0->data; + } + else + { + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b0, ip6_0, &bogus); + ASSERT(bogus == 0); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + } } pkts_encapsulated ++; @@ -508,7 +572,10 @@ vxlan4_encap (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { - return vxlan_encap_inline (vm, node, from_frame, /* is_ip4 */ 1); + /* Disable chksum offload as setup overhead in tx node is not worthwhile + for ip4 header checksum only, unless udp checksum is also required */ + return vxlan_encap_inline (vm, node, from_frame, /* is_ip4 */ 1, + /* csum_offload */ 0); } static uword @@ -516,7 +583,9 @@ vxlan6_encap (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { - return vxlan_encap_inline (vm, node, from_frame, /* is_ip4 */ 0); + /* Enable checksum offload for ip6 as udp checksum is mandatory, */ + return vxlan_encap_inline (vm, node, from_frame, /* is_ip4 */ 0, + /* csum_offload */ 1); } VLIB_REGISTER_NODE (vxlan4_encap_node) = { |