FreeRTOS + TCP ARP only resolves when request is initiated from remote

Hello,

I am exploring integrating the TCP stack on a STM32F7 platform. So far it’s been great, clear instructions on how to setup. It’s compiling and I manage to open a udp socket to periodically send a “hello world” message.

ARP requests are being sent to my “remote” computer and I can see the traffic on wireshark. I’m under the impression the computer replies to the ARP requests. Yet the ARP table does not seem to ever get updated, in the end the “hello world” frame is never transmitted.

It is however sufficient to send a single UDP package from the remote to the STM to initiate an ARP request in the opposite direction, and here everything unlocks. ARP is resolved, and my hello world message is being transmitted.

I am quite novice at the TCP stack. Is this expected? Is this a known issue?

what kind of infra structure is between your DUT and the peer?

What exctly do you mean “you are under the impression that the computer replies to the ARP requests?” What makes you think that?

Do you have wireshark traces of the traffic?

I should’ve included some traces you are right.

The following are the debug traces from RTOs (printed over UART)

=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2025.04.24 14:15:08 =~=~=~=~=~=~=~=~=~=~=~=^M
FreeRTOS_AddEndPoint: MAC: 44-55 IPv4: c0a80145ip
prvIPTask started
PHY ID 7C130
xPhyReset: phyBMCR_RESET 0 ready
+TCP: advertise: 01E1 config 3100
Autonego ready: 00000004: full duplex 100 mbit high status
Opened socket!^M
ARP c0a80164ip miss using c0a80164ip
HAL_ETH_RxLinkCallback: Buffer Dropped
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
HAL_ETH_RxLinkCallback: Buffer Dropped
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip
HAL_ETH_RxLinkCallback: Buffer Dropped
ARP c0a80164ip miss using c0a80164ip
ARP c0a80164ip miss using c0a80164ip

The tcpdump traces (from the ‘remote’) are as follows:

> sudo tcpdump -i enp5s0 arp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp5s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
14:19:53.376760 ARP, Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
14:19:53.376796 ARP, Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
14:19:59.678919 ARP, Request who-has 192.168.1.69 tell LL-PF3MRH7X-L.pc.goiba.net, length 28
14:19:59.688314 ARP, Reply 192.168.1.69 is-at 00:11:22:33:44:55 (oui Unknown), length 50
14:20:00.014921 ARP, Request who-has 192.168.1.69 tell 192.168.1.69, length 46
14:20:30.025393 ARP, Request who-has 192.168.1.69 tell 192.168.1.69, length 46
14:20:38.314691 ARP, Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
14:20:38.314730 ARP, Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
14:20:44.953629 ARP, Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
14:20:44.953648 ARP, Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
14:20:44.953659 ARP, Request who-has 192.168.1.69 tell 192.168.1.69, length 46
^C
11 packets captured
11 packets received by filter
0 packets dropped by kernel

I believe the HAL_ETH_RxLinkCallback: Buffer Dropped traces coincide with the ARP packet.

what kind of infra structure is between your DUT and the peer?

I’m not sure I understand your question. It’s a physical connection over ethernet port between “remote” and STM32.

it seems as if your dut is attempting to contact 192.168.1.100 which fails. Is that the ip address of your peer? Please elaborate on the configuration, what are the ip addresses of both your units as well as the configured default gateway and netmask? Source code of your dut conf sequence is most useful here.

There is an entry in the arp log which suggests that your dut is at 192.168.1.69, but that node also requests the arp resolution of a dns host name which seems strange.

I was really asking for a packet (wireshark or full tcpdump) trace of the network traffic.

The remote is configured with a static IPv4 address: 192.168.1.100/24

The dut (? I don’t know this abbreviation, google wasn’t helpful) is configured as follows:

IPv4: 192.168.1.69
netmask: 255.255.255.0
gatweay: 192.168.1.33

If I’m being honest, I just grabbed a random number in IP range for the gateway as to my understanding it shouldn’t come into play in this scenario. I might be wrong.

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

#include "FreeRTOS_IP.h"

//
static uint8_t ucMACAddress[ 6 ] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };

static const uint8_t ucIPAddress[ 4 ] = { 192, 168, 1, 69 };
static const uint8_t ucNetMask[ 4 ] = { 255, 255, 255, 0 };
static const uint8_t ucGatewayAddress[ 4 ] = { 192, 168, 1, 33 };
static const uint8_t ucDNSServerAddress[ 4 ] = { 208, 67, 222, 222 };

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

void StartDefaultTask(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  FreeRTOS_IPInit(ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress);
}

#define DEST_IP_ADDR  "192.168.1.100"   // Destination IP
#define DEST_PORT     5005              // Destination port
#define LOCAL_PORT    5000              // Local port (can be 0 for auto)

