[ACCEPTED]-How can I get the interface name/index associated with a TCP socket?-tcp

Accepted answer
Score: 10

Use getsockname() to get IP of local end of the TCP connection. Then 3 use getifaddrs() to find the corresponding interface:

struct sockaddr_in addr;
struct ifaddrs* ifaddr;
struct ifaddrs* ifa;
socklen_t addr_len;

addr_len = sizeof (addr);
getsockname(sock_fd, (struct sockaddr*)&addr, &addr_len);
getifaddrs(&ifaddr);

// look which interface contains the wanted IP.
// When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...)
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
    if (ifa->ifa_addr)
    {
        if (AF_INET == ifa->ifa_addr->sa_family)
        {
            struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;

            if (inaddr->sin_addr.s_addr == addr.sin_addr.s_addr)
            {
                if (ifa->ifa_name)
                {
                    // Found it
                }
            }
        }
    }
}
freeifaddrs(ifaddr);

Above 2 is just a dirty example, some modifications 1 are needed:

  1. Add missing error checks
  2. IPv6 support
Score: 4

In general, you shouldn't need to know what 13 interface the packets are going to be sent/received 12 on; that's the kernel's routing table's 11 job. It's difficult to find out the interface 10 for a socket because there really is no 9 direct association. The routing of packets 8 can change within the socket's lifetime 7 based on routing information.

For datagram 6 (UDP) sockets, you may be able to use getsockopt(s, IPPROTO_IP, IP_PKTINFO, ...); see 5 getsockopt(2) and ip(7).

For stream (TCP) sockets, one option 4 might be to open multiple listening sockets, one 3 for each interface on the system, and use 2 setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ...) to bind each to one interface; see setsockopt(2) and 1 socket(7).

Score: 4

Here's some C++11 code to find the interface 1 name of a socket:

std::string to_string(sockaddr_in const& addr)
{
    char buf[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)) == nullptr)
    {
        std::clog << "inet_ntop: " << strerror(errno) << '\n';
        return {};
    }
    return buf;
}

std::string to_string(sockaddr_in6 const& addr)
{
    char buf[INET6_ADDRSTRLEN];
    if (inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf)) == nullptr)
    {
        std::clog << "inet_ntop: " << strerror(errno) << '\n';
        return {};
    }
    return buf;
}

std::string to_string(sockaddr_storage const& addr, socklen_t len)
{
    switch (addr.ss_family)
    {
    case AF_INET:
    {
        auto& a = reinterpret_cast<sockaddr_in const&>(addr);
        if (len < sizeof(a))
        {
            std::clog << "Invalid sockaddr length: " << len << '\n';
            return {};
        }
        return to_string(a);
    }
    case AF_INET6:
    {
        auto& a = reinterpret_cast<sockaddr_in6 const&>(addr);
        if (len < sizeof(a))
        {
            std::clog << "Invalid sockaddr length: " << len << '\n';
            return {};
        }
        return to_string(a);
    }
    default:
    {
        std::clog << "Invalid sockaddr family: " << addr.ss_family << '\n';
        return {};
    }
    }
}

std::string get_iface_name(sockaddr_in const& addr)
{
    ifaddrs *ifa = nullptr;
    if (getifaddrs(&ifa) == -1)
    {
        std::clog << "getifaddrs: " << strerror(errno) << '\n';
        return {};
    }
    std::unique_ptr<ifaddrs, void(*)(ifaddrs*)>
        finally{ifa, freeifaddrs};

    for (; ifa; ifa = ifa->ifa_next)
    {
        if (!ifa->ifa_addr)
            continue;
        if (!ifa->ifa_name)
            continue;
        if (ifa->ifa_addr->sa_family != AF_INET)
            continue;
        auto& a = reinterpret_cast<sockaddr_in&>(*ifa->ifa_addr);
        if (a.sin_addr.s_addr == addr.sin_addr.s_addr)
            return ifa->ifa_name;
    }

    std::clog << "No interface found for IPv4 address " << to_string(addr) << '\n';
    return {};
}

std::string get_iface_name(sockaddr_in6 const& addr)
{
    ifaddrs *ifa = nullptr;
    if (getifaddrs(&ifa) == -1)
    {
        std::clog << "getifaddrs: " << strerror(errno) << '\n';
        return {};
    }
    std::unique_ptr<ifaddrs, void(*)(ifaddrs*)>
        finally{ifa, freeifaddrs};

    for (; ifa; ifa = ifa->ifa_next)
    {
        if (!ifa->ifa_addr)
            continue;
        if (!ifa->ifa_name)
            continue;
        if (ifa->ifa_addr->sa_family != AF_INET6)
            continue;
        auto& a = reinterpret_cast<sockaddr_in6&>(*ifa->ifa_addr);
        if (memcmp(a.sin6_addr.s6_addr,
                   addr.sin6_addr.s6_addr,
                   sizeof(a.sin6_addr.s6_addr)) == 0)
            return ifa->ifa_name;
    }

    std::clog << "No interface found for IPv6 address " << to_string(addr) << '\n';
    return {};
}

