FreeRTOS TCP 4.0.0 Port Binding Problem

Hi,

Yesterday I downloaded FreeRTOS 4.0.0. I do my tests on Zynq.
I initialized one interface and two endpoints. I try to send periodic message from both endpoints but I am getting an error from the bind function because I use the same port number for both endpoints. The reason is that FreeRTOS TCP 4.0.0 has only one xBoundUDPSocketsList. Is it normal for multi ethernet interface?

Have a good work.

Are you implying that this is a new behavior in 4.0.0, behaving differently from previous versions?

If not, I would argue that this is expected behavior as two distinct endpoints on the same target would need different port/IP address combos to allow the TCP stack to direct incoming packets to a distinct endpoint.

Hi RAc,

I have two distinct endpoints. 192.168.1.100 is the IP address of first one. 192.168.1.101 is the other one. I created two sockets . First socket is created from 192.168.1.100 adress and 17000 port. Second socket is created form 192.168.1.101 adress and 17000 port. But second socket’s bind function gives an error. The word Socket is the combination of port and IP address. So the first combo(192.168.1.100/17000) is different than the second(192.1.68.1.101/1700) one. For that reason it shouldn’t give error. Is there something I’m thinking wrong?

Hi @ozanagma,

Thanks for checking out v4.0.0 of FreeRTOS+TCP and posting your feedback.

As you have already figured, the entire range of port numbers is shared between all the available endpoints/interfaces; there are no endpoint/interface specific socket-port binding lists for either TCP or UDP. Is there a specific use case that requires you to use the same port numbers?

No, we are perfectly in sync, you just didn’t state explicitly that you bind to distinct IP addresses. However, is it intended that both IP addresses are in the same subnet?

Again, did it work without error in previous releases?

Sorry for the missing information. I want it to work on the same subnet.

I don’t know because this is the first version of TCP 4.0.0. that I have used.

Hi @tony-josi-aws

I was using multiple cards to be able to test a library that uses Ethernet. These cards were using TCP 3.1. I found out that there is multi ethernet in TCP 4.0.0. I thought if I put the eth physical into loopback mode it could do it on a single board. For this reason, I copied the code on the second card to the first without changing it and I got the error I mentioned above. It’s definitely not a must-have feature, but that’s how the problem came to me.

Can you share the code that sets up the sockets?

Perhaps there should be a list of port numbers per subnet, but you may find routing of packets within FreeRTOS+TCP wouldn’t work as intended if you used the same port number on the same subnet for two endpoints.

Separately -

Having the same server, on a known port number, on two interfaces at once would be a reason to allow the same port number on more than one interface. For example, having an HTTP server on 10.10.10.10:80 and 1.2.3.4:80 at the same time.

Of course.

    static uint32_t interface_count = 0;

    Socket = FreeRTOS_socket(FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP);
	if(FREERTOS_INVALID_SOCKET == Socket){
		...
	}

	freertos_sockaddr addr;
	addr.sin_family = FREERTOS_AF_INET;
	addr.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr(p_local_ip);
	addr.sin_port = FreeRTOS_htons(LOCAL_UDP_PORT + interface_count);

	BaseType_t ret = FreeRTOS_bind(Socket, &addr, sizeof(addr));
	if(0 != ret){
		...
	}

    interface_count++;
	

This is the constructor of class. I added the static interface_count variable because of binding error.

And you are sure that the IP addresses passed to the two constructors are distinct? Just to rule out the obvious fallacies that we all fall victim to every once in a while…

You are right. Such mistakes happen a lot.

This is the constans.

const char GATEWAY_IP[] = "192.168.1.1";
const char NET_MASK[] = "255.255.255.0";

const char CARD_0_IP[] = "192.168.1.100";
const uint32_t  CARD_0_COMMUNICATION_PORT = 6000;
const uint32_t  CARD_0_DISCOVERY_PORT = 6001;

#define CARD_0_MAC_ADDR0		0x00
#define CARD_0_MAC_ADDR1		0x11
#define CARD_0_MAC_ADDR2		0x22
#define CARD_0_MAC_ADDR3		0x33
#define CARD_0_MAC_ADDR4		0x44
#define CARD_0_MAC_ADDR5		0x55

const char CARD_IP_1[] = "192.168.1.110";
const uint32_t  CARD_1_COMMUNICATION_PORT = 6010;
const uint32_t  CARD_1_DISCOVERY_PORT = 6011;

