Skip to main content
Mz.1
Associate III
May 8, 2023
Question

How to receive a 4k UDP packet when my MTU is 1500, using fragmented IP packets?

  • May 8, 2023
  • 21 replies
  • 10027 views

Hey,

I Have STM32H743Zi

I'm new at this, so please forgive me if I'm unclear.

I'm trying to receive 4k UDP packets but I'm getting only 1k each time.

I did a Google search and understand that if I want to receive a UDP packet of 4k I need to use fragmentation due to the MTU being 1500.

I tried to enable the below parameters:

IP_REASSEMBLY - enabled

MEMP_NUM_REASSDATA - 5

IP_REASS_MAX_PBUFS - 10

MEMP_NUM_FRAG_PBUF - 4

inside my callback function, I'm trying to read all the 4k data, I'm getting 1k in the first node but the next node in the pbuf is always NULL.

I also see on Wirshak messages with 1k

while I'm sending using Hrecules 4k for sure.

this is the callback function code:

void UDP_TX_RECEIVE_CALLBACK(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)

{

 char TxUdpPacket[SPI_PACKET_SIZE] = {0, };

 // Process each received fragment

 while (p != NULL)

 {

  // Copy fragment data to appropriate position in udpPacket array

  memcpy(&TxUdpPacket[p->tot_len - p->len], p->payload, p->len);

  // Move to the next fragment

  struct pbuf *next = p->next;

  pbuf_free(p);

  p = next;

 }

}

pls, help!!

thank you :)

This topic has been closed for replies.

21 replies

waclawek.jan
Super User
May 8, 2023

> I also see on Wirshak messages with 1k

> while I'm sending using Hrecules 4k for sure.

If Wireshark does not see 4k packets, why would STM32 see them?

JW

Mz.1
Mz.1Author
Associate III
May 8, 2023

@Community member​ 

sorry you are right, my mistake.

I see 4k on Wireshark as well now.

still no change...

but if I'm adding a breakpoint in the callback method, it won't stop if the message is more than 1500 bytes.

Mz.1
Mz.1Author
Associate III
May 8, 2023

@Community member​ any idea what I'm doing wrong?

Pavel A.
May 8, 2023

Depends on how the other party is connected to the network. The ETH controller of STM32743 supports jumbo (a.k.a. giant) packets up to ~9 KB. So you can send the whole 4KB in one piece if the switch allows this or the connection is direct.

Mz.1
Mz.1Author
Associate III
May 8, 2023

@Pavel A.​ 

This is interesting...

I ran the below command with Python:

>>> import psutil

>>> print(psutil.net_if_stats())

and got this information on the STM network adapter in the PC:

snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=9000)

 the connection is direct to the PC (between the PC and the STM)

is this what you meant?

if so, do you have any extra information that explains this "jumbo (a.k.a. giant) packets up to ~9 KB", and how you use it?

thank you for your answer!!

Pavel A.
May 8, 2023

@Mz.1​ what is 'STM network adapter in the PC'?

The jumbo packet size is not unique for the STM32 ETH. Many ethernet controllers found in desktop or notebook PCs have it too. For example Realtek PCIe GBe supports jumbo frame up to 9 KB as well.

For more info on jumbo frames in general, please google.

For STM32H743, search the ETH section of RM0433 for "jumbo".

It can be enabled by bit 16 in ETH_MACCR register.

Existing examples unfortunately do not demonstrate this feature.

Piranha
Principal III
May 10, 2023

The code in UDP_TX_RECEIVE_CALLBACK() is broken. The pbuf_free() has to be called once on the whole buffer chain, not on every segment.

While it's technically possible, nobody fragments UDP packets, because UDP does not guarantee neither the delivery, nor the order of the fragments. Therefore such a requirement sounds like just a wrong approach to the problem. If you would describe the requirements and goals, probably users would give you some better ideas for solutions. Generally with 4 KB packets, most likely one should implement an application layer protocol over TCP or UDP.

Mz.1
Mz.1Author
Associate III
May 11, 2023

@Piranha​ Hi, thanks for the answer!

Regarding the  UDP_TX_RECEIVE_CALLBACK() method, you are right but that was just an example of what I'm trying to implement and not the final code.

