RSS

Use 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 is a socket option can be used in setsockopt() 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, there is no SO_BINDTODEVICE, the corresponding 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.

Example code to use IP_BOUND_IF in setsockopt().

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

DNS Firewall for iOS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad