mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-17 17:02:43 +00:00
Merge bitcoin/bitcoin#32159: net, pcp: handle multi-part responses and filter for default route while querying default gateway
88db09bafe9ec363525e5e526c5f6cdd13691447 net: handle multi-part netlink responses (willcl-ark)
42e99ad77396e4e9b02d67daf46349e215e99a0f net: skip non-route netlink responses (willcl-ark)
57ce645f05d18d8ad10711c347a5989076f1f788 net: filter for default routes in netlink responses (willcl-ark)
Pull request description:
...for default route in pcp pinholing.
Currently we only make a single recv call, which trucates results from large routing tables, or in the case the kernel may split the message into multiple responses (which may happen with `NLM_F_DUMP`).
We also do not filter on the default route. For IPv6, this led to selecting the first route with an `RTA_GATEWAY` attribute, often a non-default route instead of the actual default. This caused PCP port mapping failures because the wrong gateway was used.
Fix both issues by adding multi-part handling of responses and filter for the default route.
Limit responses to ~ 1MB to prevent any router-based DoS.
ACKs for top commit:
achow101:
ACK 88db09bafe9ec363525e5e526c5f6cdd13691447
davidgumberg:
Code Review re-ACK 88db09b
Sjors:
re-utACK 88db09bafe9ec363525e5e526c5f6cdd13691447
Tree-SHA512: ea5948edebfad5896a487a61737aa5af99f529fad3cf3da68dced456266948238a7143383847e79a7bb90134e023eb173c25116d8eb80ff57fa4c4a0377ca1ed
This commit is contained in:
commit
2562fe1b2b
@ -65,6 +65,9 @@ std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr, std::optional<
|
||||
// will fail, so we skip that.
|
||||
#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1400000)
|
||||
|
||||
// Good for responses containing ~ 10,000-15,000 routes.
|
||||
static constexpr ssize_t NETLINK_MAX_RESPONSE_SIZE{1'048'576};
|
||||
|
||||
std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
|
||||
{
|
||||
// Create a netlink socket.
|
||||
@ -113,40 +116,68 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
|
||||
|
||||
// Receive response.
|
||||
char response[4096];
|
||||
int64_t recv_result;
|
||||
do {
|
||||
recv_result = sock->Recv(response, sizeof(response), 0);
|
||||
} while (recv_result < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
if (recv_result < 0) {
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "recv() from netlink socket: %s\n", NetworkErrorString(errno));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (nlmsghdr* hdr = (nlmsghdr*)response; NLMSG_OK(hdr, recv_result); hdr = NLMSG_NEXT(hdr, recv_result)) {
|
||||
rtmsg* r = (rtmsg*)NLMSG_DATA(hdr);
|
||||
int remaining_len = RTM_PAYLOAD(hdr);
|
||||
|
||||
// Iterate over the attributes.
|
||||
rtattr *rta_gateway = nullptr;
|
||||
int scope_id = 0;
|
||||
for (rtattr* attr = RTM_RTA(r); RTA_OK(attr, remaining_len); attr = RTA_NEXT(attr, remaining_len)) {
|
||||
if (attr->rta_type == RTA_GATEWAY) {
|
||||
rta_gateway = attr;
|
||||
} else if (attr->rta_type == RTA_OIF && sizeof(int) == RTA_PAYLOAD(attr)) {
|
||||
std::memcpy(&scope_id, RTA_DATA(attr), sizeof(scope_id));
|
||||
}
|
||||
ssize_t total_bytes_read{0};
|
||||
bool done{false};
|
||||
while (!done) {
|
||||
int64_t recv_result;
|
||||
do {
|
||||
recv_result = sock->Recv(response, sizeof(response), 0);
|
||||
} while (recv_result < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
if (recv_result < 0) {
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "recv() from netlink socket: %s\n", NetworkErrorString(errno));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Found gateway?
|
||||
if (rta_gateway != nullptr) {
|
||||
if (family == AF_INET && sizeof(in_addr) == RTA_PAYLOAD(rta_gateway)) {
|
||||
in_addr gw;
|
||||
std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));
|
||||
return CNetAddr(gw);
|
||||
} else if (family == AF_INET6 && sizeof(in6_addr) == RTA_PAYLOAD(rta_gateway)) {
|
||||
in6_addr gw;
|
||||
std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));
|
||||
return CNetAddr(gw, scope_id);
|
||||
total_bytes_read += recv_result;
|
||||
if (total_bytes_read > NETLINK_MAX_RESPONSE_SIZE) {
|
||||
LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "Netlink response exceeded size limit (%zu bytes, family=%d)\n", NETLINK_MAX_RESPONSE_SIZE, family);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (nlmsghdr* hdr = (nlmsghdr*)response; NLMSG_OK(hdr, recv_result); hdr = NLMSG_NEXT(hdr, recv_result)) {
|
||||
if (!(hdr->nlmsg_flags & NLM_F_MULTI)) {
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (hdr->nlmsg_type == NLMSG_DONE) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
rtmsg* r = (rtmsg*)NLMSG_DATA(hdr);
|
||||
int remaining_len = RTM_PAYLOAD(hdr);
|
||||
|
||||
if (hdr->nlmsg_type != RTM_NEWROUTE) {
|
||||
continue; // Skip non-route messages
|
||||
}
|
||||
|
||||
// Only consider default routes (destination prefix length of 0).
|
||||
if (r->rtm_dst_len != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over the attributes.
|
||||
rtattr* rta_gateway = nullptr;
|
||||
int scope_id = 0;
|
||||
for (rtattr* attr = RTM_RTA(r); RTA_OK(attr, remaining_len); attr = RTA_NEXT(attr, remaining_len)) {
|
||||
if (attr->rta_type == RTA_GATEWAY) {
|
||||
rta_gateway = attr;
|
||||
} else if (attr->rta_type == RTA_OIF && sizeof(int) == RTA_PAYLOAD(attr)) {
|
||||
std::memcpy(&scope_id, RTA_DATA(attr), sizeof(scope_id));
|
||||
}
|
||||
}
|
||||
|
||||
// Found gateway?
|
||||
if (rta_gateway != nullptr) {
|
||||
if (family == AF_INET && sizeof(in_addr) == RTA_PAYLOAD(rta_gateway)) {
|
||||
in_addr gw;
|
||||
std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));
|
||||
return CNetAddr(gw);
|
||||
} else if (family == AF_INET6 && sizeof(in6_addr) == RTA_PAYLOAD(rta_gateway)) {
|
||||
in6_addr gw;
|
||||
std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));
|
||||
return CNetAddr(gw, scope_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user