During the transition towards checksum offloading, the function to
handle software fallback of IPv4 checksums didn't account for the
options field.

Fixes: 5d11c47d3ebe ("userspace: Enable IP checksum offloading by default.")
Reported-by: Jun Wang <[email protected]>
Signed-off-by: Mike Pattrick <[email protected]>
---
v2:
 - Added tests
 - Added additional checks
---
 lib/dp-packet.h      | 31 ++++++++++++++++++++++++++++---
 tests/dpif-netdev.at | 24 ++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index a75b1c5cd..e816b9f20 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -529,6 +529,16 @@ dp_packet_inner_l3(const struct dp_packet *b)
            : NULL;
 }
 
+static inline size_t
+dp_packet_inner_l3_size(const struct dp_packet *b)
+{
+    return OVS_LIKELY(b->inner_l3_ofs != UINT16_MAX)
+           ? (const char *) dp_packet_tail(b)
+           - (const char *) dp_packet_inner_l3(b)
+           - dp_packet_l2_pad_size(b)
+           : 0;
+}
+
 static inline void *
 dp_packet_inner_l4(const struct dp_packet *b)
 {
@@ -1390,11 +1400,26 @@ dp_packet_hwol_l3_ipv4(const struct dp_packet *b)
 static inline void
 dp_packet_ip_set_header_csum(struct dp_packet *p, bool inner)
 {
-    struct ip_header *ip = (inner) ? dp_packet_inner_l3(p) : dp_packet_l3(p);
+    struct ip_header *ip;
+    size_t l3_size;
+    size_t ip_len;
+
+    if (inner) {
+        ip = dp_packet_inner_l3(p);
+        l3_size = dp_packet_inner_l3_size(p);
+    } else {
+        ip = dp_packet_l3(p);
+        l3_size = dp_packet_l3_size(p);
+    }
 
     ovs_assert(ip);
-    ip->ip_csum = 0;
-    ip->ip_csum = csum(ip, sizeof *ip);
+
+    ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
+
+    if (OVS_LIKELY(ip_len >= IP_HEADER_LEN && ip_len < l3_size)) {
+        ip->ip_csum = 0;
+        ip->ip_csum = csum(ip, ip_len);
+    }
 }
 
 /* Returns 'true' if the packet 'p' has good integrity and the
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index bdc24cc30..d31f13931 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -807,6 +807,30 @@ 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_expected}
 ])
+
+dnl Test with IP optional fields in a valid packet.
+optpkt=dnl
+["aaaaaaaaaaaabbbbbbbbbbbb0800"]dnl
+["4f000044abab00004001eeee0a0000010a000002"]dnl
+["07270c010203040a00000300000000"]dnl
+["00000000000000000000000000000000"]dnl
+["00000000000000000008003e2fb6d00000"]
+
+dnl IP header is indicates optional fields but doesn't contain any
+microgram="aaaaaaaaaaaabbbbbbbbbbbb08004f000044abab00004001eeee0a0000010a000002"
+
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum=true])
+AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_csum_set_good=true])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${optpkt}])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${microgram}])
+AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1])
+dnl The first packet has a checksum and the second doesn't
+AT_CHECK_UNQUOTED([tail -n 2 p2.pcap.txt], [0], [dnl
+[aaaaaaaaaaaabbbbbbbbbbbb08004f000044abab00004001dd2e0a000001c0a80101]dnl
+[07270c010203040a0000030000000000000000000000000000000000000000000000]dnl
+[00000000000008003e2fb6d00000]
+[aaaaaaaaaaaabbbbbbbbbbbb08004f000044abab00004001eeee0a0000010a000002]
+])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-- 
2.43.5

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to