Jump to content
caymon

TIdUDPServer listening to all local addresses

Recommended Posts

A system A, which has several IP addresses, sends UDP packets to a system B that also have several addresses:

 

On system A,  there is a TIdUPDClient with the code:

 

  UDPClient.Host := '2017::7690:5000:0:1'; // one of the addresses of B;
  UDPClient.Port := 3610;
  UDPClient.IPVersion := Id_IPv6;
  UDPClient.BoundIP := 'FD00:CAFE:CAFE::AAAA'; // one of the addresses of A

 

The UDP packets are sent with: Src= FD00:CAFE:CAFE::AAAA and Dst=2017::7690:5000:0:1, which is OK.

 

In the system B, there is an IdUDPServer listening to any addresses of B, i.e:

IdUDPServer.Bindings.Add.IP := '';

 

When B receives a packet, the handler UDPRead is executed, with a ''ABinding'' parameter with the values:

ABinding.IP = '0:0:0:0.0:0:0:0'

ABinding.Port = 3610

ABinding.PeerIP = 'FD00:CAFE:CAFE::AAAA'  // src address of the packet, which is correct

ABinding.PeerPort = ...any value

 

From the 'ABinding' parameter, is there a way to know the ''real local address'' that received the packet? In our example, this address should be 2017::7690:5000:0:1?

 

Then, before responding to the received message, could I do UDPServer.Binding.IP := ''the real local address'' and then UDPServer.SendBuffer() so that

the sent UDP packet would have Src=2017::7690:5000:0:1 and Dst=FD00:CAFE:CAFE::AAAA?

 

Thank you.

 

Share this post


Link to post
Posted (edited)
2 hours ago, caymon said:

From the 'ABinding' parameter, is there a way to know the ''real local address'' that received the packet?

No, when ABinding is bound to a wildcard IP like '::0'.

 

Internally, TIdUDPServer uses recvfrom() to read packets, and the information you want is simply not available from recvfrom().  You would need to use recvmsg()/WSARecvMsg() instead (ie, via Indy's TIdStack.ReceiveMsg() method), which requires the IP_PKTINFO/IPV6_RECVPKINFO option be enabled on the socket for recvmsg() to report the receiving adapter's info on each packet read.

 

TIdUDPServer does not support recvmsg(), though.  You would have to alter its source code and recompile.  Otherwise, you could use TIdUDPClient instead and run your own reading logic with it (despite their names, TIdUDPClient and TIdUDPServer are not true client/server components since UDP is connectionless, so their roles are a little more blurry than with TCP.  You can use TIdUDPClient as a server, and TIdUDPServer as a client).

 

Otherwise, you could simply enumerate the local IPs (ie via Indy's TIdStack.GetLocalAddressList() method) and create a separate Binding for each IP individually, and then ABinding.IP will have a meaningful value in the OnUDPRead event.  This does mean that TIdUDPServer would need to allocate multiple sockets, though.

Quote

Then, before responding to the received message, could I do UDPServer.Binding.IP := ''the real local address'' and then UDPServer.SendBuffer() so that

the sent UDP packet would have Src=2017::7690:5000:0:1 and Dst=FD00:CAFE:CAFE::AAAA?

No.  Sending data out from a bound socket will use the IP of the adapter that the socket is bound to.  If the socket is bound to a wildcard IP, then the OS will pick which adapter to use, based on its own routing tables for the destination IP.

 

If you want to send out from a specific adapter, you need to either

  • create and bind a separate socket (ie TIdUDPClient) to that adapter, and then send using that socket.
  • use sendmsg()/WSASendMsg() (which Indy does not have a wrapper for), passing in a msghdr struct containing an IP_PKTINFO/IPV6_PKTINFO control message that specifies the desired source IP and interface index.

See Setting the source IP for a UDP socket

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×