#define CARD_1_MAC_ADDR0		0x00
#define CARD_1_MAC_ADDR1		0x11
#define CARD_1_MAC_ADDR2		0x22
#define CARD_1_MAC_ADDR3		0x33
#define CARD_1_MAC_ADDR4		0x44
#define CARD_1_MAC_ADDR5		0x55

And this is the initialization part.

	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_IP_1);
	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)};


	static NetworkInterface_t xInterfaces[1];
	static NetworkEndPoint_t xEndPoints[2];


	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);


	FreeRTOS_IPInit_Multi();

You only have one interface but two end points? Wouldn’t you have seperate interfaces for each network card?

As a side note, it looks like you are re using the same MAC address for both cards. That would be unrelated to your problem at hand but does not seem right nevertheless…

For now, I can continue like this, but I think this feature should be added.

Yes - two identical MAC addresses on the same subnet is going to cause a problem. Which would respond to an ARP, for example.

A short comment at the end of this long topic:

Within FreeRTOS+TCP, a port number can only be bound to one socket system wide. So for instance, you can open one HTTP socket at port 80, which will receive connections from all endpoints and all interfaces. And you can only bind one UDP socket to port 17000.

This behaviour has been present in all earlier versions of +TCP /multi /IPv6, since about 2016.

The reason for this choice was:

  • A simpler implementation
  • Lower memory foot print ( a socket is an “expensive” object )
  • Simpler application code

On Linux or Windows you can choose between binding to a particular IP address ( 192.168.2.11 ), or binding to the the ANY address 0.0.0.0.
When binding to a particular address like 192.168.2.11:80, one can allow other TCP servers to re-use port 80 by setting the SO_REUSEADDR option on the socket.

@rtel wrote:

For example, having an HTTP server on 10.10.10.10:80 and 1.2.3.4:80 at the same time.

Exactly.

@RAc wrote:

And you are sure that the IP addresses passed to the two constructors are distinct?

In fact, the binding IP-address is ignored, only the port is what matters.

it looks like you are re using the same MAC address for both cards

That is unfortunate, because that makes routing more difficult.

On FreeRTOS+TCP, you can check the IP-addresses of both sides of a TCP connection with:

    BaseType_t FreeRTOS_GetRemoteAddress( ConstSocket_t xSocket,
                                          struct freertos_sockaddr * pxAddress );
    size_t FreeRTOS_GetLocalAddress( ConstSocket_t xSocket,
                                     struct freertos_sockaddr * pxAddress );

On the basis of this information, you can decide to offer a different type of website.

Most of you will know: a TCP server socket doesn’t do much, it can create a local socket with the same port number as the server.

bind function because I use the same port number for both endpoints

@ozanagma, Could you try the following?

endpoint 1 : 192.168.1.100, mask 255.255.255.0
endpoint 2 : 192.168.2.100, mask 255.255.255.0

Create a socket and bind it to port 17000. Now you can write to both endpoints and the routing should work well.
When a device replies, you can find out who’s replying by checking the parameter pxSourceAddress.

@rtel wrote:

Yes - two identical MAC addresses on the same subnet is going to cause a problem. Which would respond to an ARP, for example.

That is another reason to give endpoint a unique netmask.

If the above is impossible in your setup, lets think of finding a solution in the library.

Thanks

3 Likes

Hi @htibosch,

Thank you for your detailed answer.

As I mentioned in my comment above, my main purpose is to pretend to run more than one card software on the same card, communicate them over ethernet and test the software. After failing to do what I wanted to do, I tried other configurations and using the same mac address was one of them. Actually, I do not intend to use the same mac address. So you can think of it as if I’m not using the same mac address.

The main problem was this: I have a task and this task was sending a udp packet from a socket once in a while. I am not receiving any packets from this socket at all. I ran into this problem when I tried to run two different card software on the same card. I solved my own problem by increasing the port number of the second task by one. At this point, it was enough for me to know that the error I was getting was normal.

The methods you suggest will be very useful for the problems I will encounter.

This solved my problem and thank you for your help.

Note: I solved the main problem(provide communication between two endpoints) by putting the loopback code you wrote, inside the ZynqNetworkInterfaceOutput function and making a few more changes. Also, thank you for the code you wrote. I will write about the problems I encountered while doing this in a new post.

Thanks @ozanagma for the clarification.
We will add this to our FreeRTOS-plus-TCP port number page (TCP port and UDP port).

1 Like