Hello,
I integrated the very last sources of the Ethernet stack to a project and I think that MAC driver file "EMAC_stm32f4xx.c" is now not compatible with IP fragmentation. When a UDP fragmented frame is sent, the UDP checksum is calculated by the stack because the offload calculation applies only on one Ethernet frame. But is such a case, UDP checksum calculation offload must be deactived to avoid a bad UDP checksum put in the last fragmented frame by hardware. I modified the file as follow in order to not crush the UDP checksum previously calculated by the software :
static int32_t SendFrame (const uint8_t *frame, uint32_t len, uint32_t flags) { uint8_t *dst = Emac.frame_end; uint32_t ctrl; uint16_t fragOfs, prot; // Added to fix bug ... fragOfs = (((uint16_t)frame[20]) << 8) | frame[21]; // Added to fix bug prot = (((uint16_t)frame[12]) << 8) | frame[13]; // Added to fix bug ... #if (EMAC_CHECKSUM_OFFLOAD != 0) // Original code : // if (Emac.tx_cks_offload) { ctrl |= DMA_TX_CIC; } // Correction in case of fragmented frame : if ((prot == 0x0800) && ((fragOfs & (IP4_MF_FLAG | IP4_FRAG_MASK)) != 0) { // fragmented frame, only IP checksum is calculated if (Emac.tx_cks_offload) { ctrl |= DMA_TX_CIC & 0x00400000U; } }
PLZ DO PROPERLY. YOUR POST IS A MESS.
UDP checksum is not modified for fragmented IP frames.
RM0090 - Reference manual for STM32F4xx (DocID018909 Rev 15), page 1145
Transmit Checksum Offload:
b) Fragmented IP frames (IPv4 or IPv6), IP frames with security features (such as an authentication header or encapsulated security payload), and IPv6 frames with routing headers are bypassed and not processed by the checksum.
I've read that document and know that STM32F4 should not calculate the offload UDP checksum on fragmented frames . But it seems that there is a bug in STM32F407IG. The MAC driver code modified as below allows to obtain a right UDP checksum in such frames. See below the difference in second and last fragmented frame (payload size = 2000) where UDP checksum area is crushed by offload UDP checksum :
Wireshark result with EMAC driver NOT modified (ECHO response) :
Frame 25: 562 bytes on wire (4496 bits), 562 bytes captured (4496 bits) on interface 0 Ethernet II, Src: Faiveley_a2:45:5f (00:1b:79:a2:45:5f), Dst: Trendnet_da:2c:db (00:14:d1:da:2c:db) Internet Protocol Version 4, Src: 192.168.33.1, Dst: 192.168.33.2 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) 0000 00.. = Differentiated Services Codepoint: Default (0) .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) Total Length: 548 Identification: 0x0000 (0) Flags: 0x00 0... .... = Reserved bit: Not set .0.. .... = Don't fragment: Not set ..0. .... = More fragments: Not set Fragment offset: 1480 Time to live: 128 Protocol: UDP (17) Header checksum: 0x74bc [correct] [Header checksum status: Good] [Calculated Checksum: 0x74bc] Source: 192.168.33.1 Destination: 192.168.33.2 [Source GeoIP: Unknown] [Destination GeoIP: Unknown] [2 IPv4 Fragments (2008 bytes): #24(1480), #25(528)] [Frame: 24, payload: 0-1479 (1480 bytes)] [Frame: 25, payload: 1480-2007 (528 bytes)] [Fragment count: 2] [Reassembled IPv4 length: 2008] [Reassembled IPv4 data: 0007baea07d8eea5000102030405060708090a0b0c0d0e0f...] User Datagram Protocol, Src Port: echo (7), Dst Port: 47850 (47850) Source Port: echo (7) Destination Port: 47850 (47850) Length: 2008 Checksum: 0xeea5 incorrect, should be 0xb20b (maybe caused by "UDP checksum offload"?) [Expert Info (Error/Checksum): Bad checksum [should be 0xb20b]] [Bad checksum [should be 0xb20b]] [Severity level: Error] [Group: Checksum] [Calculated Checksum: 0xb20b] [Checksum Status: Bad] [Stream index: 3] Echo Echo data: 000102030405060708090a0b0c0d0e0f1011121314151617...
Frame (562 bytes): 0000 00 14 d1 da 2c db 00 1b 79 a2 45 5f 08 00 45 00 ....,...y.E_..E. 0010 02 24 00 00 00 b9 80 11 74 bc c0 a8 21 01 c0 a8 .$......t...!... 0020 21 02 c0 c1 c2 c3 c4 c5 42 8c c8 c9 ca cb cc cd !.......B....... 0030 ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd ................
Wireshark result with EMAC driver modified :
Frame 14: 562 bytes on wire (4496 bits), 562 bytes captured (4496 bits) on interface 0 Ethernet II, Src: Faiveley_a2:45:5f (00:1b:79:a2:45:5f), Dst: Trendnet_da:2c:db (00:14:d1:da:2c:db) Internet Protocol Version 4, Src: 192.168.33.1, Dst: 192.168.33.2 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) 0000 00.. = Differentiated Services Codepoint: Default (0) .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) Total Length: 548 Identification: 0x0000 (0) Flags: 0x00 0... .... = Reserved bit: Not set .0.. .... = Don't fragment: Not set ..0. .... = More fragments: Not set Fragment offset: 1480 Time to live: 128 Protocol: UDP (17) Header checksum: 0x74bc [correct] [Header checksum status: Good] [Calculated Checksum: 0x74bc] Source: 192.168.33.1 Destination: 192.168.33.2 [Source GeoIP: Unknown] [Destination GeoIP: Unknown] [2 IPv4 Fragments (2008 bytes): #13(1480), #14(528)] [Frame: 13, payload: 0-1479 (1480 bytes)] [Frame: 14, payload: 1480-2007 (528 bytes)] [Fragment count: 2] [Reassembled IPv4 length: 2008] [Reassembled IPv4 data: 0007baea07d82dd0000102030405060708090a0b0c0d0e0f...] User Datagram Protocol, Src Port: echo (7), Dst Port: 47850 (47850) Source Port: echo (7) Destination Port: 47850 (47850) Length: 2008 Checksum: 0x2dd0 [correct] [Calculated Checksum: 0x2dd0] [Checksum Status: Good] [Stream index: 1] Echo Echo data: 000102030405060708090a0b0c0d0e0f1011121314151617...
Frame (562 bytes): 0000 00 14 d1 da 2c db 00 1b 79 a2 45 5f 08 00 45 00 ....,...y.E_..E. 0010 02 24 00 00 00 b9 80 11 74 bc c0 a8 21 01 c0 a8 .$......t...!... 0020 21 02 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd !............... 0030 ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd ................
UDP checksum calculated in "net_udp_send" function when frame is fragmented (size > UDP MTU) is OK (verified using a breakpoint) but replaced in the frame on wire by the wrong offload checksum.
There must be another bug in checksum calculation, this time for IP header checksum : I use a Bombardier software tool that test the biggest fragmented frame acceptable by a software. The tool sends an ECHO request (RFC862), increasing the size of the UDP payload in each new request. In only ONE case, when payload size is 2958 (up to about 8000), the IP header offload checksum is bad ! I turned around this suppose hardware bug as follow :
// if (Emac.tx_cks_offload) { ctrl |= DMA_TX_CIC; } // Original code // Bug fixed on IP header checksum if ((prot == 0x0800) && ((fragOfs & (IP4_MF_FLAG | IP4_FRAG_MASK)) != 0)) { // Fragmented frame detected // STM32 bug turnaround on IP header checksum calculation if (tx_desc[Emac.tx_index].Size == 40) { // A frame is always minimum 60 bytes long by hardware (IEEE standard) tx_desc[Emac.tx_index].Size = 42; ((__packed uint16_t *)dst)[0] = 0; // Bytes added set to 0 } }
Remark 1: the STM32 sets the frame size the 60 when requested < 60 (IEEE standard, see RM0090) therefore modification has no effect on frame size on wire. Remark 2: not all 40 bytes length frames have a bad IP header checksum.
Yes you're right. The problem exists also in fragmented ICMP checksum calculation. We will correct the drivers.