Skip to content

Commit e7ce59d

Browse files
oleremkuba-moo
authored andcommitted
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/[email protected]/ Signed-off-by: Oleksij Rempel <[email protected]> Reviewed-by: Simon Horman <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent a6f1906 commit e7ce59d

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

net/core/selftests.c

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct net_packet_attrs {
2727
int max_size;
2828
u8 id;
2929
u16 queue_mapping;
30+
bool bad_csum;
3031
};
3132

3233
struct net_test_priv {
@@ -165,6 +166,20 @@ static struct sk_buff *net_test_get_skb(struct net_device *ndev,
165166
thdr->check = ~tcp_v4_check(l4len, ihdr->saddr, ihdr->daddr, 0);
166167
skb->csum_start = skb_transport_header(skb) - skb->head;
167168
skb->csum_offset = offsetof(struct tcphdr, check);
169+
170+
if (attr->bad_csum) {
171+
/* Force mangled checksum */
172+
if (skb_checksum_help(skb)) {
173+
kfree_skb(skb);
174+
return NULL;
175+
}
176+
177+
if (thdr->check != CSUM_MANGLED_0)
178+
thdr->check = CSUM_MANGLED_0;
179+
else
180+
thdr->check = csum16_sub(thdr->check,
181+
cpu_to_be16(1));
182+
}
168183
} else {
169184
udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr);
170185
}
@@ -239,7 +254,11 @@ static int net_test_loopback_validate(struct sk_buff *skb,
239254
if (tpriv->packet->id != shdr->id)
240255
goto out;
241256

242-
tpriv->ok = true;
257+
if (tpriv->packet->bad_csum && skb->ip_summed == CHECKSUM_UNNECESSARY)
258+
tpriv->ok = -EIO;
259+
else
260+
tpriv->ok = true;
261+
243262
complete(&tpriv->comp);
244263
out:
245264
kfree_skb(skb);
@@ -285,7 +304,12 @@ static int __net_test_loopback(struct net_device *ndev,
285304
attr->timeout = NET_LB_TIMEOUT;
286305

287306
wait_for_completion_timeout(&tpriv->comp, attr->timeout);
288-
ret = tpriv->ok ? 0 : -ETIMEDOUT;
307+
if (tpriv->ok < 0)
308+
ret = tpriv->ok;
309+
else if (!tpriv->ok)
310+
ret = -ETIMEDOUT;
311+
else
312+
ret = 0;
289313

290314
cleanup:
291315
dev_remove_pack(&tpriv->pt);
@@ -345,6 +369,42 @@ static int net_test_phy_loopback_tcp(struct net_device *ndev)
345369
return __net_test_loopback(ndev, &attr);
346370
}
347371

372+
/**
373+
* net_test_phy_loopback_tcp_bad_csum - PHY loopback test with a deliberately
374+
* corrupted TCP checksum
375+
* @ndev: the network device to test
376+
*
377+
* Builds the same minimal Ethernet/IPv4/TCP frame as
378+
* net_test_phy_loopback_tcp(), then flips the least-significant bit of the TCP
379+
* checksum so the resulting value is provably invalid (neither 0 nor 0xFFFF).
380+
* The frame is transmitted through the device’s internal PHY loopback path:
381+
*
382+
* test code -> MAC driver -> MAC HW -> xMII -> PHY ->
383+
* internal PHY loopback -> xMII -> MAC HW -> MAC driver -> test code
384+
*
385+
* Result interpretation
386+
* ---------------------
387+
* 0 The frame is delivered to the stack and the driver reports
388+
* ip_summed as CHECKSUM_NONE or CHECKSUM_COMPLETE - both are
389+
* valid ways to indicate “bad checksum, let the stack verify.”
390+
* -ETIMEDOUT The MAC/PHY silently dropped the frame; hardware checksum
391+
* verification filtered it out before the driver saw it.
392+
* -EIO The driver returned the frame with ip_summed ==
393+
* CHECKSUM_UNNECESSARY, falsely claiming a valid checksum and
394+
* indicating a serious RX-path defect.
395+
*
396+
* Return: 0 on success or a negative error code on failure.
397+
*/
398+
static int net_test_phy_loopback_tcp_bad_csum(struct net_device *ndev)
399+
{
400+
struct net_packet_attrs attr = { };
401+
402+
attr.dst = ndev->dev_addr;
403+
attr.tcp = true;
404+
attr.bad_csum = true;
405+
return __net_test_loopback(ndev, &attr);
406+
}
407+
348408
static const struct net_test {
349409
char name[ETH_GSTRING_LEN];
350410
int (*fn)(struct net_device *ndev);
@@ -368,6 +428,9 @@ static const struct net_test {
368428
}, {
369429
.name = "PHY internal loopback, TCP ",
370430
.fn = net_test_phy_loopback_tcp,
431+
}, {
432+
.name = "PHY loopback, bad TCP csum ",
433+
.fn = net_test_phy_loopback_tcp_bad_csum,
371434
}, {
372435
/* This test should be done after all PHY loopback test */
373436
.name = "PHY internal loopback, disable",

0 commit comments

Comments
 (0)