Hi Mike,

Thanks for working on this issue.

On Fri, Jan 14, 2022 at 10:45:35AM -0500, Mike Pattrick wrote:
> Formerly when userspace TSO was enabled but with a non-DKDK interface
> without support IP checksum offloading, FTP NAT connections would fail
> if the packet length changed. This can happen if the packets length
> changes during L7 NAT translation, predominantly with FTP.
> 
> Now we correct the IP header checksum if hwol is disabled or if DPDK
> will not handle the IP checksum. This fixes the conntrack - IPv4 FTP
> Passive with DNAT" test when run with check-system-tso.
> 
> Reported-by: Flavio Leitner <f...@sysclose.org>

Actually, this was initially reported by Ilya.

> Signed-off-by: Mike Pattrick <m...@redhat.com>
> ---
>  lib/conntrack.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index 33a1a9295..1b8a26ac2 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -3402,7 +3402,8 @@ handle_ftp_ctl(struct conntrack *ct, const struct 
> conn_lookup_ctx *ctx,
>                  }
>                  if (seq_skew) {
>                      ip_len = ntohs(l3_hdr->ip_tot_len) + seq_skew;
> -                    if (!dp_packet_hwol_is_ipv4(pkt)) {
> +                    if (!dp_packet_hwol_is_ipv4(pkt) ||
> +                        !dp_packet_ip_checksum_valid(pkt)) {
>                          l3_hdr->ip_csum = recalc_csum16(l3_hdr->ip_csum,
>                                                          l3_hdr->ip_tot_len,
>                                                          htons(ip_len));

The problem is that the current code doesn't include IPv4 csum
handling as required by the Linux software ports.

The patch above resolves the unit test issue because non-DPDK
interfaces will not flag the packet with good IP csum, and then
the csum is updated accordingly. However, a packet coming from
a physical DPDK port can have that flag set by the PMD, then if
it goes through that part the IP csum is not updated, which
will cause a problem if that packet is sent out over a Linux
software port later.

A better solution would to have the IP csum updated if the
packet requires and the port doesn't support that as proposed
in the RFC[1]. 

[1] https://mail.openvswitch.org/pipermail/ovs-dev/2021-December/389993.html
    [See: dp_packet_ol_send_prepare()]

Perhaps we can achieve something in the middle with something
like below (not tested).

What do you think?

diff --git a/lib/conntrack.c b/lib/conntrack.c
index 33a1a9295..9e0364719 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -3403,6 +3403,8 @@ handle_ftp_ctl(struct conntrack *ct, const struct 
conn_lookup_ctx *ctx,
                 if (seq_skew) {
                     ip_len = ntohs(l3_hdr->ip_tot_len) + seq_skew;
                     if (!dp_packet_hwol_is_ipv4(pkt)) {
+                        dp_packet_ip_checksum_reset_valid(pkt);
+                    } else {
                         l3_hdr->ip_csum = recalc_csum16(l3_hdr->ip_csum,
                                                         l3_hdr->ip_tot_len,
                                                         htons(ip_len));
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index ee0805ae6..8a880a0ce 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -25,6 +25,7 @@
 #include <rte_mbuf.h>
 #endif
 
+#include "csum.h"
 #include "netdev-afxdp.h"
 #include "netdev-dpdk.h"
 #include "openvswitch/list.h"
@@ -1064,6 +1065,20 @@ dp_packet_ip_checksum_valid(const struct dp_packet *p)
             DP_PACKET_OL_RX_IP_CKSUM_GOOD;
 }
 
+/* Sets IP valid checksum flag in packet 'p'. */
+static inline void
+dp_packet_ip_checksum_set_valid(const struct dp_packet *p)
+{
+    *dp_packet_ol_flags_ptr(p) |= DP_PACKET_OL_RX_IP_CKSUM_GOOD;
+}
+
+/* Resets IP valid checksum flag in packet 'p'. */
+static inline void
+dp_packet_ip_checksum_reset_valid(const struct dp_packet *p)
+{
+    *dp_packet_ol_flags_ptr(p) &= ~DP_PACKET_OL_RX_IP_CKSUM_GOOD;
+}
+
 static inline bool
 dp_packet_ip_checksum_bad(const struct dp_packet *p)
 {
@@ -1071,6 +1086,17 @@ dp_packet_ip_checksum_bad(const struct dp_packet *p)
             DP_PACKET_OL_RX_IP_CKSUM_BAD;
 }
 
+/* Calculate and set the IPv4 header checksum in packet 'p'. */
+static inline void
+dp_packet_ip_set_header_csum(struct dp_packet *p)
+{
+    struct ip_header *ip = dp_packet_l3(p);
+
+    ovs_assert(ip);
+    ip->ip_csum = 0;
+    ip->ip_csum = csum(ip, sizeof *ip);
+}
+
 static inline bool
 dp_packet_l4_checksum_valid(const struct dp_packet *p)
 {
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 620a451de..a2a87ab92 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -932,7 +932,6 @@ netdev_linux_common_construct(struct netdev *netdev_)
         netdev_->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
         netdev_->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
         netdev_->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
     }
 
     return 0;
diff --git a/lib/netdev.c b/lib/netdev.c
index 8305f6c42..5bbde02af 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -794,6 +794,15 @@ netdev_send_prepare_packet(const uint64_t netdev_flags,
 {
     uint64_t l4_mask;
 
+    if (dp_packet_hwol_is_ipv4(packet)
+        && !dp_packet_ip_checksum_valid(packet)
+        && !(netdev_flags & NETDEV_TX_OFFLOAD_IPV4_CKSUM)) {
+        /* The packet requested IPv4 offload and the netdev
+         * doesn't support that. */
+        dp_packet_ip_set_header_csum(packet);
+        dp_packet_ip_checksum_set_valid(packet);
+    }
+
     if (dp_packet_hwol_is_tso(packet)
         && !(netdev_flags & NETDEV_TX_OFFLOAD_TCP_TSO)) {
             /* Fall back to GSO in software. */



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

Reply via email to