// Data to send
static const char *messageToSend = "Hello from STM32 over UDP!\r\n";
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
    Socket_t xSocket;
    struct freertos_sockaddr xDestinationAddress;
    BaseType_t xSendResult;

    // wait for IP task to be ready  // TODO use semaphore
    osDelay(1000);

    // Create the UDP socket
    xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
                              FREERTOS_SOCK_DGRAM,
                              FREERTOS_SOCK_DEPENDENT_PROTO );


    if (xSocket == FREERTOS_INVALID_SOCKET) {
        // Handle error
    	printf("Inavlid socket\r\n");
        vTaskDelete(NULL);
    }
    else
    {
    	printf("Opened socket!\r\n");
    }

    // Optional: bind to a local port
    struct freertos_sockaddr xBindAddress;
    xBindAddress.sin_port = FreeRTOS_htons(LOCAL_PORT);
    FreeRTOS_bind(xSocket, &xBindAddress, sizeof(xBindAddress));

    // Set destination address
    xDestinationAddress.sin_addr = FreeRTOS_inet_addr(DEST_IP_ADDR);
    xDestinationAddress.sin_port = FreeRTOS_htons(DEST_PORT);

    for(;;)
    {
		// Send the UDP message
		xSendResult = FreeRTOS_sendto(xSocket,
									  messageToSend,
									  strlen(messageToSend),
									  0,
									  &xDestinationAddress,
									  sizeof(xDestinationAddress));

		if (xSendResult < 0) {
			printf("Error\r\n");
		}

		osDelay(1000);
    }
}

I will update with a full tcpdump tomorrw, I don’t have access to my tools.

but if your dut (device under test) has the .69, the above arp request sounds odd, normally that is nothing a FreeRTOS unit would do. Could you have an IP address conflict in your net?

Hi,

I haven’t had the time to look at it in a while. Here’s a full tcp dump. I have to admit it is quite cryptic to me.

tcpdump: listening on enp5s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:48:31.523461 IP6 (hlim 1, next-header Options (0) payload length: 56) LL-PF3MRH7X-L.pc.goiba.net > ff02::16: HBH (rtalert: 0x0000) (padn) [icmp6 sum ok] ICMP6, multicast listener report v2, 2 group record(s) [gaddr ff02::1:ff55:c1e3 to_ex, 0 source(s)] [gaddr ff02::fb to_ex, 0 source(s)]
17:48:31.559998 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
17:48:31.560018 ARP, Ethernet (len 6), IPv4 (len 4), Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
17:48:32.022103 IP (tos 0x0, ttl 1, id 38000, offset 0, flags [DF], proto UDP (17), length 202)
    LL-PF3MRH7X-L.pc.goiba.net.59893 > 239.255.255.250.1900: UDP, length 174
17:48:32.252460 IP6 (hlim 1, next-header Options (0) payload length: 56) LL-PF3MRH7X-L.pc.goiba.net > ff02::16: HBH (rtalert: 0x0000) (padn) [icmp6 sum ok] ICMP6, multicast listener report v2, 2 group record(s) [gaddr ff02::1:ff55:c1e3 to_ex, 0 source(s)] [gaddr ff02::fb to_ex, 0 source(s)]
17:48:33.023194 IP (tos 0x0, ttl 1, id 38889, offset 0, flags [DF], proto UDP (17), length 202)
    LL-PF3MRH7X-L.pc.goiba.net.59893 > 239.255.255.250.1900: UDP, length 174
17:48:34.023497 IP (tos 0x0, ttl 1, id 39208, offset 0, flags [DF], proto UDP (17), length 202)
    LL-PF3MRH7X-L.pc.goiba.net.59893 > 239.255.255.250.1900: UDP, length 174
