changing default source IP for udp server bind with INADDR_ANY - linux

My application has opened an UDP socket that is bound to INADDR_ANY to listen to packets on all the interfaces my server has. I'm sending out replies through the same socket.
However, while sending a reply from the server, default IP is chosen by the IP layer of linux depending upon which interface is chosen for packet to going out. The IP associated with this interface may not be the destination address with which this UDP server got a query from a client. Thus source IP of the reply from server becomes different from the destination IP with which the query came. The client may be uncomfortable with such a reply.
Following link gives the behavior of INADDR_ANY with UDP:
http://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html
How can I change this default behavior and use a particular interface IP in the source address? That is more control on the application code to decide what will be the source address. Also it make sense that source address in the reply be same as the destination address with which the query came.

Assuming you have multiple interfaces (one of which has the correct ip) of course you can bind to an interface for outgoing response. Take a look at SO_BINDTODEVICE socket option.
int bind_sock2inf(int sock, char *interface_name)
{
int status = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface_name);
if ( (status = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
(void *)&ifr, sizeof(ifr))) < 0) {
log_debug(4, "Failed binding to interface named %s", inf_name);
}
else log_debug(3, "Binding to interface %s", inf_name);
return status;
}
Now your outgoing request should automatically use the ip address attached to this interface. The only down side is that you stop receiving messages on any other interface for this socket.
Possible work arounds are:
Use a separate socket for listening which is not bound to any interface and another one for sending and bind to whatever interface you need before sending.
Bind to interface before sending a message and bind to "" again which clears the previous bind immediately after sending. However, you might loose any packets received during this time frame which your socket was bound to interface say eth0 and packets arrived at eth1.
Also you can simply use bind() for associating a source ip for an outgoing packet.

Once a socket is bound to an address you can not bind it again to another address or you will get error EINVAL. But there is another technique described in this post Setting the source IP for a UDP socket

Related

What happens if you bind to a specific interface and then the IP changes?

With the following snippet I bind to a specific interface
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return nil, fmt.Errorf("ERR: Error using interface %q: %q", ifaceName, err.Error())
}
addrs, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf("ERR: Error using interface %q: %q", ifaceName, err.Error())
}
if len(addrs) < 1 {
return nil, fmt.Errorf("ERR: Interface %q has no addresses?", ifaceName)
}
ipAddr := addrs[0].(*net.IPNet).IP
udpAddr := &net.UDPAddr { IP: ipAddr }
and then use this to as the local address for Listen. Does anybody have any idea what happens if the IP address of the interface changes?
This is a question that I believe will be best answered in terms of linux networking, not so much go language, since a go server will have to do the following to listen on the socket:
bind to a socket
listen
accept
I describe the case for TCP server, but UDP server will be analogous (to a certain degree!). You can investigate it yourself by setting up a simple server and running it with strace, for example:
strace -e trace=network nc -l -s 192.168.0.1 7777 (strace part will display system calls and nc -l part will listen on the given address)
Output (I encourage you to try similar thing with a simple go server!):
bind(3, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("192.168.0.1")}, 16) = 0
listen(3, 1) = 0
accept4(3,
We can see that the server blocks on accept call and waits for incoming connections. What happens here is specified more precisely in Linux man pages:
If no pending connections are present on the queue, and the socket is
not marked as nonblocking, accept() blocks the caller until a
connection is present. If the socket is marked nonblocking and no
pending connections are present on the queue, accept() fails with the
error EAGAIN or EWOULDBLOCK.
Now the question is how a packet that is received by an interface on the host gets to that socket. It needs to travel through the TCP/IP stack and finally arrive in our application. It is not a trivial process and you can find details on it in many linux networking guides). In essence, what happens is (this is VERY HIGH level description/generalization):
Packet arrives at the Network Interface Card, gets classified and
arrives at correct upper layer (e.g. IP in case of IP packets)
Packet arrives at the IP layer, where IP header is checked among
other things, and then IP header is stripped and packet being
carried by this IP packet is passed to the upper layer (e.g. in case
of TCP - to TCP layer)
tcp_v4_rcv function is called from IP layer (in case of TCPv4),
and the packet arrives at TCP layer. Here, header is checked and
then an open socket for this incoming packet is being looked for
(call to __tcp_v4_lookup).
At this stage, if the TCP socket for this packet is not found, the packet is discarded. Otherwise, the packed it passed to the process.
To directly answer your question: if IP address changes, and old TCP socket was bound to old IP address, the packet that is received for this socket (which will be still open is the server is running and is not refreshed) will be discarded somewhere along the journey through networking parts of linux kernel.
Here's a
If you really want to go in-depth, a good description of what happens can be found in a great article Inside the Linux Packet Filter (and part 2 about what happens above IP layer)

source IP in multihomed client host while bind is called

Which is the source IP address in tcp socket if bind is called on a multihomed client host? Client has two interfaces eth0(IP0) and eth1(IP1) and the client tcp socket is bound to IP0. After socket, bind, connect in client, it sends a packet to server.The destination IP isservIP. But servIP and IP0 are not in a same subnet(Maybe servIP and IP1 are). Which is the source IP in the packet sent to server? And what will getsockname return?
There are two separate issues here:
1) Which IP to bind on?
When calling bind() you have an option to specify and address to bind on or you can leave this decision to TCP/IP stack on your computer. You can pass a specific address in 'addr' parameter or leave it as INADDR_ANY. You can find more information how to do it in manual page of ip(7). If you call bind() providing the valid IP address and call to bind() succeeds, then datagrams sent using the binded socket will have their source address set to the value provided in call to bind().
2) How the packet is routed?
The way your packet is routed depend only on the destination address and not the source address. It can be that your source address will be the one from eth0 and it will go out through eht1. This is because the routing system in your OS is using destination based routing as opposed to source based routing. You can always see which adapter will be used by issuing "route" command in the console of your OS and comparing the output with the destination address

