Skip to content

Commit e9ef7b0

Browse files
committed
drivers: ethernet: xmc4xxx: Fixes the checksum of bridged packets
Due to checksum offloading, the checksum field of a packet must be zero before sending it to packet to the ethernet controller, otherwise the computed checksum will be incorrect. If the packet is bridged, it already has the correct checksum in place, which will cause the automatically computed checksum to incorrectly become zero. In this patch we give bridged packets the special attention they need. Signed-off-by: Marcelo Roberto Jimenez <marcelo.jimenez@gmail.com>
1 parent 9c1fbc8 commit e9ef7b0

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

drivers/ethernet/eth_xmc4xxx.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,123 @@ static inline void eth_xmc4xxx_trigger_dma_rx(ETH_GLOBAL_TypeDef *regs)
228228
regs->RECEIVE_POLL_DEMAND = 0U;
229229
}
230230

231+
static void pkt_hexdump(struct net_pkt *pkt, const char *str)
232+
{
233+
struct net_buf *buf = pkt->buffer;
234+
235+
while (buf) {
236+
LOG_HEXDUMP_DBG(buf->data, buf->len, str);
237+
buf = buf->frags;
238+
}
239+
}
240+
241+
/* Clears the checksum field of an ICMPv4 packet. */
242+
static int eth_xmc4xxx_clear_checksum_icmpv4(struct net_pkt *pkt)
243+
{
244+
NET_PKT_DATA_ACCESS_DEFINE(icmpv4_access, struct net_icmp_hdr);
245+
struct net_icmp_hdr *icmp_hdr;
246+
247+
if (IS_ENABLED(CONFIG_NET_IPV4_HDR_OPTIONS)) {
248+
if (net_pkt_skip(pkt, net_pkt_ipv4_opts_len(pkt))) {
249+
return -ENOBUFS;
250+
}
251+
}
252+
253+
icmp_hdr = (struct net_icmp_hdr *)net_pkt_get_data(pkt, &icmpv4_access);
254+
if (!icmp_hdr) {
255+
return -ENOBUFS;
256+
}
257+
258+
icmp_hdr->chksum = 0;
259+
net_pkt_set_chksum_done(pkt, true);
260+
261+
return net_pkt_set_data(pkt, &icmpv4_access);
262+
}
263+
264+
/* Clears the checksum value in packets that will go through checksum
265+
* offloading. */
266+
static int eth_xmc4xxx_clear_checksum(struct net_pkt *pkt)
267+
{
268+
int ret = 0;
269+
uint16_t p_type;
270+
uint8_t next_header = -1;
271+
struct net_eth_hdr *eth_hdr = NET_ETH_HDR(pkt);
272+
struct net_pkt_cursor backup;
273+
bool overwrite;
274+
const char *msg = NULL;
275+
const char *p_type_str;
276+
const char *next_header_str = "<does not apply>";
277+
278+
overwrite = net_pkt_is_being_overwritten(pkt);
279+
net_pkt_cursor_backup(pkt, &backup);
280+
net_pkt_cursor_init(pkt);
281+
net_pkt_set_overwrite(pkt, true);
282+
eth_hdr = NET_ETH_HDR(pkt);
283+
net_pkt_skip(pkt, sizeof(struct net_eth_hdr));
284+
p_type = ntohs(eth_hdr->type);
285+
switch (p_type) {
286+
case NET_ETH_PTYPE_IP: {
287+
NET_PKT_DATA_ACCESS_DEFINE(ip_access, struct net_ipv4_hdr);
288+
struct net_ipv4_hdr *ip_hdr;
289+
290+
p_type_str = "IPv4";
291+
ip_hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &ip_access);
292+
next_header = ip_hdr->proto;
293+
net_pkt_skip(pkt, sizeof(struct net_ipv4_hdr));
294+
295+
break;
296+
}
297+
case NET_ETH_PTYPE_IPV6: {
298+
NET_PKT_DATA_ACCESS_DEFINE(ip_access, struct net_ipv6_hdr);
299+
struct net_ipv6_hdr *ip_hdr;
300+
301+
p_type_str = "IPv6";
302+
ip_hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ip_access);
303+
next_header = ip_hdr->nexthdr;
304+
net_pkt_skip(pkt, sizeof(struct net_ipv6_hdr));
305+
306+
break;
307+
}
308+
case NET_ETH_PTYPE_ARP:
309+
p_type_str = "ARP";
310+
goto end;
311+
default:
312+
p_type_str = "Unknown";
313+
msg = "Unknown protocol type";
314+
goto end;
315+
}
316+
317+
switch (next_header) {
318+
case IPPROTO_TCP:
319+
next_header_str = "TCP";
320+
break;
321+
case IPPROTO_UDP:
322+
next_header_str = "UDP";
323+
break;
324+
case IPPROTO_ICMP:
325+
next_header_str = "ICMPv4";
326+
ret = eth_xmc4xxx_clear_checksum_icmpv4(pkt);
327+
break;
328+
case IPPROTO_ICMPV6:
329+
next_header_str = "ICMPv6";
330+
break;
331+
default:
332+
next_header_str = "Unknown";
333+
msg = "Unknown next header";
334+
break;
335+
}
336+
337+
end:
338+
net_pkt_cursor_restore(pkt, &backup);
339+
net_pkt_set_overwrite(pkt, overwrite);
340+
if (msg) {
341+
LOG_DBG("p_type = 0x%04x (%s), next_header = 0x%02x (%s)\n", p_type, p_type_str,
342+
next_header, next_header_str);
343+
pkt_hexdump(pkt, msg);
344+
}
345+
return ret;
346+
}
347+
231348
static int eth_xmc4xxx_send(const struct device *dev, struct net_pkt *pkt)
232349
{
233350
struct eth_xmc4xxx_data *dev_data = dev->data;
@@ -338,6 +455,19 @@ static int eth_xmc4xxx_send(const struct device *dev, struct net_pkt *pkt)
338455
return -EIO;
339456
}
340457

458+
/* Due to checksum offloading, the checksum field must be zero before
459+
* sending the packet to the ethernet controller, otherwise the computed
460+
* checksum will be incorrect.
461+
*
462+
* If the packet is bridged, it already has the correct checksum in
463+
* place, which will cause the automatically computed checksum to
464+
* incorrectly become zero.
465+
*
466+
* Here we give bridged packets the special attention they need. */
467+
if (net_pkt_is_l2_bridged(pkt)) {
468+
eth_xmc4xxx_clear_checksum(pkt);
469+
}
470+
341471
unsigned int key = irq_lock();
342472

343473
/* label last dma descriptor as last segment and trigger interrupt on last segment */

0 commit comments

Comments
 (0)