| // Copyright 2018 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net-base/rtnl_handler.h" | |
| #include <arpa/inet.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <linux/netlink.h> | |
| #include <linux/rtnetlink.h> | |
| #include <net/if.h> | |
| #include <net/if_arp.h> | |
| #include <netinet/ether.h> | |
| #include <string.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/socket.h> | |
| #include <unistd.h> | |
| #include <limits> | |
| #include <utility> | |
| #include <vector> | |
| #include <base/check.h> | |
| #include <base/containers/contains.h> | |
| #include <base/functional/bind.h> | |
| #include <base/logging.h> | |
| #include <base/strings/stringprintf.h> | |
| #include <base/strings/string_number_conversions.h> | |
| #include "net-base/byte_utils.h" | |
| #include "net-base/rtnl_message.h" | |
| namespace net_base { | |
| const uint32_t RTNLHandler::kErrorWindowSize = 16; | |
| const uint32_t RTNLHandler::kStoredRequestWindowSize = 32; | |
| namespace { | |
| base::LazyInstance<RTNLHandler>::DestructorAtExit g_rtnl_handler = | |
| LAZY_INSTANCE_INITIALIZER; | |
| // Increasing buffer size to avoid overflows on IPV6 routing events. | |
| constexpr int kReceiveBufferBytes = 3 * 1024 * 1024; | |
| } // namespace | |
| RTNLHandler::RTNLHandler() | |
| : in_request_(false), | |
| netlink_groups_mask_(0), | |
| request_flags_(0), | |
| request_sequence_(0), | |
| last_dump_sequence_(0) { | |
| error_mask_window_.resize(kErrorWindowSize); | |
| VLOG(2) << "RTNLHandler created"; | |
| } | |
| RTNLHandler::~RTNLHandler() { | |
| VLOG(2) << "RTNLHandler removed"; | |
| Stop(); | |
| } | |
| RTNLHandler* RTNLHandler::GetInstance() { | |
| return g_rtnl_handler.Pointer(); | |
| } | |
| void RTNLHandler::Start(uint32_t netlink_groups_mask) { | |
| netlink_groups_mask_ = netlink_groups_mask; | |
| if (rtnl_socket_ != nullptr) { | |
| return; | |
| } | |
| rtnl_socket_ = socket_factory_->CreateNetlink( | |
| NETLINK_ROUTE, netlink_groups_mask_, kReceiveBufferBytes); | |
| if (rtnl_socket_ == nullptr) { | |
| PLOG(ERROR) << __func__ << " failed to create netlink socket."; | |
| return; | |
| } | |
| rtnl_socket_->SetReadableCallback( | |
| base::BindRepeating(&RTNLHandler::OnReadable, base::Unretained(this))); | |
| NextRequest(last_dump_sequence_); | |
| VLOG(2) << "RTNLHandler started"; | |
| } | |
| void RTNLHandler::Stop() { | |
| rtnl_socket_.reset(); | |
| in_request_ = false; | |
| request_flags_ = 0; | |
| request_sequence_ = 0; | |
| last_dump_sequence_ = 0; | |
| stored_requests_.clear(); | |
| oldest_request_sequence_ = 0; | |
| VLOG(2) << "RTNLHandler stopped"; | |
| } | |
| void RTNLHandler::OnReadable() { | |
| std::vector<uint8_t> message; | |
| if (rtnl_socket_->RecvMessage(&message)) { | |
| ParseRTNL(message); | |
| } else { | |
| PLOG(ERROR) << "RTNL Socket read returns error"; | |
| ResetSocket(); | |
| } | |
| } | |
| void RTNLHandler::AddListener(RTNLListener* to_add) { | |
| listeners_.AddObserver(to_add); | |
| VLOG(2) << "RTNLHandler added listener"; | |
| } | |
| void RTNLHandler::RemoveListener(RTNLListener* to_remove) { | |
| listeners_.RemoveObserver(to_remove); | |
| VLOG(2) << "RTNLHandler removed listener"; | |
| } | |
| void RTNLHandler::SetInterfaceFlags(int interface_index, | |
| unsigned int flags, | |
| unsigned int change) { | |
| if (rtnl_socket_ == nullptr) { | |
| LOG(ERROR) << __func__ | |
| << " called while not started. " | |
| "Assuming we are in unit tests."; | |
| return; | |
| } | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeLink, RTNLMessage::kModeAdd, NLM_F_REQUEST, | |
| 0, // sequence to be filled in by RTNLHandler::SendMessage(). | |
| 0, // pid. | |
| interface_index, AF_UNSPEC); | |
| msg->set_link_status(RTNLMessage::LinkStatus(ARPHRD_VOID, flags, change)); | |
| ErrorMask error_mask; | |
| if ((flags & IFF_UP) == 0) { | |
| error_mask.insert(ENODEV); | |
| } | |
| SendMessageWithErrorMask(std::move(msg), error_mask, nullptr); | |
| } | |
| void RTNLHandler::SetInterfaceMTU(int interface_index, unsigned int mtu) { | |
| if (rtnl_socket_ == nullptr) { | |
| LOG(ERROR) << __func__ | |
| << " called while not started. " | |
| "Assuming we are in unit tests."; | |
| return; | |
| } | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeLink, RTNLMessage::kModeAdd, NLM_F_REQUEST, | |
| 0, // sequence to be filled in by RTNLHandler::SendMessage(). | |
| 0, // pid. | |
| interface_index, AF_UNSPEC); | |
| msg->SetAttribute(IFLA_MTU, byte_utils::ToBytes<unsigned int>(mtu)); | |
| CHECK(SendMessage(std::move(msg), nullptr)); | |
| } | |
| void RTNLHandler::SetInterfaceMac(int interface_index, | |
| const MacAddress& mac_address) { | |
| SetInterfaceMac(interface_index, mac_address, ResponseCallback()); | |
| } | |
| void RTNLHandler::SetInterfaceMac(int interface_index, | |
| const MacAddress& mac_address, | |
| ResponseCallback response_callback) { | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeLink, RTNLMessage::kModeAdd, NLM_F_REQUEST | NLM_F_ACK, | |
| 0, // sequence to be filled in by RTNLHandler::SendMessage(). | |
| 0, // pid. | |
| interface_index, AF_UNSPEC); | |
| msg->SetAttribute(IFLA_ADDRESS, mac_address.ToBytes()); | |
| uint32_t seq; | |
| CHECK(SendMessage(std::move(msg), &seq)); | |
| if (!response_callback.is_null()) { | |
| response_callbacks_[seq] = std::move(response_callback); | |
| } | |
| } | |
| void RTNLHandler::RequestDump(uint32_t request_flags) { | |
| if (rtnl_socket_ == nullptr) { | |
| LOG(ERROR) << __func__ | |
| << " called while not started. " | |
| "Assuming we are in unit tests."; | |
| return; | |
| } | |
| request_flags_ |= request_flags; | |
| VLOG(2) << base::StringPrintf("RTNLHandler got request to dump 0x%x", | |
| request_flags); | |
| if (!in_request_) { | |
| NextRequest(last_dump_sequence_); | |
| } | |
| } | |
| void RTNLHandler::DispatchEvent(uint32_t type, const RTNLMessage& msg) { | |
| for (RTNLListener& listener : listeners_) { | |
| listener.NotifyEvent(type, msg); | |
| } | |
| } | |
| void RTNLHandler::NextRequest(uint32_t seq) { | |
| uint32_t flag = 0; | |
| RTNLMessage::Type type; | |
| VLOG(2) << base::StringPrintf("RTNLHandler nextrequest %d %d 0x%x", seq, | |
| last_dump_sequence_, request_flags_); | |
| if (seq != last_dump_sequence_) | |
| return; | |
| sa_family_t family = AF_UNSPEC; | |
| if ((request_flags_ & kRequestAddr) != 0) { | |
| type = RTNLMessage::kTypeAddress; | |
| flag = kRequestAddr; | |
| } else if ((request_flags_ & kRequestRoute) != 0) { | |
| type = RTNLMessage::kTypeRoute; | |
| flag = kRequestRoute; | |
| } else if ((request_flags_ & kRequestRule) != 0) { | |
| type = RTNLMessage::kTypeRule; | |
| flag = kRequestRule; | |
| } else if ((request_flags_ & kRequestLink) != 0) { | |
| type = RTNLMessage::kTypeLink; | |
| flag = kRequestLink; | |
| } else if ((request_flags_ & kRequestNeighbor) != 0) { | |
| type = RTNLMessage::kTypeNeighbor; | |
| flag = kRequestNeighbor; | |
| } else if ((request_flags_ & kRequestBridgeNeighbor) != 0) { | |
| type = RTNLMessage::kTypeNeighbor; | |
| flag = kRequestBridgeNeighbor; | |
| family = AF_BRIDGE; | |
| } else { | |
| VLOG(2) << "Done with requests"; | |
| in_request_ = false; | |
| return; | |
| } | |
| auto msg = std::make_unique<RTNLMessage>(type, RTNLMessage::kModeGet, 0, 0, 0, | |
| 0, family); | |
| uint32_t msg_seq; | |
| CHECK(SendMessage(std::move(msg), &msg_seq)); | |
| last_dump_sequence_ = msg_seq; | |
| request_flags_ &= ~flag; | |
| in_request_ = true; | |
| } | |
| void RTNLHandler::ParseRTNL(base::span<const uint8_t> data) { | |
| const uint8_t* buf = data.data(); | |
| const uint8_t* end = buf + data.size(); | |
| while (buf + sizeof(struct nlmsghdr) <= end) { | |
| const struct nlmsghdr* hdr = reinterpret_cast<const struct nlmsghdr*>(buf); | |
| if (!NLMSG_OK(hdr, static_cast<unsigned int>(end - buf))) { | |
| break; | |
| } | |
| const uint8_t* payload = reinterpret_cast<const uint8_t*>(hdr); | |
| VLOG(5) << __func__ << "RTNL received payload length " << hdr->nlmsg_len | |
| << ": \"" << base::HexEncode(payload, hdr->nlmsg_len) << "\""; | |
| // Swapping out of |stored_requests_| here ensures that the RTNLMessage will | |
| // be destructed regardless of the control flow below. | |
| std::unique_ptr<RTNLMessage> request_msg = PopStoredRequest(hdr->nlmsg_seq); | |
| std::unique_ptr<RTNLMessage> msg = | |
| RTNLMessage::Decode({payload, hdr->nlmsg_len}); | |
| if (msg) { | |
| switch (msg->type()) { | |
| case RTNLMessage::kTypeLink: | |
| DispatchEvent(kRequestLink, *msg); | |
| break; | |
| case RTNLMessage::kTypeAddress: | |
| DispatchEvent(kRequestAddr, *msg); | |
| break; | |
| case RTNLMessage::kTypeRoute: | |
| DispatchEvent(kRequestRoute, *msg); | |
| break; | |
| case RTNLMessage::kTypeRule: | |
| DispatchEvent(kRequestRule, *msg); | |
| break; | |
| case RTNLMessage::kTypeNeighbor: | |
| DispatchEvent(kRequestNeighbor, *msg); | |
| break; | |
| // Various ND user options. | |
| case RTNLMessage::kTypeRdnss: | |
| case RTNLMessage::kTypeDnssl: | |
| case RTNLMessage::kTypeCaptivePortal: | |
| case RTNLMessage::kTypeNdUserOption: | |
| DispatchEvent(kRequestNdUserOption, *msg); | |
| break; | |
| default: | |
| LOG(ERROR) << "Unknown RTNL message type: " << msg->type(); | |
| } | |
| } else { | |
| VLOG(5) << __func__ << ": rtnl packet type " << hdr->nlmsg_type | |
| << " length " << hdr->nlmsg_len << " sequence " << hdr->nlmsg_seq; | |
| switch (hdr->nlmsg_type) { | |
| case NLMSG_NOOP: | |
| case NLMSG_OVERRUN: | |
| break; | |
| case NLMSG_DONE: | |
| GetAndClearErrorMask(hdr->nlmsg_seq); // Clear any queued error mask. | |
| NextRequest(hdr->nlmsg_seq); | |
| break; | |
| case NLMSG_ERROR: { | |
| if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { | |
| VLOG(5) << "invalid error message header: length " | |
| << hdr->nlmsg_len; | |
| break; | |
| } | |
| const struct nlmsgerr* hdrErr = | |
| reinterpret_cast<nlmsgerr*>(NLMSG_DATA(hdr)); | |
| std::string request_str; | |
| RTNLMessage::Mode mode = RTNLMessage::kModeUnknown; | |
| if (request_msg) { | |
| request_str = " (" + request_msg->ToString() + ")"; | |
| mode = request_msg->mode(); | |
| } else { | |
| const uint8_t* error_payload = | |
| reinterpret_cast<const uint8_t*>(&(hdrErr->msg)); | |
| if (NLMSG_OK(&(hdrErr->msg), | |
| static_cast<unsigned int>(end - error_payload))) { | |
| const auto msgErr = | |
| RTNLMessage::Decode({error_payload, hdrErr->msg.nlmsg_len}); | |
| if (msgErr) { | |
| request_str = " (" + msgErr->ToString() + ")"; | |
| mode = msgErr->mode(); | |
| } | |
| } | |
| } | |
| if (request_str.empty()) { | |
| request_str = "(Request Unavailable)"; | |
| } | |
| int error_number = hdrErr->error; | |
| if (error_number == 0) { | |
| VLOG(3) << base::StringPrintf("sequence %d%s received success", | |
| hdr->nlmsg_seq, request_str.c_str()); | |
| } else if ((error_number > 0 || | |
| error_number == std::numeric_limits<int>::min())) { | |
| LOG(ERROR) << base::StringPrintf( | |
| "sequence %d%s received invalid error %d", hdr->nlmsg_seq, | |
| request_str.c_str(), error_number); | |
| } else { | |
| error_number = -error_number; | |
| std::string error_msg = base::StringPrintf( | |
| "sequence %d%s received error %d (%s)", hdr->nlmsg_seq, | |
| request_str.c_str(), error_number, strerror(error_number)); | |
| if (base::Contains(GetAndClearErrorMask(hdr->nlmsg_seq), | |
| error_number) || | |
| (error_number == EEXIST && mode == RTNLMessage::kModeAdd) || | |
| (mode == RTNLMessage::kModeDelete && | |
| (error_number == ENOENT || error_number == ESRCH || | |
| error_number == ENODEV || error_number == EADDRNOTAVAIL))) { | |
| // EEXIST for create requests and ENOENT, ESRCH, ENODEV, | |
| // EADDRNOTAVAIL for delete requests do not really indicate an | |
| // error condition. | |
| VLOG(2) << error_msg; | |
| } else { | |
| LOG(ERROR) << error_msg; | |
| } | |
| } | |
| auto response_callback_iter = | |
| response_callbacks_.find(hdr->nlmsg_seq); | |
| if (response_callback_iter != response_callbacks_.end()) { | |
| std::move(response_callback_iter->second).Run(error_number); | |
| response_callbacks_.erase(response_callback_iter); | |
| } | |
| break; | |
| } | |
| default: | |
| LOG(ERROR) << "Unknown NL message type: " << hdr->nlmsg_type; | |
| } | |
| } | |
| buf += NLMSG_ALIGN(hdr->nlmsg_len); | |
| } | |
| } | |
| bool RTNLHandler::AddressRequest(int interface_index, | |
| RTNLMessage::Mode mode, | |
| int flags, | |
| const IPCIDR& local, | |
| const std::optional<IPv4Address>& broadcast) { | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeAddress, mode, NLM_F_REQUEST | flags, 0, 0, | |
| interface_index, ToSAFamily(local.GetFamily())); | |
| msg->set_address_status(RTNLMessage::AddressStatus( | |
| static_cast<unsigned char>(local.prefix_length()), 0, 0)); | |
| // By definition, IFA_LOCAL and IFA_ADDRESS are the same for an interface not | |
| // point-to-point, which should always be true in our case. | |
| msg->SetAttribute(IFA_LOCAL, local.address().ToBytes()); | |
| msg->SetAttribute(IFA_ADDRESS, local.address().ToBytes()); | |
| if (broadcast) { | |
| CHECK_EQ(local.GetFamily(), IPFamily::kIPv4); | |
| msg->SetAttribute(IFA_BROADCAST, broadcast->ToBytes()); | |
| } | |
| return SendMessage(std::move(msg), nullptr); | |
| } | |
| bool RTNLHandler::AddInterfaceAddress( | |
| int interface_index, | |
| const IPCIDR& local, | |
| const std::optional<IPv4Address>& broadcast) { | |
| return AddressRequest(interface_index, RTNLMessage::kModeAdd, | |
| NLM_F_CREATE | NLM_F_EXCL | NLM_F_ECHO, local, | |
| broadcast); | |
| } | |
| bool RTNLHandler::RemoveInterfaceAddress(int interface_index, | |
| const IPCIDR& local) { | |
| return AddressRequest(interface_index, RTNLMessage::kModeDelete, NLM_F_ECHO, | |
| local, std::nullopt); | |
| } | |
| bool RTNLHandler::RemoveInterface(int interface_index) { | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeLink, RTNLMessage::kModeDelete, NLM_F_REQUEST, 0, 0, | |
| interface_index, AF_UNSPEC); | |
| return SendMessage(std::move(msg), nullptr); | |
| } | |
| int RTNLHandler::GetInterfaceIndex(const std::string& interface_name) { | |
| if (interface_name.empty()) { | |
| LOG(ERROR) << "Empty interface name -- unable to obtain index."; | |
| return -1; | |
| } | |
| struct ifreq ifr; | |
| if (interface_name.size() >= sizeof(ifr.ifr_name)) { | |
| LOG(ERROR) << "Interface name too long: " << interface_name.size() | |
| << " >= " << sizeof(ifr.ifr_name); | |
| return -1; | |
| } | |
| auto socket = socket_factory_->Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); | |
| if (socket == nullptr) { | |
| PLOG(ERROR) << "Unable to open INET socket"; | |
| return -1; | |
| } | |
| memset(&ifr, 0, sizeof(ifr)); | |
| strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name)); | |
| if (!socket->Ioctl(SIOCGIFINDEX, &ifr).has_value()) { | |
| PLOG(ERROR) << "SIOCGIFINDEX error for " << interface_name; | |
| return -1; | |
| } | |
| return ifr.ifr_ifindex; | |
| } | |
| bool RTNLHandler::AddInterface(const std::string& interface_name, | |
| const std::string& link_kind, | |
| base::span<const uint8_t> link_info_data, | |
| ResponseCallback response_callback) { | |
| if (interface_name.length() >= IFNAMSIZ) { | |
| LOG(DFATAL) << "Interface name is too long: " << interface_name; | |
| return false; | |
| } | |
| auto msg = std::make_unique<RTNLMessage>( | |
| RTNLMessage::kTypeLink, RTNLMessage::kModeAdd, | |
| NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, 0 /* seq */, | |
| 0 /* pid */, 0 /* if_index */, AF_UNSPEC); | |
| msg->SetAttribute(IFLA_IFNAME, | |
| byte_utils::StringToCStringBytes(interface_name)); | |
| msg->SetIflaInfoKind(link_kind, link_info_data); | |
| uint32_t seq; | |
| if (!SendMessage(std::move(msg), &seq)) { | |
| LOG(WARNING) << "Failed to send add link message for " << interface_name; | |
| return false; | |
| } | |
| if (!response_callback.is_null()) { | |
| response_callbacks_[seq] = std::move(response_callback); | |
| } | |
| return true; | |
| } | |
| bool RTNLHandler::SendMessage(std::unique_ptr<RTNLMessage> message, | |
| uint32_t* msg_seq) { | |
| ErrorMask error_mask; | |
| if (message->mode() == RTNLMessage::kModeAdd) { | |
| error_mask = {EEXIST}; | |
| } else if (message->mode() == RTNLMessage::kModeDelete) { | |
| error_mask = {ESRCH, ENODEV}; | |
| if (message->type() == RTNLMessage::kTypeAddress) { | |
| error_mask.insert(EADDRNOTAVAIL); | |
| } | |
| } | |
| return SendMessageWithErrorMask(std::move(message), error_mask, msg_seq); | |
| } | |
| bool RTNLHandler::SendMessageWithErrorMask(std::unique_ptr<RTNLMessage> message, | |
| const ErrorMask& error_mask, | |
| uint32_t* msg_seq) { | |
| VLOG(5) << __func__ << " sequence " << request_sequence_ << " message type " | |
| << message->type() << " mode " << message->mode() | |
| << " with error mask size " << error_mask.size(); | |
| SetErrorMask(request_sequence_, error_mask); | |
| message->set_seq(request_sequence_); | |
| const auto msgdata = message->Encode(); | |
| if (msgdata.size() == 0) { | |
| return false; | |
| } | |
| VLOG(5) << "RTNL sending payload with request sequence " << request_sequence_ | |
| << ", length " << msgdata.size() << ": \"" << base::HexEncode(msgdata) | |
| << "\""; | |
| request_sequence_++; | |
| if (!rtnl_socket_->Send(msgdata, 0).has_value()) { | |
| PLOG(ERROR) << "RTNL send failed"; | |
| return false; | |
| } | |
| if (msg_seq) | |
| *msg_seq = message->seq(); | |
| StoreRequest(std::move(message)); | |
| return true; | |
| } | |
| void RTNLHandler::ResetSocket() { | |
| auto it = response_callbacks_.begin(); | |
| while (it != response_callbacks_.end()) { | |
| std::move(it->second).Run(EIO); | |
| response_callbacks_.erase(it); | |
| } | |
| Stop(); | |
| Start(netlink_groups_mask_); | |
| } | |
| bool RTNLHandler::IsSequenceInErrorMaskWindow(uint32_t sequence) { | |
| return (request_sequence_ - sequence) < kErrorWindowSize; | |
| } | |
| void RTNLHandler::SetErrorMask(uint32_t sequence, const ErrorMask& error_mask) { | |
| if (IsSequenceInErrorMaskWindow(sequence)) { | |
| error_mask_window_[sequence % kErrorWindowSize] = error_mask; | |
| } | |
| } | |
| RTNLHandler::ErrorMask RTNLHandler::GetAndClearErrorMask(uint32_t sequence) { | |
| ErrorMask error_mask; | |
| if (IsSequenceInErrorMaskWindow(sequence)) { | |
| error_mask.swap(error_mask_window_[sequence % kErrorWindowSize]); | |
| } | |
| return error_mask; | |
| } | |
| void RTNLHandler::StoreRequest(std::unique_ptr<RTNLMessage> request) { | |
| auto seq = request->seq(); | |
| if (stored_requests_.empty()) { | |
| oldest_request_sequence_ = seq; | |
| } | |
| // Note that this will update an existing stored request of the same sequence | |
| // number, removing the original RTNLMessage. | |
| stored_requests_[seq] = std::move(request); | |
| while (CalculateStoredRequestWindowSize() > kStoredRequestWindowSize) { | |
| auto old_request = PopStoredRequest(oldest_request_sequence_); | |
| CHECK(old_request) << "PopStoredRequest returned nullptr but " | |
| << "the calculated window size is greater than 0. " | |
| << "This is a bug in RTNLHandler."; | |
| VLOG(2) << "Removing stored RTNLMessage of sequence " << old_request->seq() | |
| << " (" << old_request->ToString() | |
| << ") without receiving a response for this sequence"; | |
| } | |
| } | |
| std::unique_ptr<RTNLMessage> RTNLHandler::PopStoredRequest(uint32_t seq) { | |
| auto seq_request = stored_requests_.find(seq); | |
| if (seq_request == stored_requests_.end()) { | |
| return nullptr; | |
| } | |
| std::unique_ptr<RTNLMessage> res; | |
| res.swap(seq_request->second); | |
| if (seq == oldest_request_sequence_) { | |
| auto next_oldest_seq_request = std::next(seq_request); | |
| // Seq overflow could have occurred between the oldest and second oldest | |
| // stored requests. | |
| if (next_oldest_seq_request == stored_requests_.end()) { | |
| next_oldest_seq_request = stored_requests_.begin(); | |
| } | |
| // Note that this condition means |oldest_request_sequence_| will not be | |
| // changed when the last stored request is popped. This does not pose any | |
| // correctness issues. | |
| if (next_oldest_seq_request != seq_request) { | |
| oldest_request_sequence_ = next_oldest_seq_request->first; | |
| } | |
| } | |
| stored_requests_.erase(seq_request); | |
| return res; | |
| } | |
| uint32_t RTNLHandler::CalculateStoredRequestWindowSize() { | |
| if (stored_requests_.size() <= 1) { | |
| return static_cast<uint32_t>(stored_requests_.size()); | |
| } | |
| auto seq_request = stored_requests_.begin(); | |
| if (seq_request->first != oldest_request_sequence_) { | |
| // If we overflowed, the sequence of the newest request is the | |
| // greatest sequence less than |oldest_request_sequence_|. | |
| seq_request = std::prev(stored_requests_.find(oldest_request_sequence_)); | |
| } else { | |
| seq_request = std::prev(stored_requests_.end()); | |
| } | |
| return seq_request->first - oldest_request_sequence_ + 1; | |
| } | |
| } // namespace net_base |