FreeRTOS TCP4.0.0 Init and Loopback Problems


I just upgraded from TCP 3.1.0 to 4.0.0 The purpose of this update is to create multiple endpoints and communicate between them.

In order to be able to run TCP 4.0.0 in the first place, I had to put the declaration of the function pxZynq_FillInterfaceDescriptor in FreeRTOS_IP.h. Because I had to call it from main.cpp. Is it normal?

To enable communication between endpoints, I put the loopback code (below) at the beginning of the xZynqNetworkInterfaceOutput function.

   if( xCheckLoopback( pxBuffer, bReleaseAfterSend ) != 0 )
	  usGenerateProtocolChecksum( pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength, pdTRUE );
	  return pdTRUE;

Since none of the endpoints were aware of each other’s arp message, there was no communication. So I create a task that updates arp entries once in a second. The code is below.


		for(size_t i = 0; i < ipconfigARP_CACHE_ENTRIES; i++){

			if(xARPCache[i].ulIPAddress == FreeRTOS_inet_addr("")){
				xARPCache[i].ucAge = 0xff;
				xARPCache[i].ucValid = 1;
				xARPCache[i].xMACAddress = {0x00, 0x11, 0x22, 0x33, 0x44,0x6E};

			if(xARPCache[i].ulIPAddress == FreeRTOS_inet_addr("")){
				xARPCache[i].ucAge = 0xff;
				xARPCache[i].ucValid = 1;
				xARPCache[i].xMACAddress = {0x00, 0x11, 0x22, 0x33, 0x44, 0x64};

Of course, to do this, I removed the static keyword in front of the xARPCache variable.

I think endpoints should be aware of each other’s mac-ip address pair.

Is there a way I can solve this problem without changing the source code?

Happy coding.

Hi @ozanagma

Thank you for the detailed explanation of the problem.

As this is not part of header file this need to declared and can be done in your main.cpp , see the example here. It is required to ignore compiler warning.

Also To communicate between 2 endpoints on different subnet it has to be through the gateway.

Can you please check this readme and this demo mentioned here which shows initialisation of multiple endpoint and can be modified to communicate between them.
You can see the endpoint initialisation here onwards.

Can you please help in explaining us what is the use case to communicate between 2 endpoints on the same device using loopback ?

Are you trying to (ab)use the loopback address to communicate between distinct physical end points on your device? From my understanding, this is not possible as the loopback interface is an internal “logical” channel within a TCP stack that by definition bypasses the physical and link layers.

Hi @moninom1,

Now I understand why I am getting error. It is my mistake. Because function declearation in c but my compiler is g++ so I should decleare function in my main.cpp as below.

extern "C" {
NetworkInterface_t * pxZynq_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface );

I moved the ip addresses to different subnets due to a suggestion to another question in this forum. I have now restored it. My ip addresses are and and these addresses are in the same subnet. These addresses have different mac addresses. But there is no change.

My init sequence is below:

	const uint32_t card_0_ip_addr = FreeRTOS_inet_addr(CARD_0_IP);

	const uint8_t card_0_ip_arr[4] = {(uint8_t)((card_0_ip_addr >> 0) & 0xFF), (uint8_t)((card_0_ip_addr >> 8) & 0xFF), (uint8_t)((card_0_ip_addr >> 16) & 0xFF), (uint8_t)((card_0_ip_addr >> 24) & 0xFF)};
	const uint8_t card_0_mac_arr[6] = { CARD_0_MAC_ADDR0, CARD_0_MAC_ADDR1, CARD_0_MAC_ADDR2, CARD_0_MAC_ADDR3, CARD_0_MAC_ADDR4, CARD_0_MAC_ADDR5 };

	const uint32_t card_1_ip_addr = FreeRTOS_inet_addr(CARD_1_IP);
	const uint8_t card_1_ip_arr[4] = {(uint8_t)((card_1_ip_addr >> 0) & 0xFF), (uint8_t)((card_1_ip_addr >> 8) & 0xFF), (uint8_t)((card_1_ip_addr >> 16) & 0xFF), (uint8_t)((card_1_ip_addr >> 24) & 0xFF)};
	const uint8_t card_1_mac_arr[6] = { CARD_1_MAC_ADDR0, CARD_1_MAC_ADDR1, CARD_1_MAC_ADDR2, CARD_1_MAC_ADDR3, CARD_1_MAC_ADDR4, CARD_1_MAC_ADDR5 };

	const uint32_t gateway_addr = FreeRTOS_inet_addr(GATEWAY_IP);
	const uint32_t netmask_addr = FreeRTOS_inet_addr(NET_MASK);

	const uint8_t gateway_arr[4] = {(uint8_t)((gateway_addr >> 0) & 0xFF), (uint8_t)((gateway_addr >> 8) & 0xFF), (uint8_t)((gateway_addr >> 16) & 0xFF), (uint8_t)((gateway_addr >> 24) & 0xFF)};
	const uint8_t net_mask_arr[4] =	{(uint8_t)((netmask_addr >> 0) & 0xFF), (uint8_t)((netmask_addr >> 8) & 0xFF), (uint8_t)((netmask_addr >> 16) & 0xFF), (uint8_t)((netmask_addr >> 24) & 0xFF)};

	pxZynq_FillInterfaceDescriptor(0, &(xInterfaces[0]));

    FreeRTOS_FillEndPoint(&(xInterfaces[0]), &(xEndPoints[0]), card_0_ip_arr, net_mask_arr, gateway_arr, gateway_arr, card_0_mac_arr);

    FreeRTOS_FillEndPoint(&(xInterfaces[0]), &(xEndPoints[1]), card_1_ip_arr, net_mask_arr, gateway_arr, gateway_arr, card_1_mac_arr);



I want to test a library that uses Ethernet. If I want to simulate each ethernet user with a card, the test environment will become very large and expensive. For this reason, I reach my goal in a much more elegant way by running different card software on the same card. This is the reason for my request.

Have a nice day.

Hi @RAc ,

xCheckLoopback function has a brief top on that which is:

 * @brief  This function will check if the target IP-address belongs to this device.
 *         If so, the packet will be passed to the IP-stack, who will answer it.
 *         The function is to be called within the function xNetworkInterfaceOutput().

As far as I understand from the description, this function is already written to communicate with endpoints. Also the code looks like that. Most likely the code was written by the @htibosch. Maybe he can say something about his code.

As a result, what I did seems to be correct.

Have a nice day.

Hi @ozanagma

As we discussed using IP address on different subnet will not work unless you explicitly add entries in the ARP table or first check if valid endpoint is present with the entry, as you were already doing.

Can you please help in answering few queries :

  1. Were you able to communicate when the IP addresses on the same subnet? Without explicitly adding the ARP entries.
  2. What do you mean by no change here :

xCheckLoopback has not much updated between single/multi endpoint hence those manual updates are needed. @htibosch have a loopback driver which will also check the multicast address but that is a work in progress.

Hello @ozanagma, here is the loopback module that @moninom1 talked about: portable/NetworkInterface/loopback/NetworkInterface.c (5.6 KB)

It works quite differently from the earlier loopback function xCheckLoopback() in /single /IPv4.

It should be retested and we should get experience with it and make sure that prvLoopback_Output() won’t leak a network buffer when pxDuplicateNetworkBufferWithDescriptor() fails.

Please let us know when you try it.


Hi @moninom1,

First of all, there is another code that I forgot to write here, which is necessary for the loopback interface to work.

void vApplicationIPNetworkEventHook_Multi(eIPCallbackEvent_t eNetworkEvent, struct xNetworkEndPoint * pxEndPoint)
    uint32_t card_0_ip_address = FreeRTOS_inet_addr(CARD_0_IP);

    MACAddress_t card_0_mac_address;
    card_0_mac_address.ucBytes[0] = CARD_0_MAC_ADDR0;
    card_0_mac_address.ucBytes[1] = CARD_0_MAC_ADDR1;
    card_0_mac_address.ucBytes[2] = CARD_0_MAC_ADDR2;
    card_0_mac_address.ucBytes[3] = CARD_0_MAC_ADDR3;
    card_0_mac_address.ucBytes[4] = CARD_0_MAC_ADDR4;
    card_0_mac_address.ucBytes[5] = CARD_0_MAC_ADDR5;

    uint32_t card_1_ip_address = FreeRTOS_inet_addr(CARD_1_IP);

    MACAddress_t card_1_mac_address;
    card_1_mac_address.ucBytes[0] = CARD_1_MAC_ADDR0;
    card_1_mac_address.ucBytes[1] = CARD_1_MAC_ADDR1;
    card_1_mac_address.ucBytes[2] = CARD_1_MAC_ADDR2;
    card_1_mac_address.ucBytes[3] = CARD_1_MAC_ADDR3;
    card_1_mac_address.ucBytes[4] = CARD_1_MAC_ADDR4;
    card_1_mac_address.ucBytes[5] = CARD_1_MAC_ADDR5;

   if (eNetworkEvent == eNetworkUp){

	   if(pxEndPoint->ipv4_defaults.ulIPAddress == card_0_ip_address){
		   vARPRefreshCacheEntry(&card_1_mac_address, card_1_ip_address, pxEndPoint);

	   }else if(pxEndPoint->ipv4_defaults.ulIPAddress == card_1_ip_address){
		   vARPRefreshCacheEntry(&card_0_mac_address, card_0_ip_address, pxEndPoint);


With this code I add the other endpoint to the arp table.

After you answered, I do tests again this morning and I am giving my answers accordingly. I can answer your questions like this:

  1. I can’t communicate even when my ips are in the same subnet without explicitly adding the ARP entries. If the hook function is empty, I can’t speak at all. If I fill in the hook function as above but don’t run the task that updates the arp table, I can only communicate for 1500 seconds. Because I can’t get a response to the arp package sent near the end of the arp age. For this reason, the task that updates the arp table also needs to run continuously.
  2. It doesn’t matter if it’s on the same subnet or not. I see the same result. If I do the same things I did above for ip addresses in separate subnets, I can still communicate.

Thank you for your answer. Enjoy your work.

Hello @htibosch,

Thank you for your sharing.

I copy to code that you share, to a file named LoopbackNetworkInterface.c. Then I put it in the same place as the file called NetworkInterface.c.

When I give the ip address of to this interface, it returns from this line. For this reason, I had to give an ordinary ip address.

But still my arp problem still persisted. This is how I solved the problem:
LoopbackNetworkInterface.c (5.9 KB)

This solution:

  1. Refreshes the arp table every time we send a packet.
  2. Fills the checksum field that cannot be filled by the driver.
  3. Does not require an additional task for the arp table.
  4. Makes it unnecessary to fill inside the hook function.
  5. Allows us to keep the arp table static.

Unfortunately it didn’t work without making these changes.

Have a nice day.

In a meanwhile things have changed. Please have a look at PR #1020 and PR #1022
You will need both PRs for testing the new loopback driver.

1 Like