receiving multicast on a server with multiple interfaces (linux)

To receive a multicast on my not default NIC (dvb) I do the following:
open a socket (AF_INET, SOCK_DGRAM)
join the multicast address with IP_ADD_MEMBERSHIP on the dvb interface
bind the multicast address (note that a common error is to bind "0.0.0.0" and then receive on that socket even multicast you are not interested in) and the port
at this point the only way to receive the needed multicast packets is to add in the routing table a rule to reach the network where the sender is (another network) trough the dvb, as if the dvb needs to reply to the multicast sender; let say a sort of source sender multicast mode. Anyone knows what is going on? The problem is annoying to me because in principle I don't know the ip of sender.
You appear to be being stung by rp_filter reverse-path filtering. This drops packets if they arrive on an interface that doesn't have a route for the source address.
You can disable it on a per-interface basis with the sysctl /proc/sys/net/ipv4/conf/<if>/rp_filter.
bind the multicast address
That is definitely wrong. You must bind to an actual IP address of a real adapter, or 0.0.0.0.
note that a common error is to bind
"0.0.0.0"
That's not an error. THat is correct procedure unless you only want to listen to one IP address.
and then receive on that
socket even multicast you are not
interested in
I don't know what this means.
in principle I don't know the ip of sender
The IP address of the sender of any UDP datagram is available via the sockets API.

SSDP and interface IP address

I'm writing a UPnP AV/DLNA DMS which needs to send and receive SSDP messages. In response to some M-SEARCH packets I need to send a reply with the URL of a resource (in this case a HTTP server), which I've chosen to bind to INADDR_ANY (0.0.0.0). Of course this address is meaningless to the sender of the M-SEARCH packet: The address of the interface on which the M-SEARCH was received is most appropriate.
How can I determine the appropriate address to send in the reply packet?
Some ideas I've considered are:
Binding a different receiver to each socket. When a receiver gets an M-SEARCH packet, the reply address can use the socket's local address in the reply. However this requires knowing and iterating over all the interfaces, and adding and removing receivers as interface availability changes.
Put a single receiver on INADDR_ANY, and iterate interface netmasks to determine the possible source. However more than one interface might share the same subnet.
Extract the packets IP target address upon receiving it. This would be IP specific, and may be lost somewhere in the network abstraction.
getsockname(2) followed by getnameinfo(3) reports the IP address that your TCP/IP stack has assigned to the socket. (Obviously, this won't match what the client could use if server and client are on opposite sides of a NAT system; in that case, perhaps there is clever UPnP trickery to discover the IP address that the client could use to contact the server.)
I assume your server looks something like this:
lfd = socket();
ret = bind(lfd,...);
connection = listen(lfd, 10);
/* add connection to your select queue or poll queue */
You could append code similar to this:
struct sockaddr_storage me;
socklen_t *len = sizeof(me);
char name[40];
ret = getsockname(connection, &me, &len);
ret = getnameinfo(&me, &len, name, sizeof(name), NULL, 0, NI_NUMERICHOST);
getnameinfo(3) inspects the struct sockaddr_storage me for your IP address. Because these are generic interfaces, it'll work for IPv4 or IPv6 addresses.

Disabling self-reception of UDP broadcasts

I wish to know is there any way I can disable the UDP broadcast packet from the node A to not received by node A itself.
For braodcast I am simply using INADDR_BROADCAST and on the
receiver side I am using AI_PASSIVE | AI_NUMERICHOST.
No, this is fundamental property of broadcasting - every host on the subnet, including the sender, will have to process the packet all the way up the network stack. You options are:
Switch to multicast. This is preferred since multicast reduces the load on the whole network compared to broadcast, and because you can explicitly control multicast loopback with the IP_MULTICAST_LOOP socket option.
Don't bind(2) the destination port on the sending machine. This works but is sort of kludgy since it puts restrictions on application design and/or deployment.
Bind to interface, not just address.
#include <net/if.h>
#include <socket.h>
struct ifreq interface;
strcpy(interface.ifr_ifrn.ifrn_name, "eth0");
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, sizeof(interface));
//... bind(fd,...) ...
This way data that didn't arrive at the interface specified (but originated from it instead) will not be received.
Here are results of my experiments with Python's socket library. Whether UDP broadcaster receives messages sent by itself is dependent on what address will you bind broadcasting socket to. For greater clarity, broadcaster's IP address was 192.168.2.1.
When binding to '192.168.2.255' or '' (empty address), broadcaster receives messages sent by itself
When binding to '192.168.2.1', '255.255.255.255' or '<broadcast>', broadcaster will NOT receive messages sent by itself
Receiver received broadcasted UDP messages in all these cases.
P.S. Tested on Python 2.7.9, OS Raspbian 8 (adaptation of Debian for Raspberry Pi), Linux kernel 4.4.38

Resources