Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or https://www.opensource.org/licenses/mit-license.php.5#include <bitcoin-build-config.h> // IWYU pragma: keep7#include <common/netif.h>9#include <compat/compat.h>10#include <netbase.h>11#include <util/check.h>12#include <util/log.h>13#include <util/sock.h>15#include <cerrno>16#include <cstdint>17#include <cstring>18#include <functional>19#include <memory>20#include <string>21#include <type_traits>23#if defined(__linux__)24#include <linux/netlink.h>25#include <linux/rtnetlink.h>26#elif defined(__FreeBSD__)27#include <netlink/netlink.h>28#include <netlink/netlink_route.h>29#elif defined(WIN32)30#include <iphlpapi.h>31#elif defined(__APPLE__)32#include <net/route.h>33#include <sys/sysctl.h>34#endif36#ifdef HAVE_IFADDRS37#include <ifaddrs.h>38#endif40namespace {42//! Return CNetAddr for the specified OS-level network address.43//! If a length is not given, it is taken to be sizeof(struct sockaddr_*) for the family.44std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr, std::optional<socklen_t> sa_len_opt)45{46socklen_t sa_len = 0;47if (sa_len_opt.has_value()) {48sa_len = *sa_len_opt;49} else {50// If sockaddr length was not specified, determine it from the family.51switch (addr->sa_family) {52case AF_INET: sa_len = sizeof(struct sockaddr_in); break;53case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;54default:55return std::nullopt;56}57}58// Fill in a CService from the sockaddr, then drop the port part.59CService service;60if (service.SetSockAddr(addr, sa_len)) {61return (CNetAddr)service;62}63return std::nullopt;64}66// Linux and FreeBSD.67#if defined(__linux__) || defined(__FreeBSD__)69// Good for responses containing ~ 10,000-15,000 routes.70static constexpr ssize_t NETLINK_MAX_RESPONSE_SIZE{1'048'576};72std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)73{74// Create a netlink socket.75auto sock{CreateSock(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)};76if (!sock) {77LogError("socket(AF_NETLINK): %s\n", NetworkErrorString(errno));78return std::nullopt;79}81// Send request.82struct {83nlmsghdr hdr; ///< Request header.84rtmsg data; ///< Request data, a "route message".85nlattr dst_hdr; ///< One attribute, conveying the route destination address.86char dst_data[16]; ///< Route destination address. To query the default route we use 0.0.0.0/0 or [::]/0. For IPv4 the first 4 bytes are used.87} request{};89// Whether to use the first 4 or 16 bytes from request.dst_data.90const size_t dst_data_len = family == AF_INET ? 4 : 16;92request.hdr.nlmsg_type = RTM_GETROUTE;93request.hdr.nlmsg_flags = NLM_F_REQUEST;94#ifdef __linux__95// Linux IPv4 / IPv6 - this must be present, otherwise no gateway is found96// FreeBSD IPv4 - does not matter, the gateway is found with or without this97// FreeBSD IPv6 - this must be absent, otherwise no gateway is found98request.hdr.nlmsg_flags |= NLM_F_DUMP;99#endif100request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg) + sizeof(nlattr) + dst_data_len);101request.hdr.nlmsg_seq = 0; // Sequence number, used to match which reply is to which request. Irrelevant for us because we send just one request.102request.data.rtm_family = family;103request.data.rtm_dst_len = 0; // Prefix length.104#ifdef __FreeBSD__105// Linux IPv4 / IPv6 this must be absent, otherwise no gateway is found106// FreeBSD IPv4 - does not matter, the gateway is found with or without this107// FreeBSD IPv6 - this must be present, otherwise no gateway is found108request.data.rtm_flags = RTM_F_PREFIX;109#endif110request.dst_hdr.nla_type = RTA_DST;111request.dst_hdr.nla_len = sizeof(nlattr) + dst_data_len;113if (sock->Send(&request, request.hdr.nlmsg_len, 0) != static_cast<ssize_t>(request.hdr.nlmsg_len)) {114LogError("send() to netlink socket: %s\n", NetworkErrorString(errno));115return std::nullopt;116}118// Receive response.119char response[4096];120ssize_t total_bytes_read{0};121bool done{false};122while (!done) {123int64_t recv_result;124do {125recv_result = sock->Recv(response, sizeof(response), 0);126} while (recv_result < 0 && (errno == EINTR || errno == EAGAIN));127if (recv_result < 0) {128LogError("recv() from netlink socket: %s\n", NetworkErrorString(errno));129return std::nullopt;130}132total_bytes_read += recv_result;133if (total_bytes_read > NETLINK_MAX_RESPONSE_SIZE) {134LogWarning("Netlink response exceeded size limit (%zu bytes, family=%d)\n", NETLINK_MAX_RESPONSE_SIZE, family);135return std::nullopt;136}138using recv_result_t = std::conditional_t<std::is_signed_v<decltype(NLMSG_HDRLEN)>, int64_t, decltype(NLMSG_HDRLEN)>;140for (nlmsghdr* hdr = (nlmsghdr*)response; NLMSG_OK(hdr, static_cast<recv_result_t>(recv_result)); hdr = NLMSG_NEXT(hdr, recv_result)) {141if (!(hdr->nlmsg_flags & NLM_F_MULTI)) {142done = true;143}145if (hdr->nlmsg_type == NLMSG_DONE) {146done = true;147break;148}150rtmsg* r = (rtmsg*)NLMSG_DATA(hdr);151int remaining_len = RTM_PAYLOAD(hdr);153if (hdr->nlmsg_type != RTM_NEWROUTE) {154continue; // Skip non-route messages155}157// Only consider default routes (destination prefix length of 0).158if (r->rtm_dst_len != 0) {159continue;160}162// Iterate over the attributes.163rtattr* rta_gateway = nullptr;164int scope_id = 0;165for (rtattr* attr = RTM_RTA(r); RTA_OK(attr, remaining_len); attr = RTA_NEXT(attr, remaining_len)) {166if (attr->rta_type == RTA_GATEWAY) {167rta_gateway = attr;168} else if (attr->rta_type == RTA_OIF && sizeof(int) == RTA_PAYLOAD(attr)) {169std::memcpy(&scope_id, RTA_DATA(attr), sizeof(scope_id));170}171}173// Found gateway?174if (rta_gateway != nullptr) {175if (family == AF_INET && sizeof(in_addr) == RTA_PAYLOAD(rta_gateway)) {176in_addr gw;177std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));178return CNetAddr(gw);179} else if (family == AF_INET6 && sizeof(in6_addr) == RTA_PAYLOAD(rta_gateway)) {180in6_addr gw;181std::memcpy(&gw, RTA_DATA(rta_gateway), sizeof(gw));182return CNetAddr(gw, scope_id);183}184}185}186}188return std::nullopt;189}191#elif defined(WIN32)193std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)194{195NET_LUID interface_luid = {};196SOCKADDR_INET destination_address = {};197MIB_IPFORWARD_ROW2 best_route = {};198SOCKADDR_INET best_source_address = {};199DWORD best_if_idx = 0;200DWORD status = 0;202// Pass empty destination address of the requested type (:: or 0.0.0.0) to get interface of default route.203destination_address.si_family = family;204status = GetBestInterfaceEx((sockaddr*)&destination_address, &best_if_idx);205if (status != NO_ERROR) {206LogError("Could not get best interface for default route: %s\n", NetworkErrorString(status));207return std::nullopt;208}210// Get best route to default gateway.211// Leave interface_luid at all-zeros to use interface index instead.212status = GetBestRoute2(&interface_luid, best_if_idx, nullptr, &destination_address, 0, &best_route, &best_source_address);213if (status != NO_ERROR) {214LogError("Could not get best route for default route for interface index %d: %s\n",215best_if_idx, NetworkErrorString(status));216return std::nullopt;217}219Assume(best_route.NextHop.si_family == family);220if (family == AF_INET) {221return CNetAddr(best_route.NextHop.Ipv4.sin_addr);222} else if(family == AF_INET6) {223return CNetAddr(best_route.NextHop.Ipv6.sin6_addr, best_route.InterfaceIndex);224}225return std::nullopt;226}228#elif defined(__APPLE__)230#define ROUNDUP32(a) \231((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))233//! MacOS: Get default gateway from route table. See route(4) for the format.234std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)235{236// net.route.0.inet[6].flags.gateway237int mib[] = {CTL_NET, PF_ROUTE, 0, family, NET_RT_FLAGS, RTF_GATEWAY};238// The size of the available data is determined by calling sysctl() with oldp=nullptr. See sysctl(3).239size_t l = 0;240if (sysctl(/*name=*/mib, /*namelen=*/sizeof(mib) / sizeof(int), /*oldp=*/nullptr, /*oldlenp=*/&l, /*newp=*/nullptr, /*newlen=*/0) < 0) {241LogError("Could not get sysctl length of routing table: %s\n", NetworkErrorString(errno));242return std::nullopt;243}244std::vector<std::byte> buf(l);245if (sysctl(/*name=*/mib, /*namelen=*/sizeof(mib) / sizeof(int), /*oldp=*/buf.data(), /*oldlenp=*/&l, /*newp=*/nullptr, /*newlen=*/0) < 0) {246LogError("Could not get sysctl data of routing table: %s\n", NetworkErrorString(errno));247return std::nullopt;248}249// Iterate over messages (each message is a routing table entry).250for (size_t msg_pos = 0; msg_pos < buf.size(); ) {
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.