Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: selftests: add PHY-loopback test for bad TCP checksums

Detect NICs and drivers that either drop frames with a corrupted TCP
checksum or, worse, pass them up as valid. The test flips one bit in
the checksum, transmits the packet in internal loopback, and fails when
the driver reports CHECKSUM_UNNECESSARY.

Discussed at:
https://lore.kernel.org/all/20250625132117.1b3264e8@kernel.org/

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250717083524.1645069-1-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
e7ce59d9 a6f19063

+65 -2
+65 -2
net/core/selftests.c
··· 27 27 int max_size; 28 28 u8 id; 29 29 u16 queue_mapping; 30 + bool bad_csum; 30 31 }; 31 32 32 33 struct net_test_priv { ··· 166 165 thdr->check = ~tcp_v4_check(l4len, ihdr->saddr, ihdr->daddr, 0); 167 166 skb->csum_start = skb_transport_header(skb) - skb->head; 168 167 skb->csum_offset = offsetof(struct tcphdr, check); 168 + 169 + if (attr->bad_csum) { 170 + /* Force mangled checksum */ 171 + if (skb_checksum_help(skb)) { 172 + kfree_skb(skb); 173 + return NULL; 174 + } 175 + 176 + if (thdr->check != CSUM_MANGLED_0) 177 + thdr->check = CSUM_MANGLED_0; 178 + else 179 + thdr->check = csum16_sub(thdr->check, 180 + cpu_to_be16(1)); 181 + } 169 182 } else { 170 183 udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr); 171 184 } ··· 254 239 if (tpriv->packet->id != shdr->id) 255 240 goto out; 256 241 257 - tpriv->ok = true; 242 + if (tpriv->packet->bad_csum && skb->ip_summed == CHECKSUM_UNNECESSARY) 243 + tpriv->ok = -EIO; 244 + else 245 + tpriv->ok = true; 246 + 258 247 complete(&tpriv->comp); 259 248 out: 260 249 kfree_skb(skb); ··· 304 285 attr->timeout = NET_LB_TIMEOUT; 305 286 306 287 wait_for_completion_timeout(&tpriv->comp, attr->timeout); 307 - ret = tpriv->ok ? 0 : -ETIMEDOUT; 288 + if (tpriv->ok < 0) 289 + ret = tpriv->ok; 290 + else if (!tpriv->ok) 291 + ret = -ETIMEDOUT; 292 + else 293 + ret = 0; 308 294 309 295 cleanup: 310 296 dev_remove_pack(&tpriv->pt); ··· 369 345 return __net_test_loopback(ndev, &attr); 370 346 } 371 347 348 + /** 349 + * net_test_phy_loopback_tcp_bad_csum - PHY loopback test with a deliberately 350 + * corrupted TCP checksum 351 + * @ndev: the network device to test 352 + * 353 + * Builds the same minimal Ethernet/IPv4/TCP frame as 354 + * net_test_phy_loopback_tcp(), then flips the least-significant bit of the TCP 355 + * checksum so the resulting value is provably invalid (neither 0 nor 0xFFFF). 356 + * The frame is transmitted through the device’s internal PHY loopback path: 357 + * 358 + * test code -> MAC driver -> MAC HW -> xMII -> PHY -> 359 + * internal PHY loopback -> xMII -> MAC HW -> MAC driver -> test code 360 + * 361 + * Result interpretation 362 + * --------------------- 363 + * 0 The frame is delivered to the stack and the driver reports 364 + * ip_summed as CHECKSUM_NONE or CHECKSUM_COMPLETE - both are 365 + * valid ways to indicate “bad checksum, let the stack verify.” 366 + * -ETIMEDOUT The MAC/PHY silently dropped the frame; hardware checksum 367 + * verification filtered it out before the driver saw it. 368 + * -EIO The driver returned the frame with ip_summed == 369 + * CHECKSUM_UNNECESSARY, falsely claiming a valid checksum and 370 + * indicating a serious RX-path defect. 371 + * 372 + * Return: 0 on success or a negative error code on failure. 373 + */ 374 + static int net_test_phy_loopback_tcp_bad_csum(struct net_device *ndev) 375 + { 376 + struct net_packet_attrs attr = { }; 377 + 378 + attr.dst = ndev->dev_addr; 379 + attr.tcp = true; 380 + attr.bad_csum = true; 381 + return __net_test_loopback(ndev, &attr); 382 + } 383 + 372 384 static const struct net_test { 373 385 char name[ETH_GSTRING_LEN]; 374 386 int (*fn)(struct net_device *ndev); ··· 428 368 }, { 429 369 .name = "PHY internal loopback, TCP ", 430 370 .fn = net_test_phy_loopback_tcp, 371 + }, { 372 + .name = "PHY loopback, bad TCP csum ", 373 + .fn = net_test_phy_loopback_tcp_bad_csum, 431 374 }, { 432 375 /* This test should be done after all PHY loopback test */ 433 376 .name = "PHY internal loopback, disable",