I will describe the task:

We have a Python code that runs on a Windows machine that sends every 1 sec UDP packet - size 4k (can be less than 4k, but the max size is 4k) to the STM32H743Zi device.

the PC and the STM are connected directly (RJ45)

The STM code uses LWIP and DMA.

I have this callback function that is called every time when there is an incoming UDP packet.

if the UDP packet the PC sends is more than 1500 bytes (due to the MTU limit) the callback function isn't called otherwise I see packets.

I need a solution for receiving UDP packets of more than 1500 bytes.

you said that packet fragmentation with UDP is not a good solution and that makes sense to me.

@Pavel A.​ mentioned above an interesting solution with the "Jumbo frames" - I looked online and I didn't find an example or lots of information about how to implement this.

if anybody has an idea/solution I would appreciate the help...

thanks :)

Pavel A.
May 11, 2023

When you send fragmented 4K UDP, will you see at least one fragment received in the low_level_input() ?

The easiest: send from PC 3 * 1.4K packets.

Mz.1
Mz.1Author
Associate III
May 14, 2023

@Pavel A.​ - I'm not sending a fragmented frame, I'm sending from the PC an entire 4k message.

I tried 2 scenarios:

  1. when I'm sending a smaller message of 1472 bytes (~1500 bytes) I see the message in low_level_input() method
  2. when I'm sending a 4k I don't see the message in low_level_input() method

maybe I need to configure specific parameters?

below is my lwipopts.h file:

/**

 ******************************************************************************

 * File Name     : Target/lwipopts.h

 * Description    : This file overrides LwIP stack default configuration

 *           done in opt.h file.

 ******************************************************************************

 * @attention

 *

 * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.

 * All rights reserved.</center></h2>

 *

 * This software component is licensed by ST under Ultimate Liberty license

 * SLA0044, the "License"; You may not use this file except in compliance with

 * the License. You may obtain a copy of the License at:

 *               www.st.com/SLA0044

 *

 ******************************************************************************

 */

/* Define to prevent recursive inclusion --------------------------------------*/

#ifndef __LWIPOPTS__H__

#define __LWIPOPTS__H__

#include "main.h"

/*-----------------------------------------------------------------------------*/

/* Current version of LwIP supported by CubeMx: 2.1.2 -*/

/*-----------------------------------------------------------------------------*/

/* Within 'USER CODE' section, code will be kept by default at each generation */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