17:48:35.022945 IP (tos 0x0, ttl 1, id 51080, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:35.023013 IP (tos 0x0, ttl 1, id 51081, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:35.024123 IP (tos 0x0, ttl 1, id 39854, offset 0, flags [DF], proto UDP (17), length 202)
    LL-PF3MRH7X-L.pc.goiba.net.59893 > 239.255.255.250.1900: UDP, length 174
17:48:36.024185 IP (tos 0x0, ttl 1, id 51212, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:36.024284 IP (tos 0x0, ttl 1, id 51213, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:37.725936 IP6 (flowlabel 0xb20d6, hlim 255, next-header UDP (17) payload length: 149) LL-PF3MRH7X-L.pc.goiba.net.mdns > ff02::fb.mdns: [bad udp cksum 0xedeb -> 0x2c48!] 0 [9q] PTR (QM)? _nfs._tcp.local. PTR (QM)? _ipp._tcp.local. PTR (QM)? _ipps._tcp.local. PTR (QM)? _ftp._tcp.local. PTR (QM)? _webdav._tcp.local. PTR (QM)? _webdavs._tcp.local. PTR (QM)? _sftp-ssh._tcp.local. PTR (QM)? _smb._tcp.local. PTR (QM)? _afpovertcp._tcp.local. (141)
17:48:37.867091 IP (tos 0x0, ttl 255, id 51731, offset 0, flags [DF], proto UDP (17), length 169)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 [9q] PTR (QM)? _nfs._tcp.local. PTR (QM)? _ipp._tcp.local. PTR (QM)? _ipps._tcp.local. PTR (QM)? _ftp._tcp.local. PTR (QM)? _webdav._tcp.local. PTR (QM)? _webdavs._tcp.local. PTR (QM)? _sftp-ssh._tcp.local. PTR (QM)? _smb._tcp.local. PTR (QM)? _afpovertcp._tcp.local. (141)
17:48:38.024578 IP (tos 0x0, ttl 1, id 51855, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:38.024697 IP (tos 0x0, ttl 1, id 51856, offset 0, flags [DF], proto UDP (17), length 68)
    LL-PF3MRH7X-L.pc.goiba.net.mdns > mdns.mcast.net.mdns: 0 PTR (QM)? _googlecast._tcp.local. (40)
17:48:41.559685 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
17:48:41.559697 ARP, Ethernet (len 6), IPv4 (len 4), Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
17:48:41.563185 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.1.69 tell 192.168.1.69, length 46
17:48:51.559751 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has LL-PF3MRH7X-L.pc.goiba.net tell 192.168.1.69, length 46
17:48:51.559760 ARP, Ethernet (len 6), IPv4 (len 4), Reply LL-PF3MRH7X-L.pc.goiba.net is-at 6c:24:08:02:b8:49 (oui Unknown), length 28
17:48:51.967362 IP6 (flowlabel 0xa22cf, hlim 255, next-header ICMPv6 (58) payload length: 8) LL-PF3MRH7X-L.pc.goiba.net > ip6-allrouters: [icmp6 sum ok] ICMP6, router solicitation, length 8

Do you see anything off?

I will add that on full power cycle the chip manages to resolve the ARP autonomously. Resetting the mcu through the NRST input will result in the ARP never being resolved unless I send an UDP package from my host computer.

I still believe you have two machines in your net configured for the .69. If you submit a ping to that address from your PC and unplug your DUT, do you keep getting responses?

@DelicateMoomin

HAL_ETH_RxLinkCallback: Buffer Dropped

What are the configuration set for the macros ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES and ipconfigETHERNET_DRIVER_FILTERS_PACKETS? Do you see any changes/improvements when you disable both?

Also, you can check for the reason for buffer drop by putting breakpoints in those break statements inside the prvAcceptPacket call; that will give you more information on why those packets are dropped.

Negative, pinging when DUT is disconnected results in 100% packet loss.

I checked it but don’t remember the result. It seemed to me like the content of the packet was just garbage tbh. I will report back.

I will check thanks!

This worked! And it makes sense why the board power cycle worked, but it got stuck when only resetting the mcu.

Are there any ways to configure the driver to filter these properly?

Both these macros, if disabled, cause the IP task to filter the packets instead of the network driver task (EMAC task) using network driver-specific filtering functions.

Are there any ways to configure the driver to filter these properly?

Which version of FreeRTOS+TCP are you using? If using the newer versions then there are 2 network interfaces for STM32 - newer version and legacy. Please confirm which one is used.

I believe you previously had the ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES enabled and the ipconfigETHERNET_DRIVER_FILTERS_PACKETS disabled? If thats the case something is causing eConsiderFrameForProcessing to drop the packets, adding more debug logs or breakpoints could help isolate the issue.

A lot of your points finally put some sense into me. I made a few blunders leading up to this.

I just grabbed cloned the repo and went from main. I should’ve checked the tags for releases …

I had both enabled.

What happened is that eConsiderPacketForProcessing was not defined in the commit I was sitting in. So I simply removed that block of code for the time being (and promptly forgot about it).

In hindsight that was not so smart.

I am however confused

I would expect with ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES enabled that the ethernet driver is taking care of filtering MAC addresses. Why do we run eConsiderFrameForProcessing if supposedly the frame has been filtered by hardware already?

I am using the “modern” port, not the legacy.

With ipconfigETHERNET_DRIVER_FILTERS_PACKETS enabled and no call to eConsiderPacketForProcessing from the network interface, there is no packet filtering at all happening. You can take a look at what ipconfigETHERNET_DRIVER_FILTERS_PACKETS was doing in the legacy network interface for STM32.

Here, ETHERNET_DRIVER in the name ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES refers to the network interface RX task (the one that is running with the name EMAC_STM32), instead of the IP task of the FreeRTOS+TCP stack. The name is chosen such that when a network interface is implemented for hardware, it can use ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES to call driver-specific APIs for that hardware to filter out packets that might be more faster/efficient. However, for the latest STM32 network interface, provided with the network stack, both APIs are the same; the only difference with the macro is the task from which it’s called.

Thanks this is helpful. I’ll have to take a deep dive in it when I have the time.

It is somewhat still unclear to me why enabling ipconfigETHERNET_DRIVER_FILTERS_PACKETS seems to produce this inconsistent behaviour.

I believe since the ipconfigETHERNET_DRIVER_FILTERS_PACKETS is not yet fully implemented for newer STM32 interface, the ARP packets are dropped inside the prvAcceptPacket [probably as an ETH_IP_PAYLOAD_UNKNOWN packet].

You can verify if that’s the case by putting some breakpoints on those break statements.