Compare commits

...

6 commits

Author SHA1 Message Date
Will Clark
8534b1e0eb
Merge 4c53178256 into c5e44a0435 2025-04-29 11:53:25 +02:00
merge-script
c5e44a0435
Merge bitcoin/bitcoin#32369: test: Use the correct node for doubled keypath test
Some checks are pending
CI / macOS 14 native, arm64, fuzz (push) Waiting to run
CI / Windows native, VS 2022 (push) Waiting to run
CI / Windows native, fuzz, VS 2022 (push) Waiting to run
CI / Linux->Windows cross, no tests (push) Waiting to run
CI / Windows, test cross-built (push) Blocked by required conditions
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
32d55e28af test: Use the correct node for doubled keypath test (Ava Chow)

Pull request description:

  #29124 had a silent merge conflict with #32350 which resulted in it using the wrong node. Fix the test to use the correct v22 node.

ACKs for top commit:
  maflcko:
    lgtm ACK 32d55e28af
  rkrux:
    ACK 32d55e28af
  BrandonOdiwuor:
    Code Review ACK 32d55e28af

Tree-SHA512: 1e0231985beb382b16e1d608c874750423d0502388db0c8ad450b22d17f9d96f5e16a6b44948ebda5efc750f62b60d0de8dd20131f449427426a36caf374af92
2025-04-29 09:59:42 +01:00
Ava Chow
32d55e28af test: Use the correct node for doubled keypath test 2025-04-28 14:44:17 -07:00
willcl-ark
4c53178256
net: handle multi-part netlink responses
Handle multi-part netlink responses to prevent truncated results from
large routing tables.

Previously, we only made a single recv call, which led to incomplete
results when the kernel split the message into multiple responses (which
happens frequently with NLM_F_DUMP).

Also guard against a potential hanging issue where the code would
indefinitely wait for NLMSG_DONE for non-multi-part responses by
detecting the NLM_F_MULTI flag and only continue waiting when necessary.
2025-04-02 12:01:01 +01:00
willcl-ark
42e99ad773
net: skip non-route netlink responses
This shouldn't usually be hit, but is a good belt-and-braces.
2025-04-02 09:47:27 +01:00
willcl-ark
57ce645f05
net: filter for default routes in netlink responses
Filter netlink responses to only consider default routes by checking the
destination prefix length (rtm_dst_len == 0).

Previously, we selected the first route with an RTA_GATEWAY attribute,
which for IPv6 often resulted in choosing a non-default route instead of
the actual default.

This caused occasional PCP port mapping failures because a gateway for a
non-default route was selected.
2025-04-02 09:46:56 +01:00
2 changed files with 71 additions and 31 deletions

View file

@ -36,6 +36,9 @@ namespace {
// 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.
@ -84,41 +87,78 @@ 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;
}
ssize_t total_bytes_read{0};
bool done{false};
bool multi_part{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;
}
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);
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;
}
// 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));
bool processed_one{false};
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);
processed_one = true;
if (hdr->nlmsg_flags & NLM_F_MULTI) {
multi_part = true;
}
if (hdr->nlmsg_type == NLMSG_DONE) {
done = true;
break;
}
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);
}
}
}
// 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);
}
// If we processed at least one message and multi flag not set, or if
// we received no valid messages, then we're done.
if ((processed_one && !multi_part) || !processed_one) {
done = true;
}
}

View file

@ -87,7 +87,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# 0.21.x and 22.x would both produce bad derivation paths when topping up an inactive hd chain
# Make sure that this is being automatically cleaned up by migration
node_master = self.nodes[1]
node_v22 = self.nodes[self.num_nodes - 5]
node_v22 = self.nodes[self.num_nodes - 3]
wallet_name = "bad_deriv_path"
node_v22.createwallet(wallet_name=wallet_name, descriptors=False)
bad_deriv_wallet = node_v22.get_wallet_rpc(wallet_name)