29

How can I bind a socket to a particular network interface? I tried using setsockopt on server side, but the clients can still access the service through both eth0 and lo interfaces.

I can achieve this by setting the particular IP address using serv_addr.sin_addr.s_addr.

But I suspect that we can bind to an interface using only setsockopt (without mentioning the IP address).

Casey
  • 12,070
  • 18
  • 71
  • 107
user2003595
  • 331
  • 1
  • 4
  • 7

3 Answers3

42

You can bind to a specific interface by setting SO_BINDTODEVICE socket option.

struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0");
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
    ... error handling ...
}

Warning: You have to be root or have the CAP_NET_RAW capability in order to use this option.

The second method is that you can resolv IP address tied to an interface with getifaddrs().

Follow the latter link for a comprehensive example.

larsks
  • 277,717
  • 41
  • 399
  • 399
Davide Berra
  • 6,387
  • 2
  • 29
  • 50
  • 1
    I have a new question, so does it matter eth0 and eth1 have same IP address using your method? – Splash Oct 24 '15 at 14:40
  • I'm pretty sure that it should say "You have to be root *OR* have the CAP_NET_RAW capability", you don't need both. – dreua Aug 25 '22 at 10:14
  • 1
    Hi https://linux.die.net/man/3/setsockopt does not list SO_BINDTODEVICE is it no longer supported? – AAB Sep 01 '22 at 07:37
2

The only way you can do it is as you mention -

by setting the particular IP address using serv_addr.sin_addr.s_addr

You can't do it without knowing the address to bind to.

You can use ioctls to determine the current IP address if you need, though there may be a cleverer way to do this these days - I've not done much in modern Linux distros lately.

jotik
  • 17,044
  • 13
  • 58
  • 123
Joe
  • 7,378
  • 4
  • 37
  • 54
1

Maybe someone will find it useful, so I am sharing the solution that worked for me (Linux, C++):

uint32_t interfaceIndex = if_nametoindex(interfaceName);

Where "interfaceName" is the name of the interface we want to bind to, e.g. "eth0" (see: https://linux.die.net/man/3/if_nametoindex). Now we can specify this interface in the socket address structure via "sin6_scope_id" (in case we use IPv6):

struct sockaddr_in6 socketAddress;
socketAddress.sin6_scope_id = interfaceIndex;

Now we can bind the socket to the interface via "bind" as usual.