std::string get_iface_name(sockaddr_storage const& addr, socklen_t len)
{
    switch (addr.ss_family)
    {
    case AF_INET:
    {
        auto& a = reinterpret_cast<sockaddr_in const&>(addr);
        if (len < sizeof(a))
        {
            std::clog << "Invalid sockaddr length: " << len << '\n';
            return {};
        }
        return get_iface_name(a);
    }
    case AF_INET6:
    {
        auto& a = reinterpret_cast<sockaddr_in6 const&>(addr);
        if (len < sizeof(a))
        {
            std::clog << "Invalid sockaddr length: " << len << '\n';
            return {};
        }
        return get_iface_name(a);
    }
    default:
    {
        std::clog << "Invalid sockaddr family: " << addr.ss_family << '\n';
        return {};
    }
    }
}

std::string get_iface_name(int sockfd)
{
    sockaddr_storage addr;
    socklen_t len = sizeof(addr);
    if (getsockname(sockfd, (sockaddr*)&addr, &len) == -1)
    {
        std::clog << "getsockname: " << strerror(errno) << '\n';
        return {};
    }
    std::clog << "getsockname '" << to_string(addr, len) << '\'' << '\n';
    return get_iface_name(addr, len);
}
Score: 2

The kernel routing table decides which interface 22 to send a packet out on, hence the ability 21 to bond devices. A cursory glance through 20 "Linux Socket Programming, Warren W. Gay" suggests 19 that specifying an interface is bad, and 18 that due to the dynamics of the kernel (firewall, forwarding) it 17 is more complex.

I would suggest altering 16 your IP scheme such that the IP information 15 tells you your interface(s) through looking 14 up in the same way ifconfig does, otherwise 13 you are shooting yourself in the foot design 12 wise.

1) Get the IP information from the 11 TCP session 2) Lookup which interface(s) this 10 could be valid for

I will keep looking in 9 the kernel API though. You shouldn't need 8 to know this, the abstraction is there for 7 a multitude of good reasons.

Extra Thought Pondering on 6 this, it seems that if both interfaces use 5 the same IP then there must be a client 4 address range routing difference (otherwise 3 both interfaces would be used). Your server 2 could examine the routing table based on 1 the client IP

Score: 1

I think using getsockname() after accept()ing 12 the incoming connection might be what you're 11 after. The two functions getsockname() and 10 getpeername() get the local and remote addresses 9 respectively that a socket is bound to. Both 8 should be valid for a fully connected TCP 7 socket.

Edit: Whilst this seems to be true 6 for OpenBSD according to the man page, the 5 Linux man page differs considerably and 4 so getsockname() after accept() on Linux 3 is almost certainly unuseful. Teaches me 2 for using my memory instead of checking 1 everything. sigh

Score: 0

Look at the destination address.

Each interface 7 is normally bound to a unique address. If 6 multiple interfaces are bonded together, it 5 probably doesn't matter which one it used.

The 4 only exception to this, is when using ipv6 3 anycast, but even then, you wouldn't normally 2 have multiple interfaces on the same host 1 with the same ip.

Score: 0

Obviously not something I've looked into 9 very deeply, let alone tried, this might 8 be one for the "so crazy it just might 7 work" basket...

If it's really only 6 ever going to be for Linux, you could write 5 a custom netfilter module which tracks the incoming 4 connections and notes which interface they 3 come in on, and writes that information 2 somewhere for your server application to 1 read.

Score: 0

Kieron's suggestion to write a netfilter 15 module is probably one way to try, but I 14 would like to refrain from writing my very 13 first kernel module for this solution.

I've 12 come up with another option of using source 11 NAT and translating the source port of the 10 connection to correlate with the connection 9 source. I can assign port ranges for each 8 network and check it in the server. The 7 only problem is that source NAT in iptables 6 is done in the POSTROUTING chain, and I'm 5 not sure it's used for connections that 4 are accepted by that host, so I might need 3 to use another server.

No easy solutions 2 here, too bad I can't get the interface 1 name/index from the socket...

Score: 0

I am adding another answer and a potential 14 solution after looking through the source 13 of Wireshark and iftop which seem to have 12 indirectly similar functionality.

Looks to 11 me that you can use libpcap to sniff on 10 interfaces. Presuming you can identify some 9 unique part of the TCP/IP session then you 8 can track it down to an interface fairly 7 simply using filters and session tracking.

No 6 kernel modules (and it plays nice with threads)

http://www.ex-parrot.com/pdw/iftop/ Some 5 simple source to have a peek at www.tcpdump.org/ for 4 libpcap

I think you will be able to match 3 VLANs using it too.

Also wireshark might 2 be useful for debugging. Hope this helps! It's 1 been on my brain since.

More Related questions