如何获取与TCP套接字关联的接口名称/索引?

Lee*_*eor 7 sockets linux tcp

我正在编写一个TCP服务器,需要知道每个连接来自哪个接口.我无法使用地址/子网来推断使用了哪个接口,因为可能存在具有相同地址/子网值的接口.它基于Linux,并且不需要代码可移植.

我所能找到的只是通过索引获取所有接口或单个接口的函数.我找不到任何方法来获得与接受的TCP套接字关联的接口.

有任何想法吗?我错过了什么?

编辑:重申一下,在我的情况下,IP地址并不是唯一的.目标地址(服务器本身)和源地址(客户端)都不是.是的,这是一个非常极端的IP方案.

SKi*_*SKi 8

使用getsockname()来获取TCP连接的本地端的IP。然后使用getifaddrs()找到对应的接口:

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);
Run Code Online (Sandbox Code Playgroud)

以上只是一个肮脏的例子,需要一些修改:

  1. 添加缺少的错误检查
  2. IPv6 支持


小智 5

以下是一些用于查找套接字接口名称的 C++11 代码:

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);
}
Run Code Online (Sandbox Code Playgroud)


Jim*_*imB 0

查看目标地址。

每个接口通常绑定到一个唯一的地址。如果多个接口绑定在一起,那么使用哪一个可能并不重要。

唯一的例外是使用 ipv6 任播时,但即使如此,通常也不会在同一台主机上拥有多个具有相同 IP 的接口。