#ifdef __cplusplus

 extern "C" {

#endif

/* STM32CubeMX Specific Parameters (not defined in opt.h) ---------------------*/

/* Parameters set in STM32CubeMX LwIP Configuration GUI -*/

/*----- WITH_RTOS disabled (Since FREERTOS is not set) -----*/

#define WITH_RTOS 0

/*----- CHECKSUM_BY_HARDWARE enabled -----*/

#define CHECKSUM_BY_HARDWARE 1

/*-----------------------------------------------------------------------------*/

/* LwIP Stack Parameters (modified compared to initialization value in opt.h) -*/

/* Parameters set in STM32CubeMX LwIP Configuration GUI -*/

/*----- Default value in ETH configuration GUI in CubeMx: 1524 -----*/

#define ETH_RX_BUFFER_SIZE 512

/*----- Value in opt.h for NO_SYS: 0 -----*/

#define NO_SYS 1

/*----- Value in opt.h for SYS_LIGHTWEIGHT_PROT: 1 -----*/

#define SYS_LIGHTWEIGHT_PROT 0

/*----- Value in opt.h for MEM_ALIGNMENT: 1 -----*/

#define MEM_ALIGNMENT 4

/*----- Default Value for H7 devices: 0x30044000 -----*/

#define LWIP_RAM_HEAP_POINTER 0x30044000

/*----- Value supported for H7 devices: 1 -----*/

#define LWIP_SUPPORT_CUSTOM_PBUF 1

/*----- Default Value for PBUF_POOL_SIZE: 16 ---*/

#define PBUF_POOL_SIZE 32

/*----- Default Value for PBUF_LINK_HLEN: 14 ---*/

#define PBUF_LINK_HLEN 1024

/*----- Default Value for PBUF_POOL_BUFSIZE: 592 ---*/

#define PBUF_POOL_BUFSIZE 4096

/*----- Value in opt.h for LWIP_ETHERNET: LWIP_ARP || PPPOE_SUPPORT -*/

#define LWIP_ETHERNET 1

/*----- Default Value for IP_REASSEMBLY: 1 ---*/

#define IP_REASSEMBLY 0

/*----- Value in opt.h for LWIP_DNS_SECURE: (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT) -*/

#define LWIP_DNS_SECURE 7

/*----- Value in opt.h for TCP_SND_QUEUELEN: (4*TCP_SND_BUF + (TCP_MSS - 1))/TCP_MSS -----*/

#define TCP_SND_QUEUELEN 9

/*----- Value in opt.h for TCP_SNDLOWAT: LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) -*/

#define TCP_SNDLOWAT 1071

/*----- Value in opt.h for TCP_SNDQUEUELOWAT: LWIP_MAX(TCP_SND_QUEUELEN)/2, 5) -*/

#define TCP_SNDQUEUELOWAT 5

/*----- Value in opt.h for TCP_WND_UPDATE_THRESHOLD: LWIP_MIN(TCP_WND/4, TCP_MSS*4) -----*/

#define TCP_WND_UPDATE_THRESHOLD 536

/*----- Value in opt.h for LWIP_NETIF_LINK_CALLBACK: 0 -----*/

#define LWIP_NETIF_LINK_CALLBACK 1

/*----- Value in opt.h for LWIP_NETCONN: 1 -----*/

#define LWIP_NETCONN 0

/*----- Value in opt.h for LWIP_SOCKET: 1 -----*/

#define LWIP_SOCKET 0

/*----- Value in opt.h for RECV_BUFSIZE_DEFAULT: INT_MAX -----*/

#define RECV_BUFSIZE_DEFAULT 2000000000

/*----- Value in opt.h for LWIP_STATS: 1 -----*/

#define LWIP_STATS 0

/*----- Value in opt.h for CHECKSUM_GEN_IP: 1 -----*/

#define CHECKSUM_GEN_IP 0

/*----- Value in opt.h for CHECKSUM_GEN_UDP: 1 -----*/

#define CHECKSUM_GEN_UDP 0

/*----- Value in opt.h for CHECKSUM_GEN_TCP: 1 -----*/

#define CHECKSUM_GEN_TCP 0

/*----- Value in opt.h for CHECKSUM_GEN_ICMP6: 1 -----*/

#define CHECKSUM_GEN_ICMP6 0

/*----- Value in opt.h for CHECKSUM_CHECK_IP: 1 -----*/

#define CHECKSUM_CHECK_IP 0

/*----- Value in opt.h for CHECKSUM_CHECK_UDP: 1 -----*/

#define CHECKSUM_CHECK_UDP 0

/*----- Value in opt.h for CHECKSUM_CHECK_TCP: 1 -----*/

#define CHECKSUM_CHECK_TCP 0

/*----- Value in opt.h for CHECKSUM_CHECK_ICMP6: 1 -----*/

#define CHECKSUM_CHECK_ICMP6 0

/*-----------------------------------------------------------------------------*/

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

#ifdef __cplusplus

}

#endif

#endif /*__LWIPOPTS__H__ */

/************************* (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

LCE
Principal II
May 17, 2023

Have you checked the descriptors' FIRST & LAST segment bits?

You must make sure that you build a correct pbuf chain which starts with data from FIRST segment, and ends with data from LAST segment. Only then it must be handed over to lwIP.

I don't know how well HAL stuff is handling all of that.

It looks like you have to dig a little deeper to find out what's going on between hardware and lwIP.

Mz.1
Mz.1Author
Associate III
May 18, 2023

@Community member​ - I'm not building the pbuf myself because I'm not sending the UDP packet from within the STM. I'm receiving the UDP packet from the PC:

let me explain the setup:

Windows PC uses simple Python code (with socket library) to send 4k UDP packet, the PC and the STM device are plugged directly with Ethernet cable (RJ45), and in the STM firmware I have simple code for a callback function (I used Google) such as this for example:

void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)

{

/* Copy the data from the pbuf */

