How to bind multiple interfaces to a raw socket - linux

I have opened a raw socket to get all the raw packets:
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)
When a packet is received on an interface which is member of a LAG or bond, the kernel is sending the packets to the user application 2 times. One for the actual physical interface and another one for the bond interface. How can I restrict the kernel to lift the packet only for the interface which I am interested?
We can achieve it by binding the application interested interface to the socket. But I don't want to create multiple sockets (one for each interface) to avoid scalability issue. Is it possible to bind multiple interface to the raw socket dynamically?

Use SO_BINDTODEVICE socket option to bind to specific interface:
char *iface = "eth0";
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface, 4);
In this case you'll have to create separate socket for each interface.
Alternative solution would be to use bind(2) with INADDR_ANY.
You do not have a third option.

Related

Why does socketpair() allow SOCK_DGRAM type?

I've been learning about Linux socket programming recently, mostly from this site.
The site says that using the domain/type combination PF_LOCAL/SOCK_DGRAM...
Provides datagram services within the local host. Note that this
service is connectionless, but reliable, with the possible exception
that packets might be lost if kernel buffers should become exhausted.
My question, then, is why does socketpair(int domain, int type, int protocol, int sv[2]) allow this combination, when according to its man page...
The socketpair() call creates an unnamed pair of connected sockets in
the specified domain, of the specified type...
Isn't there a contradiction here?
I thought SOCK_DGRAM in the PF_LOCAL and PF_INET domains implied UDP, which is a connectionless protocol, so I can't reconcile the seeming conflict with socketpair()'s claim to create connected sockets.
Datagram sockets have "pseudo-connections". The protocol doesn't really have connections, but you can still call connect(). This associates a remote address and port with the socket, and then it only receives packets that come from that source, rather than all packets whose destination is the address/port that the socket is bound to, and you can use send() rather than sendto() to send back to this remote address.
An example where this might be used is the TFTP protocol. The server initially listens for incoming requests on the well-known port. Once a transfer has started, a different port is used, and the sender and receiver can use connect() to associate a socket with that pair of ports. Then they can simply send and receive on that new socket to participate in the transfer.
Similarly, if you use socketpair() with datagram sockets, it creates a pseudo-connection between the two sockets.

What does the parameter protocol stand for in socket() system call?

http://linux.die.net/man/2/socket
The protocol specifies a particular protocol to be used with the
socket. Normally only a single protocol exists to support a particular
socket type within a given protocol family, in which case protocol can
be specified as 0. However, it is possible that many protocols may
exist, in which case a particular protocol must be specified in this
manner. The protocol number to use is specific to the 'communication
domain' in which communication is to take place;
http://linux.die.net/man/5/protocols
The field descriptions are:
protocol the native name for the protocol. For example ip, tcp, or
udp.
When we are already specifying the second parameter either sock dgram or sock stream then why again do we need the protocol parameter?
There are or can be many different datagram or stream protocols.
The typical default for the AF_INET and AF_INET6 domain(when you pass 0) for a streaming protocol is TCP, and for a datagram protocol is UDP.
Another interesting protocol is SCTP, which can operate in both datagram and stream mode, so you can create an SCTP socket in either of these ways:
socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
socket(AF_INET, SOCK_DGRAM, IPPROTO_SCTP);
An UDP-lite variant can be created with
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
Note also that socket() is a general call, and can be used to create non-IP based sockets, e.g. for bluetooth, infrared, CANbus protocols which might provide different protocols that you can use.

changing default source IP for udp server bind with INADDR_ANY

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

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

UDP broadcast and unicast through the same socket?

I have a Linux application that opens a UDP socket and binds it to a port. I haven't had any problem sending unicast packets through the socket. I had occasion to send a broadcast packet, so I enabled SO_BROADCAST, which allowed the broadcast packets to pass, but then I noticed that the unicast packets were being broadcast as well. Is this expected behaviour for a UDP socket, or is it more likely that I've misconfigured something?
From what I understand SO_BROADCAST is a socket option. So if you enable it on your socket this socket will broadcast. I guess you will need to open different sockets if you want to do unicast and broadcast from the same code.
I have not done much hands on programming here, but you probably need to provide more information about the library, OS version, code, etc. Maybe a code sample?
If I remember the books I read, if you set the flag on the socket, that is going to affect all datagrams sent from the socket, because the socket is a basically a data structure of network flags + a file descriptor.
I have figured out the same issue on Linux about having a socket getting unicast and broadcast at the same time. I solved the problem as follow (pseudo-code):
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
Open the socket
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &1)
Allows incoming and outgoing broadcast from this socket
bind(sock, bindaddr, sizeof(struct sockaddr) with
bindaddr.sin_family = AF_INET
bindaddr.sin_port = <YourPort>
bindaddr.sin_addr.s_addr = INADDR_ANY
Get all incoming messages on any card for <YourPort>
The caveat is that there is no filtering (see caveat in 3.). So you will get all messages.
The sent messages are either unicasted or broadcasted depedning on the given address in the sendto().

Resources