Packets with L4 partial status for a simple match flow would not get L4
checksums offloads applied.

This was not caught in unit tests, because packets from netdev-dummy
(calling miniflow_extract) would get Tx flags set early, before
parse_tcp_flags() got called during packet processing.

Signed-off-by: David Marchand <david.march...@redhat.com>
---
 lib/flow.c           | 33 ++++++++++++++++---
 lib/netdev-dummy.c   |  3 +-
 lib/netdev-linux.c   | 58 ---------------------------------
 tests/dpif-netdev.at | 76 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 106 insertions(+), 64 deletions(-)

diff --git a/lib/flow.c b/lib/flow.c
index 61e732aff8..c2e1e4ad6f 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1271,6 +1271,10 @@ parse_tcp_flags(struct dp_packet *packet,
 
         size = tot_len;   /* Never pull padding. */
         data_pull(&data, &size, ip_len);
+        dp_packet_hwol_set_tx_ipv4(packet);
+        if (dp_packet_ip_checksum_good(packet)) {
+            dp_packet_hwol_set_tx_ip_csum(packet);
+        }
     } else if (dl_type == htons(ETH_TYPE_IPV6)) {
         const struct ovs_16aligned_ip6_hdr *nh = data;
         uint16_t plen;
@@ -1283,6 +1287,7 @@ parse_tcp_flags(struct dp_packet *packet,
         }
         data_pull(&data, &size, sizeof *nh);
 
+        dp_packet_hwol_set_tx_ipv6(packet);
         plen = ntohs(nh->ip6_plen); /* Never pull padding. */
         dp_packet_set_l2_pad_size(packet, size - plen);
         size = plen;
@@ -1300,11 +1305,29 @@ parse_tcp_flags(struct dp_packet *packet,
     }
 
     packet->l4_ofs = (uint16_t)((char *)data - frame);
-    if (!(nw_frag & FLOW_NW_FRAG_LATER) && nw_proto == IPPROTO_TCP &&
-        size >= TCP_HEADER_LEN) {
-        const struct tcp_header *tcp = data;
-
-        return TCP_FLAGS(tcp->tcp_ctl);
+    if (!(nw_frag & FLOW_NW_FRAG_LATER)) {
+        if (nw_proto == IPPROTO_TCP && size >= TCP_HEADER_LEN) {
+            const struct tcp_header *tcp = data;
+
+            dp_packet_ol_l4_csum_check_partial(packet);
+            if (dp_packet_l4_checksum_good(packet)
+                || dp_packet_ol_l4_csum_partial(packet)) {
+                dp_packet_hwol_set_csum_tcp(packet);
+            }
+            return TCP_FLAGS(tcp->tcp_ctl);
+        } else if (nw_proto == IPPROTO_UDP && size >= UDP_HEADER_LEN) {
+            dp_packet_ol_l4_csum_check_partial(packet);
+            if (dp_packet_l4_checksum_good(packet)
+                || dp_packet_ol_l4_csum_partial(packet)) {
+                dp_packet_hwol_set_csum_udp(packet);
+            }
+        } else if (nw_proto == IPPROTO_SCTP && size >= SCTP_HEADER_LEN) {
+            dp_packet_ol_l4_csum_check_partial(packet);
+            if (dp_packet_l4_checksum_good(packet)
+                || dp_packet_ol_l4_csum_partial(packet)) {
+                dp_packet_hwol_set_csum_sctp(packet);
+            }
+        }
     }
 
     return 0;
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index a54083ccd2..7d5abb87e3 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -1194,6 +1194,8 @@ netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct 
dp_packet_batch *batch,
     netdev->custom_stats[0].value++;
     netdev->custom_stats[1].value++;
 
+    *dp_packet_ol_flags_ptr(packet) &= ~DP_PACKET_OL_TX_ANY_CKSUM;
+
     if (netdev->ol_ip_rx_csum_set_good) {
         dp_packet_ol_set_ip_csum_good(packet);
     } else if (netdev->ol_ip_rx_csum_set_bad) {
@@ -1215,7 +1217,6 @@ netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct 
dp_packet_batch *batch,
     if (userspace_tso_enabled() && netdev->ol_tso_segsz) {
         dp_packet_set_tso_segsz(packet, netdev->ol_tso_segsz);
         dp_packet_hwol_set_tcp_seg(packet);
-        dp_packet_hwol_set_csum_tcp(packet);
     }
 
     if (VLOG_IS_DBG_ENABLED()) {
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 19bf62ecee..8a463c2efd 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -7038,55 +7038,6 @@ af_packet_sock(void)
     return sock;
 }
 
-static int
-netdev_linux_parse_l2(struct dp_packet *b, uint16_t *l4proto)
-{
-    struct eth_header *eth_hdr;
-    ovs_be16 eth_type;
-    int l2_len;
-
-    eth_hdr = dp_packet_at(b, 0, ETH_HEADER_LEN);
-    if (!eth_hdr) {
-        return -EINVAL;
-    }
-
-    l2_len = ETH_HEADER_LEN;
-    eth_type = eth_hdr->eth_type;
-    if (eth_type_vlan(eth_type)) {
-        struct vlan_header *vlan = dp_packet_at(b, l2_len, VLAN_HEADER_LEN);
-
-        if (!vlan) {
-            return -EINVAL;
-        }
-
-        eth_type = vlan->vlan_next_type;
-        l2_len += VLAN_HEADER_LEN;
-    }
-
-    if (eth_type == htons(ETH_TYPE_IP)) {
-        struct ip_header *ip_hdr = dp_packet_at(b, l2_len, IP_HEADER_LEN);
-
-        if (!ip_hdr) {
-            return -EINVAL;
-        }
-
-        *l4proto = ip_hdr->ip_proto;
-        dp_packet_hwol_set_tx_ipv4(b);
-    } else if (eth_type == htons(ETH_TYPE_IPV6)) {
-        struct ovs_16aligned_ip6_hdr *nh6;
-
-        nh6 = dp_packet_at(b, l2_len, IPV6_HEADER_LEN);
-        if (!nh6) {
-            return -EINVAL;
-        }
-
-        *l4proto = nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
-        dp_packet_hwol_set_tx_ipv6(b);
-    }
-
-    return 0;
-}
-
 /* Initializes packet 'b' with features enabled in the prepended
  * struct virtio_net_hdr.  Returns 0 if successful, otherwise a
  * positive errno value. */
@@ -7104,15 +7055,6 @@ netdev_linux_parse_vnet_hdr(struct dp_packet *b)
     }
 
     if (vnet->flags == VIRTIO_NET_HDR_F_NEEDS_CSUM) {
-        uint16_t l4proto = 0;
-
-        if (netdev_linux_parse_l2(b, &l4proto)) {
-            return EINVAL;
-        }
-
-        if (l4proto == IPPROTO_UDP) {
-            dp_packet_hwol_set_csum_udp(b);
-        }
         /* The packet has offloaded checksum. However, there is no
          * additional information like the protocol used, so it would
          * require to parse the packet here. The checksum starting point
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 4060e356ea..5f57537388 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -1087,6 +1087,82 @@ AT_CHECK([ovs-vsctl set Interface p1 
options:ol_l4_rx_csum_set_good=false])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([userspace offload - tcp csum offload (simple match)])
+OVS_VSWITCHD_START(
+  [add-br br1 -- set bridge br1 datapath-type=dummy -- \
+   add-port br1 p1 -- \
+       set Interface p1 type=dummy -- \
+   add-port br1 p2 -- \
+       set Interface p2 type=dummy --])
+
+AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg])
+
+AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=output:p2])
+
+flow_s="\
+  eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\
+  tcp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\
+  tcp_src=54392,tcp_dst=5201,tcp_flags=ack"
+
+good_frame=$(ovs-ofctl compose-packet --bare "${flow_s}")
+bad_frame=$(echo $good_frame | sed -e "s/6b72/dead/")
+
+AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${bad_frame}
+])
+dnl First packet, no simple matching.
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], 
[0], [dnl
+  simple match hits: 0
+])
+
+dnl No Rx flag.
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${bad_frame}
+])
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], 
[0], [dnl
+  simple match hits: 1
+])
+
+dnl Flag as Rx good.
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=true])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${bad_frame}
+])
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=false])
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], 
[0], [dnl
+  simple match hits: 2
+])
+
+dnl Flag as Rx bad.
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_bad=true])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${bad_frame}
+])
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_bad=false])
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], 
[0], [dnl
+  simple match hits: 3
+])
+
+dnl Flag as Rx partial.
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_partial=true])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${bad_frame}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${good_frame}
+])
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_partial=false])
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], 
[0], [dnl
+  simple match hits: 4
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([userspace offload - udp csum offload])
 OVS_VSWITCHD_START(
   [add-br br1 -- set bridge br1 datapath-type=dummy -- \
-- 
2.48.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to