Use Linux SO_BINDTODEVICE and mac IP_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_BINDTODEVICEBind 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, particularlyAF_INETsockets. It is not supported for packet sockets (use normalbind(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. Theoptlenargument should contain the buffer size available to receive the device name and is recommended to beIFNAMSZbytes. The real device name length is reported back in theoptlenargument.
IP_BOUND_IF on Mac
In Mac, there is no SO_BINDTODEVICE, the corresponding option is IP_BOUND_IF:
IP_BOUND_IFLimit 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_IFandIPV6_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
- man socket
- man ip section 7
- Apple Developer Form: Need to route Data traffic
- netinet/in.h on Mac
- mDNSResponder/mDNSMacOSX.c
OmniLock - Block / Hide App on iOS
Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.
DNS Firewall for iOS and Mac OS
Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps