@@ -36,6 +36,9 @@ namespace {
3636// will fail, so we skip that.
3737#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1400000)
3838
39+ // Good for responses containing ~ 10,000-15,000 routes.
40+ static constexpr ssize_t NETLINK_MAX_RESPONSE_SIZE{1'048'576 };
41+
3942std::optional<CNetAddr> QueryDefaultGatewayImpl (sa_family_t family)
4043{
4144 // Create a netlink socket.
@@ -84,50 +87,78 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
8487
8588 // Receive response.
8689 char response[4096 ];
87- int64_t recv_result;
88- do {
89- recv_result = sock->Recv (response, sizeof (response), 0 );
90- } while (recv_result < 0 && (errno == EINTR || errno == EAGAIN));
91- if (recv_result < 0 ) {
92- LogPrintLevel (BCLog::NET, BCLog::Level::Error, " recv() from netlink socket: %s\n " , NetworkErrorString (errno));
93- return std::nullopt ;
94- }
95-
96- for (nlmsghdr* hdr = (nlmsghdr*)response; NLMSG_OK (hdr, recv_result); hdr = NLMSG_NEXT (hdr, recv_result)) {
97- rtmsg* r = (rtmsg*)NLMSG_DATA (hdr);
98- int remaining_len = RTM_PAYLOAD (hdr);
99-
100- if (hdr->nlmsg_type != RTM_NEWROUTE) {
101- continue ; // Skip non-route messages
90+ ssize_t total_bytes_read{0 };
91+ bool done{false };
92+ bool multi_part{false };
93+ while (!done) {
94+ int64_t recv_result;
95+ do {
96+ recv_result = sock->Recv (response, sizeof (response), 0 );
97+ } while (recv_result < 0 && (errno == EINTR || errno == EAGAIN));
98+ if (recv_result < 0 ) {
99+ LogPrintLevel (BCLog::NET, BCLog::Level::Error, " recv() from netlink socket: %s\n " , NetworkErrorString (errno));
100+ return std::nullopt ;
102101 }
103102
104- // Only consider default routes (destination prefix length of 0).
105- if (r->rtm_dst_len != 0 ) {
106- continue ;
103+ total_bytes_read += recv_result;
104+ if (total_bytes_read > NETLINK_MAX_RESPONSE_SIZE) {
105+ LogPrintLevel (BCLog::NET, BCLog::Level::Warning, " Netlink response exceeded size limit (%zu bytes, family=%d)\n " , NETLINK_MAX_RESPONSE_SIZE, family);
106+ return std::nullopt ;
107107 }
108108
109- // Iterate over the attributes.
110- rtattr *rta_gateway = nullptr ;
111- int scope_id = 0 ;
112- for (rtattr* attr = RTM_RTA (r); RTA_OK (attr, remaining_len); attr = RTA_NEXT (attr, remaining_len)) {
113- if (attr-> rta_type == RTA_GATEWAY) {
114- rta_gateway = attr ;
115- } else if (attr-> rta_type == RTA_OIF && sizeof ( int ) == RTA_PAYLOAD (attr) ) {
116- std::memcpy (&scope_id, RTA_DATA (attr), sizeof (scope_id)) ;
109+ bool processed_one{ false };
110+ for (nlmsghdr* hdr = (nlmsghdr*)response; NLMSG_OK (hdr, recv_result); hdr = NLMSG_NEXT (hdr, recv_result)) {
111+ rtmsg* r = (rtmsg*) NLMSG_DATA (hdr) ;
112+ int remaining_len = RTM_PAYLOAD (hdr);
113+
114+ processed_one = true ;
115+ if (hdr-> nlmsg_flags & NLM_F_MULTI ) {
116+ multi_part = true ;
117117 }
118- }
119118
120- // Found gateway?
121- if (rta_gateway != nullptr ) {
122- if (family == AF_INET && sizeof (in_addr) == RTA_PAYLOAD (rta_gateway)) {
123- in_addr gw;
124- std::memcpy (&gw, RTA_DATA (rta_gateway), sizeof (gw));
125- return CNetAddr (gw);
126- } else if (family == AF_INET6 && sizeof (in6_addr) == RTA_PAYLOAD (rta_gateway)) {
127- in6_addr gw;
128- std::memcpy (&gw, RTA_DATA (rta_gateway), sizeof (gw));
129- return CNetAddr (gw, scope_id);
119+ if (hdr->nlmsg_type == NLMSG_DONE) {
120+ done = true ;
121+ break ;
130122 }
123+
124+ if (hdr->nlmsg_type != RTM_NEWROUTE) {
125+ continue ; // Skip non-route messages
126+ }
127+
128+ // Only consider default routes (destination prefix length of 0).
129+ if (r->rtm_dst_len != 0 ) {
130+ continue ;
131+ }
132+
133+ // Iterate over the attributes.
134+ rtattr* rta_gateway = nullptr ;
135+ int scope_id = 0 ;
136+ for (rtattr* attr = RTM_RTA (r); RTA_OK (attr, remaining_len); attr = RTA_NEXT (attr, remaining_len)) {
137+ if (attr->rta_type == RTA_GATEWAY) {
138+ rta_gateway = attr;
139+ } else if (attr->rta_type == RTA_OIF && sizeof (int ) == RTA_PAYLOAD (attr)) {
140+ std::memcpy (&scope_id, RTA_DATA (attr), sizeof (scope_id));
141+ }
142+ }
143+
144+ // Found gateway?
145+ if (rta_gateway != nullptr ) {
146+ if (family == AF_INET && sizeof (in_addr) == RTA_PAYLOAD (rta_gateway)) {
147+ in_addr gw;
148+ std::memcpy (&gw, RTA_DATA (rta_gateway), sizeof (gw));
149+ return CNetAddr (gw);
150+ } else if (family == AF_INET6 && sizeof (in6_addr) == RTA_PAYLOAD (rta_gateway)) {
151+ in6_addr gw;
152+ std::memcpy (&gw, RTA_DATA (rta_gateway), sizeof (gw));
153+ return CNetAddr (gw, scope_id);
154+ }
155+ }
156+ }
157+
158+ // If we processed at least one message and multi flag not set, or if
159+ // we received no valid messages, then we're done.
160+ if ((processed_one && !multi_part) || !processed_one) {
161+ done = true ;
131162 }
132163 }
133164
0 commit comments