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_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, particularlyAF_INET
sockets. 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. Theoptlen
argument should contain the buffer size available to receive the device name and is recommended to beIFNAMSZ
bytes. The real device name length is reported back in theoptlen
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
andIPV6_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