strncpy (buffer, (char *)p->payload, p->len);

/* Free receive pbuf */

pbuf_free(p);

}

I didn't generate the pbuf (the low_level_input() method did).

this callback method works perfectly if the PC sends a packet with a size of 1472 bytes (~1500 bytes), if the PC sends more than that then the callback function is not called.

I need the callback function to work when I'm sending more than 1472 bytes (~1500 bytes).

@Pavel A.​ - suggested debugging the low_level_input() method, but it doesn't even enter the method if the packet is bigger.

therefore, I thought maybe I need to configure some parameters in the ioc LWIP section file to enable the packet to receive, so I saw online the fragmentation option and the jumbo frame option but I failed to do that.

that's why I need help :(

Pavel A.
May 18, 2023

>  low_level_input() method, but it doesn't even enter the method if the packet is bigger.

Recall that the low_level_input and its friends in ethernetif.c can work only if the setup has been done correctly (all these buffer arrays, descriptors...)

Are the buffer sizes enough to receive jumbo packets? Have you tried to enable jumbo packets in the ETH driver? Are any error callbacks of the ETH driver called?

LwIP parameters don't even have any significance before you get the low_level_input see the packet.

Or, just split your 4K packets and re-assemble in your code. Don't fight those wind mills.

LCE
Principal II
May 22, 2023

From RM0468 page 2922 for STM32H723..735:

To support Jumbo Transmit/Receive packets, follow these steps:

• In the Operating mode configuration register (ETH_MACCR)

a) Set JE bit to 1.

b) Set JD and WD bits to 0 to avoid giant packet error reporting.

c) Set GPSLCE bit to 1

d) Set GPSL bitfield of the Extended operating mode configuration register

(ETH_MACECR) to a value > 9026

To support Transmit/Receive packets, up to 16K, follow these steps:

• In the Operating mode configuration register (ETH_MACCR)

a) Set JD and WD bits to 1 to avoid giant packet error reporting.

b) Set GPSLCE bit to 1.

c) Set GPSL bitfield of the Extended operating mode configuration register

(ETH_MACECR) to 16383.

I never used that, but maybe the hardware discards any larger packets if these settings are not correct.

So check the registers and bits and change them if necessary.

BUT that does not mean that tha HAL driver supports that, so you also have to check the HAL stuff.

LCE
Principal II
May 22, 2023

Conclusion:

No matter what, you have to dive into the STM's ETH hardware, and into the HAL functions.

BTW, what's a method? :D

Mz.1
Mz.1Author
Associate III
May 22, 2023

@Community member​ - thank you !! I'll try what you suggested and update :)

> BTW, what's a method? :D

sorry I meant "function", my English is not so fluent :grinning_face_with_sweat:

LCE
Principal II
May 22, 2023

> sorry I meant "function", my English is not so fluent

Oops, sorry, no offense meant!

I actually thought that "method" is some C# or Python lingo... :grinning_face_with_sweat:

Piranha
Principal III
May 22, 2023

> what's a method?

Actually it's a generic term in OOP:

https://en.wikipedia.org/wiki/Method_(computer_programming)

As Pavel and LCE said, jumbo frames must be enabled in hardware, because by default that feature is disabled. Once you go over the standard MTU of 1500 bytes, you risk that some devices and computers will not support it. If the environment is fully controlled, then one can deal with it reasonably. If you intend going over the internet, then it's hopeless.

But there is another catch. The ETH hardware FIFO size is 2 KB for Tx and 2 KB for Rx. If you go over this size, you open another can of worms, because Store-and-Forward mode becomes impossible. First, you loose TCP, UDP and ICMP transmit checksum offload. Second, a DMA overflow/underflow becomes a reality and must be handled in code. Third, the driver code becomes more complex and I don't know whether the HAL ETH driver supports that mode at all.

I hope you do understand that ST's broken bloatware anyway has no chance of working reliably:

https://community.st.com/s/question/0D50X0000BOtfhnSQB/how-to-make-ethernet-and-lwip-working-on-stm32