]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: enetc: update UDP checksum when updating originTimestamp field
authorWei Fang <wei.fang@nxp.com>
Mon, 24 Feb 2025 11:12:48 +0000 (19:12 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 11:50:59 +0000 (12:50 +0100)
commit bbcbc906ab7b5834c1219cd17a38d78dba904aa0 upstream.

There is an issue with one-step timestamp based on UDP/IP. The peer will
discard the sync packet because of the wrong UDP checksum. For ENETC v1,
the software needs to update the UDP checksum when updating the
originTimestamp field, so that the hardware can correctly update the UDP
checksum when updating the correction field. Otherwise, the UDP checksum
in the sync packet will be wrong.

Fixes: 7294380c5211 ("enetc: support PTP Sync packet one-step timestamping")
Cc: stable@vger.kernel.org
Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Tested-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20250224111251.1061098-6-wei.fang@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/ethernet/freescale/enetc/enetc.c

index 728e93009528c69dae953125253e95c8969875c7..b2a8b8136f83d3b45312e8f2d2e310ac10daf0bf 100644 (file)
@@ -228,9 +228,11 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
                }
 
                if (do_onestep_tstamp) {
-                       u32 lo, hi, val;
-                       u64 sec, nsec;
+                       __be32 new_sec_l, new_nsec;
+                       u32 lo, hi, nsec, val;
+                       __be16 new_sec_h;
                        u8 *data;
+                       u64 sec;
 
                        lo = enetc_rd_hot(hw, ENETC_SICTR0);
                        hi = enetc_rd_hot(hw, ENETC_SICTR1);
@@ -244,13 +246,38 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
                        /* Update originTimestamp field of Sync packet
                         * - 48 bits seconds field
                         * - 32 bits nanseconds field
+                        *
+                        * In addition, the UDP checksum needs to be updated
+                        * by software after updating originTimestamp field,
+                        * otherwise the hardware will calculate the wrong
+                        * checksum when updating the correction field and
+                        * update it to the packet.
                         */
                        data = skb_mac_header(skb);
-                       *(__be16 *)(data + offset2) =
-                               htons((sec >> 32) & 0xffff);
-                       *(__be32 *)(data + offset2 + 2) =
-                               htonl(sec & 0xffffffff);
-                       *(__be32 *)(data + offset2 + 6) = htonl(nsec);
+                       new_sec_h = htons((sec >> 32) & 0xffff);
+                       new_sec_l = htonl(sec & 0xffffffff);
+                       new_nsec = htonl(nsec);
+                       if (udp) {
+                               struct udphdr *uh = udp_hdr(skb);
+                               __be32 old_sec_l, old_nsec;
+                               __be16 old_sec_h;
+
+                               old_sec_h = *(__be16 *)(data + offset2);
+                               inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
+                                                        new_sec_h, false);
+
+                               old_sec_l = *(__be32 *)(data + offset2 + 2);
+                               inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
+                                                        new_sec_l, false);
+
+                               old_nsec = *(__be32 *)(data + offset2 + 6);
+                               inet_proto_csum_replace4(&uh->check, skb, old_nsec,
+                                                        new_nsec, false);
+                       }
+
+                       *(__be16 *)(data + offset2) = new_sec_h;
+                       *(__be32 *)(data + offset2 + 2) = new_sec_l;
+                       *(__be32 *)(data + offset2 + 6) = new_nsec;
 
                        /* Configure single-step register */
                        val = ENETC_PM0_SINGLE_STEP_EN;