Skip to content

Commit 925e8e9

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 925e8e9

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

drivers/ethernet/eth_xmc4xxx.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,124 @@ 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+
*/
267+
static int eth_xmc4xxx_clear_checksum(struct net_pkt *pkt)
268+
{
269+
int ret = 0;
270+
uint16_t p_type;
271+
uint8_t next_header = -1;
272+
struct net_eth_hdr *eth_hdr = NET_ETH_HDR(pkt);
273+
struct net_pkt_cursor backup;
274+
bool overwrite;
275+
const char *msg = NULL;
276+
const char *p_type_str;
277+
const char *next_header_str = "<does not apply>";
278+
279+
overwrite = net_pkt_is_being_overwritten(pkt);
280+
net_pkt_cursor_backup(pkt, &backup);
281+
net_pkt_cursor_init(pkt);
282+
net_pkt_set_overwrite(pkt, true);
283+
eth_hdr = NET_ETH_HDR(pkt);
284+
net_pkt_skip(pkt, sizeof(struct net_eth_hdr));
285+
p_type = ntohs(eth_hdr->type);
286+
switch (p_type) {
287+
case NET_ETH_PTYPE_IP: {
288+
NET_PKT_DATA_ACCESS_DEFINE(ip_access, struct net_ipv4_hdr);
289+
struct net_ipv4_hdr *ip_hdr;
290+
291+
p_type_str = "IPv4";
292+
ip_hdr = (struct net_ipv4_hdr *)net_pkt_get_data(pkt, &ip_access);
293+
next_header = ip_hdr->proto;
294+
net_pkt_skip(pkt, sizeof(struct net_ipv4_hdr));
295+
296+
break;
297+
}
298+
case NET_ETH_PTYPE_IPV6: {
299+
NET_PKT_DATA_ACCESS_DEFINE(ip_access, struct net_ipv6_hdr);
300+
struct net_ipv6_hdr *ip_hdr;
301+
302+
p_type_str = "IPv6";
303+
ip_hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ip_access);
304+
next_header = ip_hdr->nexthdr;
305+
net_pkt_skip(pkt, sizeof(struct net_ipv6_hdr));
306+
307+
break;
308+
}
309+
case NET_ETH_PTYPE_ARP:
310+
p_type_str = "ARP";
311+
goto end;
312+
default:
313+
p_type_str = "Unknown";
314+
msg = "Unknown protocol type";
315+
goto end;
316+
}
317+
318+
switch (next_header) {
319+
case IPPROTO_TCP:
320+
next_header_str = "TCP";
321+
break;
322+
case IPPROTO_UDP:
323+
next_header_str = "UDP";
324+
break;
325+
case IPPROTO_ICMP:
326+
next_header_str = "ICMPv4";
327+
ret = eth_xmc4xxx_clear_checksum_icmpv4(pkt);
328+
break;
329+
case IPPROTO_ICMPV6:
330+
next_header_str = "ICMPv6";
331+
break;
332+
default:
333+
next_header_str = "Unknown";
334+
msg = "Unknown next header";
335+
break;
336+
}
337+
338+
end:
339+
net_pkt_cursor_restore(pkt, &backup);
340+
net_pkt_set_overwrite(pkt, overwrite);
341+
if (msg) {
342+
LOG_DBG("p_type = 0x%04x (%s), next_header = 0x%02x (%s)\n", p_type, p_type_str,
343+
next_header, next_header_str);
344+
pkt_hexdump(pkt, msg);
345+
}
346+
return ret;
347+
}
348+
231349
static int eth_xmc4xxx_send(const struct device *dev, struct net_pkt *pkt)
232350
{
233351
struct eth_xmc4xxx_data *dev_data = dev->data;
@@ -338,6 +456,20 @@ static int eth_xmc4xxx_send(const struct device *dev, struct net_pkt *pkt)
338456
return -EIO;
339457
}
340458

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

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

0 commit comments

Comments
 (0)