feat: find interface index from ip address

This commit is contained in:
Yat Ho 2024-03-16 01:09:33 +08:00
parent 9bcb86ebb0
commit b4ad1f78fc
3 changed files with 101 additions and 7 deletions

View File

@ -16,8 +16,12 @@
#include <utility> // std::pair
#ifdef _WIN32
#include <winsock2.h> // must come before iphlpapi.h
#include <iphlpapi.h>
#include <ws2tcpip.h>
#else
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/tcp.h> /* TCP_CONGESTION */
#endif
@ -556,6 +560,92 @@ std::pair<tr_address, std::byte const*> tr_address::from_compact_ipv6(std::byte
return { address, compact };
}
std::optional<unsigned> tr_address::to_interface_index() const noexcept
{
if (!is_valid())
{
tr_logAddDebug("Invalid target address to find interface index");
return {};
}
tr_logAddDebug(fmt::format("Find interface index for {}", display_name()));
#ifdef _WIN32
auto p_addresses = std::unique_ptr<void, void (*)(void*)>{ nullptr, operator delete };
for (auto p_addesses_size = ULONG{ 15000 } /* 15KB */;;)
{
p_addresses.reset(operator new(p_addesses_size, std::nothrow));
if (!p_addresses)
{
tr_logAddDebug("Could not allocate memory for interface list");
return {};
}
if (auto ret = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr,
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(p_addresses.get()),
&p_addesses_size);
ret != ERROR_BUFFER_OVERFLOW)
{
if (ret != ERROR_SUCCESS)
{
tr_logAddDebug(fmt::format("Failed to retrieve interface list: {} ({})", ret, tr_win32_format_message(ret)));
return {};
}
break;
}
}
for (auto const* cur = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(p_addresses.get()); cur != nullptr; cur = cur->Next)
{
if (cur->OperStatus != IfOperStatusUp)
{
continue;
}
for (auto const* sa_p = cur->FirstUnicastAddress; sa_p != nullptr; sa_p = sa_p->Next)
{
if (auto if_addr = tr_socket_address::from_sockaddr(sa_p->Address.lpSockaddr);
if_addr && if_addr->address() == *this)
{
auto const ret = type == TR_AF_INET ? cur->IfIndex : cur->Ipv6IfIndex;
tr_logAddDebug(fmt::format("Found interface index for {}: {}", display_name(), ret));
return ret;
}
}
}
#else
struct ifaddrs* ifa = nullptr;
if (getifaddrs(&ifa) != 0)
{
auto err = errno;
tr_logAddDebug(fmt::format("Failed to retrieve interface list: {} ({})", err, tr_strerror(err)));
return {};
}
auto const ifa_uniq = std::unique_ptr<ifaddrs, void (*)(struct ifaddrs*)>{ ifa, freeifaddrs };
for (; ifa != nullptr; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == nullptr || (ifa->ifa_flags & IFF_UP) == 0U)
{
continue;
}
if (auto if_addr = tr_socket_address::from_sockaddr(ifa->ifa_addr); if_addr && if_addr->address() == *this)
{
auto const ret = if_nametoindex(ifa->ifa_name);
tr_logAddDebug(fmt::format("Found interface index for {}: {}", display_name(), ret));
return if_nametoindex(ifa->ifa_name);
}
}
#endif
tr_logAddDebug(fmt::format("Could not find interface index for {}", display_name()));
return {};
}
int tr_address::compare(tr_address const& that) const noexcept // <=>
{
// IPv6 addresses are always "greater than" IPv4

View File

@ -161,11 +161,11 @@ struct tr_address
[[nodiscard]] static std::pair<tr_address, std::byte const*> from_compact_ipv4(std::byte const* compact) noexcept;
[[nodiscard]] static std::pair<tr_address, std::byte const*> from_compact_ipv6(std::byte const* compact) noexcept;
// write the text form of the address, e.g. inet_ntop()
// --- write the text form of the address, e.g. inet_ntop()
std::string_view display_name(char* out, size_t outlen) const;
[[nodiscard]] std::string display_name() const;
///
// ---
[[nodiscard]] constexpr auto is_ipv4() const noexcept
{
@ -177,7 +177,7 @@ struct tr_address
return type == TR_AF_INET6;
}
/// bt protocol compact form
// --- bt protocol compact form
// compact addr only -- used e.g. as `yourip` value in extension protocol handshake
@ -208,7 +208,11 @@ struct tr_address
}
}
// comparisons
// ---
[[nodiscard]] std::optional<unsigned> to_interface_index() const noexcept;
// --- comparisons
[[nodiscard]] int compare(tr_address const& that) const noexcept;
@ -232,7 +236,7 @@ struct tr_address
return this->compare(that) > 0;
}
//
// ---
[[nodiscard]] bool is_global_unicast_address() const noexcept;

View File

@ -20,8 +20,8 @@
#ifdef _WIN32
#include <ws2tcpip.h>
#else
#include <sys/socket.h> /* socket(), bind() */
#include <netinet/in.h> /* sockaddr_in */
#include <sys/socket.h> /* socket(), bind() */
#endif
#include <event2/event.h>
@ -410,7 +410,7 @@ private:
/* we want to join that LPD multicast group */
struct ipv6_mreq mcast_req = {};
mcast_req.ipv6mr_multiaddr = mcast6_addr_.sin6_addr;
mcast_req.ipv6mr_interface = 0; //FIXME
mcast_req.ipv6mr_interface = mediator_.bind_address(ip_protocol).to_interface_index().value_or(0);
if (setsockopt(
sock,