3

Linux allows to execute following code to bind a socket to some specific network interface. So, the data sent through this socket will always be channeled through the original interface.

setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))

As I understand, this functionality is used in VPN clients. A socket gets connected to a remote server and bound to a network interface. This way, the traffic from VPN client itself won't be looped back into VPN client.

Is there OS X equivalent of doing this? Either

  • Binding a socket to some specific interface

  • Marking a socket in VPN client to not be looped back.

BTW. I found similar question, but I didn't understand the answer: Writing an OS X kernel extension to implement Linux's SO_BINDTODEVICE socket option

Update 1

I found out that some of VPN clients use TUN/TAP devices to prevent loopback problem. http://backreference.org/2010/03/26/tuntap-interface-tutorial/

However, I am not that that all OS X VPN uses that.

Community
  • 1
  • 1
Victor Ronin
  • 22,758
  • 18
  • 92
  • 184

4 Answers4

4

Yep, use IP_BOUND_IF

int idx = if_nametoindex("en0");
setsockopt(sockfd, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx))

However, you can just use bind() with the IP address of the interface instead, that's often easier.

horseyguy
  • 29,455
  • 20
  • 103
  • 145
2

Does using getifaddrs() to identify the address of the device and then binding directly to that work? Or does SO_BINDTODEVICE have additional behaviors beyond the traditional bind() call?

int BindToDevice(int sock, int family, const char* devicename)
{
    ifaddrs* pList = NULL;
    ifaddrs* pAdapter = NULL;
    ifaddrs* pAdapterFound = NULL;
    int bindresult = -1;

    int result = getifaddrs(&pList);

    if (result < 0)
        return -1;

    pAdapter = pList;
    while (pAdapter)
    {
        if ((pAdapter->ifa_addr != NULL) && (pAdapter->ifa_name != NULL) && (family == pAdapter->ifa_addr->sa_family))
        {
            if (strcmp(pAdapter->ifa_name, devicename) == 0)
            {
                pAdapterFound = pAdapter;
                break;
            }
        }
        pAdapter = pAdapter->ifa_next;
    }

    if (pAdapterFound != NULL)
    {
        int addrsize = (family == AF_INET6)?sizeof(sockaddr_in6):sizeof(sockaddr_in);
        bindresult = bind(sock, pAdapterFound->ifa_addr, addrsize);
    }

    freeifaddrs(pList);
    return bindresult;
}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • I was under impression that bind() is used only in the case when we want to do listen() and accept(). However, in my case, I want to do connect(), because this socket is for the client. – Victor Ronin Dec 16 '13 at 18:25
  • Works with client (connect) sockets too as long as the routing table allows for it. – selbie Dec 17 '13 at 05:43
0

This worked for me:

char ethInterface[4] = "en0";
setsockopt(sock, SOL_SOCKET, IP_RECVIF, ethInterface, strlen(ethInterface));
CraigD
  • 9
  • 1
  • -1 That doesn't sound right. According to Stevens this is *not* used to specify the name of the receive/send interface but for this: "This socket option causes the index of the interface on which a UDP datagram is received to be returned as ancillary data by recvmsg." – Bruno Rijsman Jul 10 '18 at 22:06
-1

Use the RFC 3542 interface for selecting outgoing interfaces (IPV6_PKTINFO). https://www.rfc-editor.org/rfc/rfc3542#section-6

Community
  • 1
  • 1
cabo
  • 987
  • 8
  • 9