RSS

Linux SO_BINDTODEVICE and mac IP_BOUND_IF to bind socket to a network interface

Use Linux SO_BINDTODEVICE and mac IP_BOUND_IF / IPV6_BOUND_IF to bind socket to a network interface

SO_BINDTODEVICE on Linux

In Linux, SO_BINDTODEVICE can be used to bind a socket to network interface. e.g.

#include <sys/types.h>
#include <sys/socket.h>

const char *interface_name = "eth0";
setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, interface_name, strlen(interface_name));

SO_BINDTODEVICE

Bind this socket to a particular device like “eth0”, as specified in the passed interface name. If the name is an empty string or the option length is zero, the socket device binding is removed. The passed option is a variable-length null-terminated interface name string with the maximum size of IFNAMSIZ. If a socket is bound to an interface, only packets received from that particular interface are processed by the socket. Note that this only works for some socket types, particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).

Before Linux 3.8, this socket option could be set, but could not retrieved with getsockopt(2). Since Linux 3.8, it is readable. The optlen argument should contain the buffer size available to receive the device name and is recommended to be IFNAMSZ bytes. The real device name length is reported back in the optlen argument.

IP_BOUND_IF on Mac

In Mac, the option is IP_BOUND_IF.

IP_BOUND_IF

Limit reception and transmission of packets to this interface. Takes an integer as an argument. The integer is the selected interface index.

Please note, setsockopt() return -1 indicate error and error code store in errno. e.g.

For IPv4:

#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>

const char *interface = "en0";

int index = if_nametoindex(interface);
if (index == 0) {
    // if_nametoindex() return 0 if interface not exist.
    return NOT_FOUND;
}

if (setsockopt(socket_fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index)) == -1) {
    return errno;
}

The if_nametoindex() function maps the interface name specified in ifname to its corresponding index.

If the specified interface does not exist, it returns 0.

For IPv6, need replace IPPROTO_IP with IPPROTO_IPV6, IP_BOUND_IF with IPV6_BOUND_IF, e.g.

if (setsockopt(socket_fd, IPPROTO_IPV6, IPV6_BOUND_IF, &index, sizeof(index)) == -1) {
    return errno;
}

Excerpt sample usage in Apple source code mDNSResponder/mDNSMacOSX.c:

Hint: search IP_BOUND_IF and IPV6_BOUND_IF.

if (dst->type == mDNSAddrType_IPv4)
{
#ifdef IP_BOUND_IF
    if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
    else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
#else
    (void)InterfaceID; // Unused
    (void)info; // Unused
#endif
}
else
{
#ifdef IPV6_BOUND_IF
    if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id));
    else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; }
#else
    (void)InterfaceID; // Unused
    (void)info; // Unused
#endif
}

IP_BOUND_IF and IPV6_BOUND_IF defined in netinet/in.h:

// netinet/in.h

#define IP_BOUND_IF             25    /* int; set/get bound interface */
#define IPV6_BOUND_IF           125   /* int; set/get bound interface */

Troubleshooting

setsockopt() return -1 and errno is 22.

22 is invalid argument. For IPv4, make sure call setsockopt() with IPPROTO_IP, not IPPROTO_IPV4.

22: Invalid argument

References