From f5462369f3ad22c9d19f54832faa2b6e61449f66 Mon Sep 17 00:00:00 2001 From: Mohsin Kazmi Date: Tue, 23 Feb 2021 15:55:04 +0100 Subject: devices: add support for pseudo header checksum Type: improvement Linux uses pseudo header checksum when checksum of l4 is offloaded. This patch adds similar support in virtual interfaces. Change-Id: I6a94d1104e59356f95057e7c122e3be9cd8659a3 Signed-off-by: Aloys Augustin Signed-off-by: Mohsin Kazmi --- src/vnet/CMakeLists.txt | 1 + src/vnet/devices/af_packet/node.c | 4 --- src/vnet/devices/virtio/device.c | 42 +++++++++++++++++----- src/vnet/devices/virtio/vhost_user_output.c | 18 ++++++++-- src/vnet/ip/ip_psh_cksum.h | 54 +++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 src/vnet/ip/ip_psh_cksum.h (limited to 'src/vnet') diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 7b01d941d38..5e1cbc24fd1 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -458,6 +458,7 @@ list(APPEND VNET_HEADERS ip/ip_table.h ip/ip_interface.h ip/ip_packet.h + ip/ip_psh_cksum.h ip/ip_source_and_port_range_check.h ip/ip_types.h ip/lookup.h diff --git a/src/vnet/devices/af_packet/node.c b/src/vnet/devices/af_packet/node.c index caddcfa416b..e2f87b10b35 100644 --- a/src/vnet/devices/af_packet/node.c +++ b/src/vnet/devices/af_packet/node.c @@ -132,7 +132,6 @@ mark_tcp_udp_cksum_calc (vlib_buffer_t *b, u8 *l4_hdr_sz) tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip4_header_bytes (ip4)); - tcp->checksum = 0; *l4_hdr_sz = tcp_header_bytes (tcp); } else if (ip4->protocol == IP_PROTOCOL_UDP) @@ -141,7 +140,6 @@ mark_tcp_udp_cksum_calc (vlib_buffer_t *b, u8 *l4_hdr_sz) udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip4_header_bytes (ip4)); - udp->checksum = 0; *l4_hdr_sz = sizeof (*udp); } vnet_buffer (b)->l3_hdr_offset = sizeof (ethernet_header_t); @@ -172,7 +170,6 @@ mark_tcp_udp_cksum_calc (vlib_buffer_t *b, u8 *l4_hdr_sz) tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip6_hdr_len); - tcp->checksum = 0; *l4_hdr_sz = tcp_header_bytes (tcp); } else if (ip6->protocol == IP_PROTOCOL_UDP) @@ -181,7 +178,6 @@ mark_tcp_udp_cksum_calc (vlib_buffer_t *b, u8 *l4_hdr_sz) udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b) + sizeof (ethernet_header_t) + ip6_hdr_len); - udp->checksum = 0; *l4_hdr_sz = sizeof (*udp); } vnet_buffer (b)->l3_hdr_offset = sizeof (ethernet_header_t); diff --git a/src/vnet/devices/virtio/device.c b/src/vnet/devices/virtio/device.c index 6c2fe34abf0..ac9be6b02ae 100644 --- a/src/vnet/devices/virtio/device.c +++ b/src/vnet/devices/virtio/device.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -296,37 +297,60 @@ set_checksum_offsets (vlib_buffer_t *b, virtio_net_hdr_v1_t *hdr, 0 /* ip6 */ ); hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; // 0x22; + + /* + * virtio devices do not support IP4 checksum offload. So driver takes + * care of it while doing tx. + */ + ip4 = (ip4_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); + if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) + ip4->checksum = ip4_header_checksum (ip4); + + /* + * virtio devices assume the l4 header is set to the checksum of the + * l3 pseudo-header, so we compute it before tx-ing + */ if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM) { + tcp_header_t *tcp = + (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + tcp->checksum = ip4_pseudo_header_cksum (ip4); hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); } else if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM) { + udp_header_t *udp = + (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + udp->checksum = ip4_pseudo_header_cksum (ip4); hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum); } - - /* - * virtio devices do not support IP4 checksum offload. So driver takes care - * of it while doing tx. - */ - ip4 = - (ip4_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); - if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) - ip4->checksum = ip4_header_checksum (ip4); } else if (b->flags & VNET_BUFFER_F_IS_IP6) { + ip6_header_t *ip6; generic_header_offset_t gho = { 0 }; vnet_generic_header_offset_parser (b, &gho, is_l2, 0 /* ip4 */ , 1 /* ip6 */ ); hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; // 0x36; + ip6 = (ip6_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); + + /* + * virtio devices assume the l4 header is set to the checksum of the + * l3 pseudo-header, so we compute it before tx-ing + */ if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM) { + tcp_header_t *tcp = + (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + tcp->checksum = ip6_pseudo_header_cksum (ip6); hdr->csum_offset = STRUCT_OFFSET_OF (tcp_header_t, checksum); } else if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM) { + udp_header_t *udp = + (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + udp->checksum = ip6_pseudo_header_cksum (ip6); hdr->csum_offset = STRUCT_OFFSET_OF (udp_header_t, checksum); } } diff --git a/src/vnet/devices/virtio/vhost_user_output.c b/src/vnet/devices/virtio/vhost_user_output.c index 4efafa85333..15e39a11692 100644 --- a/src/vnet/devices/virtio/vhost_user_output.c +++ b/src/vnet/devices/virtio/vhost_user_output.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -208,27 +209,40 @@ vhost_user_handle_tx_offload (vhost_user_intf_t * vui, vlib_buffer_t * b, int is_ip4 = b->flags & VNET_BUFFER_F_IS_IP4; int is_ip6 = b->flags & VNET_BUFFER_F_IS_IP6; vnet_buffer_oflags_t oflags = vnet_buffer (b)->oflags; + u16 psh_cksum = 0; + ip4_header_t *ip4 = 0; + ip6_header_t *ip6 = 0; ASSERT (!(is_ip4 && is_ip6)); vnet_generic_header_offset_parser (b, &gho, 1 /* l2 */ , is_ip4, is_ip6); if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM) { - ip4_header_t *ip4; - ip4 = (ip4_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); ip4->checksum = ip4_header_checksum (ip4); + psh_cksum = ip4_pseudo_header_cksum (ip4); + } + else + { + ip6 = (ip6_header_t *) (vlib_buffer_get_current (b) + gho.l3_hdr_offset); + psh_cksum = ip6_pseudo_header_cksum (ip6); } /* checksum offload */ if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM) { + udp_header_t *udp = + (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + udp->checksum = psh_cksum; hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; hdr->csum_offset = offsetof (udp_header_t, checksum); } else if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM) { + tcp_header_t *tcp = + (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset); + tcp->checksum = psh_cksum; hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = gho.l4_hdr_offset; hdr->csum_offset = offsetof (tcp_header_t, checksum); diff --git a/src/vnet/ip/ip_psh_cksum.h b/src/vnet/ip/ip_psh_cksum.h new file mode 100644 index 00000000000..eaac401f223 --- /dev/null +++ b/src/vnet/ip/ip_psh_cksum.h @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2021 Cisco Systems, Inc. + */ + +#ifndef included_ip_psh_cksum_h +#define included_ip_psh_cksum_h + +#include + +typedef struct _ip4_psh +{ + ip4_address_t src; + ip4_address_t dst; + u8 zero; + u8 proto; + u16 l4len; +} ip4_psh_t; + +typedef struct _ip6_psh +{ + ip6_address_t src; + ip6_address_t dst; + u32 l4len; + u32 proto; +} ip6_psh_t; + +STATIC_ASSERT (sizeof (ip4_psh_t) == 12, "ipv4 pseudo header is 12B"); +STATIC_ASSERT (sizeof (ip6_psh_t) == 40, "ipv6 pseudo header is 40B"); + +static_always_inline u16 +ip4_pseudo_header_cksum (ip4_header_t *ip4) +{ + ip4_psh_t psh = { 0 }; + psh.src = ip4->src_address; + psh.dst = ip4->dst_address; + psh.proto = ip4->protocol; + psh.l4len = clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) - + sizeof (ip4_header_t)); + return ~clib_net_to_host_u16 (ip_csum (&psh, sizeof (ip4_psh_t))); +} + +static_always_inline u16 +ip6_pseudo_header_cksum (ip6_header_t *ip6) +{ + ip6_psh_t psh = { 0 }; + psh.src = ip6->src_address; + psh.dst = ip6->dst_address; + psh.l4len = ip6->payload_length; + psh.proto = clib_host_to_net_u32 ((u32) ip6->protocol); + return ~clib_net_to_host_u16 (ip_csum (&psh, sizeof (ip6_psh_t))); +} + +#endif /* included_ip_psh_cksum_h */ -- cgit 1.2.3-korg