|  | // Copyright (c) 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/qbone/platform/netlink.h" | 
|  |  | 
|  | #include <linux/fib_rules.h> | 
|  | #include <utility> | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" | 
|  | #include "net/quic/platform/impl/quic_ip_address_impl.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/platform/rtnetlink_message.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | Netlink::Netlink(KernelInterface* kernel) : kernel_(kernel) { | 
|  | seq_ = QuicRandom::GetInstance()->RandUint64(); | 
|  | } | 
|  |  | 
|  | Netlink::~Netlink() { | 
|  | CloseSocket(); | 
|  | } | 
|  |  | 
|  | void Netlink::ResetRecvBuf(size_t size) { | 
|  | if (size != 0) { | 
|  | recvbuf_ = std::make_unique<char[]>(size); | 
|  | } else { | 
|  | recvbuf_ = nullptr; | 
|  | } | 
|  | recvbuf_length_ = size; | 
|  | } | 
|  |  | 
|  | bool Netlink::OpenSocket() { | 
|  | if (socket_fd_ >= 0) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | socket_fd_ = kernel_->socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | 
|  |  | 
|  | if (socket_fd_ < 0) { | 
|  | QUIC_PLOG(ERROR) << "can't open netlink socket"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | QUIC_LOG(INFO) << "Opened a new netlink socket fd = " << socket_fd_; | 
|  |  | 
|  | // bind a local address to the socket | 
|  | sockaddr_nl myaddr; | 
|  | memset(&myaddr, 0, sizeof(myaddr)); | 
|  | myaddr.nl_family = AF_NETLINK; | 
|  | if (kernel_->bind(socket_fd_, reinterpret_cast<struct sockaddr*>(&myaddr), | 
|  | sizeof(myaddr)) < 0) { | 
|  | QUIC_LOG(INFO) << "can't bind address to socket"; | 
|  | CloseSocket(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Netlink::CloseSocket() { | 
|  | if (socket_fd_ >= 0) { | 
|  | QUIC_LOG(INFO) << "Closing netlink socket fd = " << socket_fd_; | 
|  | kernel_->close(socket_fd_); | 
|  | } | 
|  | ResetRecvBuf(0); | 
|  | socket_fd_ = -1; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class LinkInfoParser : public NetlinkParserInterface { | 
|  | public: | 
|  | LinkInfoParser(string interface_name, Netlink::LinkInfo* link_info) | 
|  | : interface_name_(std::move(interface_name)), link_info_(link_info) {} | 
|  |  | 
|  | void Run(struct nlmsghdr* netlink_message) override { | 
|  | if (netlink_message->nlmsg_type != RTM_NEWLINK) { | 
|  | QUIC_LOG(INFO) << QuicStrCat( | 
|  | "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, | 
|  | " expected: ", RTM_NEWLINK); | 
|  | return; | 
|  | } | 
|  |  | 
|  | struct ifinfomsg* interface_info = | 
|  | reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_message)); | 
|  |  | 
|  | // make sure interface_info is what we asked for. | 
|  | if (interface_info->ifi_family != AF_UNSPEC) { | 
|  | QUIC_LOG(INFO) << QuicStrCat( | 
|  | "Unexpected ifi_family: ", interface_info->ifi_family, | 
|  | " expected: ", AF_UNSPEC); | 
|  | return; | 
|  | } | 
|  |  | 
|  | char hardware_address[kHwAddrSize]; | 
|  | size_t hardware_address_length = 0; | 
|  | char broadcast_address[kHwAddrSize]; | 
|  | size_t broadcast_address_length = 0; | 
|  | string name; | 
|  |  | 
|  | // loop through the attributes | 
|  | struct rtattr* rta; | 
|  | int payload_length = IFLA_PAYLOAD(netlink_message); | 
|  | for (rta = IFLA_RTA(interface_info); RTA_OK(rta, payload_length); | 
|  | rta = RTA_NEXT(rta, payload_length)) { | 
|  | int attribute_length; | 
|  | switch (rta->rta_type) { | 
|  | case IFLA_ADDRESS: { | 
|  | attribute_length = RTA_PAYLOAD(rta); | 
|  | if (attribute_length > kHwAddrSize) { | 
|  | QUIC_VLOG(2) << "IFLA_ADDRESS too long: " << attribute_length; | 
|  | break; | 
|  | } | 
|  | memmove(hardware_address, RTA_DATA(rta), attribute_length); | 
|  | hardware_address_length = attribute_length; | 
|  | break; | 
|  | } | 
|  | case IFLA_BROADCAST: { | 
|  | attribute_length = RTA_PAYLOAD(rta); | 
|  | if (attribute_length > kHwAddrSize) { | 
|  | QUIC_VLOG(2) << "IFLA_BROADCAST too long: " << attribute_length; | 
|  | break; | 
|  | } | 
|  | memmove(broadcast_address, RTA_DATA(rta), attribute_length); | 
|  | broadcast_address_length = attribute_length; | 
|  | break; | 
|  | } | 
|  | case IFLA_IFNAME: { | 
|  | name = | 
|  | string(reinterpret_cast<char*>(RTA_DATA(rta)), RTA_PAYLOAD(rta)); | 
|  | // The name maybe a 0 terminated c string. | 
|  | name = name.substr(0, name.find('\0')); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | QUIC_VLOG(2) << "interface name: " << name | 
|  | << ", index: " << interface_info->ifi_index; | 
|  |  | 
|  | if (name == interface_name_) { | 
|  | link_info_->index = interface_info->ifi_index; | 
|  | link_info_->type = interface_info->ifi_type; | 
|  | link_info_->hardware_address_length = hardware_address_length; | 
|  | if (hardware_address_length > 0) { | 
|  | memmove(&link_info_->hardware_address, hardware_address, | 
|  | hardware_address_length); | 
|  | } | 
|  | link_info_->broadcast_address_length = broadcast_address_length; | 
|  | if (broadcast_address_length > 0) { | 
|  | memmove(&link_info_->broadcast_address, broadcast_address, | 
|  | broadcast_address_length); | 
|  | } | 
|  | found_link_ = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool found_link() { return found_link_; } | 
|  |  | 
|  | private: | 
|  | const string interface_name_; | 
|  | Netlink::LinkInfo* const link_info_; | 
|  | bool found_link_ = false; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Netlink::GetLinkInfo(const string& interface_name, LinkInfo* link_info) { | 
|  | auto message = LinkMessage::New(RtnetlinkMessage::Operation::GET, | 
|  | NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, | 
|  | seq_, getpid(), nullptr); | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Pass the parser to the receive routine. It may be called multiple times | 
|  | // since there may be multiple reply packets each with multiple reply | 
|  | // messages. | 
|  | LinkInfoParser parser(interface_name, link_info); | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "recv failed."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return parser.found_link(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class LocalAddressParser : public NetlinkParserInterface { | 
|  | public: | 
|  | LocalAddressParser(int interface_index, | 
|  | uint8_t unwanted_flags, | 
|  | std::vector<Netlink::AddressInfo>* local_addresses, | 
|  | int* num_ipv6_nodad_dadfailed_addresses) | 
|  | : interface_index_(interface_index), | 
|  | unwanted_flags_(unwanted_flags), | 
|  | local_addresses_(local_addresses), | 
|  | num_ipv6_nodad_dadfailed_addresses_( | 
|  | num_ipv6_nodad_dadfailed_addresses) {} | 
|  |  | 
|  | void Run(struct nlmsghdr* netlink_message) override { | 
|  | // each nlmsg contains a header and multiple address attributes. | 
|  | if (netlink_message->nlmsg_type != RTM_NEWADDR) { | 
|  | QUIC_LOG(INFO) << "Unexpected nlmsg_type: " << netlink_message->nlmsg_type | 
|  | << " expected: " << RTM_NEWADDR; | 
|  | return; | 
|  | } | 
|  |  | 
|  | struct ifaddrmsg* interface_address = | 
|  | reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message)); | 
|  |  | 
|  | // Make sure this is for an address family we're interested in. | 
|  | if (interface_address->ifa_family != AF_INET && | 
|  | interface_address->ifa_family != AF_INET6) { | 
|  | QUIC_VLOG(2) << QuicStrCat("uninteresting ifa family: ", | 
|  | interface_address->ifa_family); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Keep track of addresses with both 'nodad' and 'dadfailed', this really | 
|  | // should't be possible and is likely a kernel bug. | 
|  | if (num_ipv6_nodad_dadfailed_addresses_ != nullptr && | 
|  | (interface_address->ifa_flags & IFA_F_NODAD) && | 
|  | (interface_address->ifa_flags & IFA_F_DADFAILED)) { | 
|  | ++(*num_ipv6_nodad_dadfailed_addresses_); | 
|  | } | 
|  |  | 
|  | uint8_t unwanted_flags = interface_address->ifa_flags & unwanted_flags_; | 
|  | if (unwanted_flags != 0) { | 
|  | QUIC_VLOG(2) << QuicStrCat("unwanted ifa flags: ", unwanted_flags); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // loop through the attributes | 
|  | struct rtattr* rta; | 
|  | int payload_length = IFA_PAYLOAD(netlink_message); | 
|  | Netlink::AddressInfo address_info; | 
|  | for (rta = IFA_RTA(interface_address); RTA_OK(rta, payload_length); | 
|  | rta = RTA_NEXT(rta, payload_length)) { | 
|  | // There's quite a lot of confusion in Linux over the use of IFA_LOCAL and | 
|  | // IFA_ADDRESS (source and destination address). For broadcast links, such | 
|  | // as Ethernet, they are identical (see <linux/if_addr.h>), but the kernel | 
|  | // sometimes uses only one or the other. We'll return both so that the | 
|  | // caller can decide which to use. | 
|  | if (rta->rta_type != IFA_LOCAL && rta->rta_type != IFA_ADDRESS) { | 
|  | QUIC_VLOG(2) << "Ignoring uninteresting rta_type: " << rta->rta_type; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | switch (interface_address->ifa_family) { | 
|  | case AF_INET: | 
|  | QUIC_FALLTHROUGH_INTENDED; | 
|  | case AF_INET6: | 
|  | // QuicIpAddress knows how to parse ip from raw bytes as long as they | 
|  | // are in network byte order. | 
|  | if (RTA_PAYLOAD(rta) == sizeof(struct in_addr) || | 
|  | RTA_PAYLOAD(rta) == sizeof(struct in6_addr)) { | 
|  | auto* raw_ip = reinterpret_cast<char*>(RTA_DATA(rta)); | 
|  | if (rta->rta_type == IFA_LOCAL) { | 
|  | address_info.local_address.FromPackedString(raw_ip, | 
|  | RTA_PAYLOAD(rta)); | 
|  | } else { | 
|  | address_info.interface_address.FromPackedString(raw_ip, | 
|  | RTA_PAYLOAD(rta)); | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: | 
|  | QUIC_LOG(ERROR) << QuicStrCat("Unknown address family: ", | 
|  | interface_address->ifa_family); | 
|  | } | 
|  | } | 
|  |  | 
|  | QUIC_VLOG(2) << "local_address: " << address_info.local_address.ToString() | 
|  | << " interface_address: " | 
|  | << address_info.interface_address.ToString() | 
|  | << " index: " << interface_address->ifa_index; | 
|  | if (interface_address->ifa_index != interface_index_) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | address_info.prefix_length = interface_address->ifa_prefixlen; | 
|  | address_info.scope = interface_address->ifa_scope; | 
|  | if (address_info.local_address.IsInitialized() || | 
|  | address_info.interface_address.IsInitialized()) { | 
|  | local_addresses_->push_back(address_info); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | const int interface_index_; | 
|  | const uint8_t unwanted_flags_; | 
|  | std::vector<Netlink::AddressInfo>* const local_addresses_; | 
|  | int* const num_ipv6_nodad_dadfailed_addresses_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Netlink::GetAddresses(int interface_index, | 
|  | uint8_t unwanted_flags, | 
|  | std::vector<AddressInfo>* addresses, | 
|  | int* num_ipv6_nodad_dadfailed_addresses) { | 
|  | // the message doesn't contain the index, we'll have to do the filtering while | 
|  | // parsing the reply. This is because NLM_F_MATCH, which only returns entries | 
|  | // that matches the request criteria, is not yet implemented (see man 3 | 
|  | // netlink). | 
|  | auto message = AddressMessage::New(RtnetlinkMessage::Operation::GET, | 
|  | NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, | 
|  | seq_, getpid(), nullptr); | 
|  |  | 
|  | // the send routine returns the socket to listen on. | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | addresses->clear(); | 
|  | if (num_ipv6_nodad_dadfailed_addresses != nullptr) { | 
|  | *num_ipv6_nodad_dadfailed_addresses = 0; | 
|  | } | 
|  |  | 
|  | LocalAddressParser parser(interface_index, unwanted_flags, addresses, | 
|  | num_ipv6_nodad_dadfailed_addresses); | 
|  | // Pass the parser to the receive routine. It may be called multiple times | 
|  | // since there may be multiple reply packets each with multiple reply | 
|  | // messages. | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "recv failed"; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class UnknownParser : public NetlinkParserInterface { | 
|  | public: | 
|  | void Run(struct nlmsghdr* netlink_message) override { | 
|  | QUIC_LOG(INFO) << "nlmsg reply type: " << netlink_message->nlmsg_type; | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Netlink::ChangeLocalAddress( | 
|  | uint32_t interface_index, | 
|  | Verb verb, | 
|  | const QuicIpAddress& address, | 
|  | uint8_t prefix_length, | 
|  | uint8_t ifa_flags, | 
|  | uint8_t ifa_scope, | 
|  | const std::vector<struct rtattr*>& additional_attributes) { | 
|  | if (verb == Verb::kReplace) { | 
|  | return false; | 
|  | } | 
|  | auto operation = verb == Verb::kAdd ? RtnetlinkMessage::Operation::NEW | 
|  | : RtnetlinkMessage::Operation::DEL; | 
|  | uint8_t address_family; | 
|  | if (address.address_family() == IpAddressFamily::IP_V4) { | 
|  | address_family = AF_INET; | 
|  | } else if (address.address_family() == IpAddressFamily::IP_V6) { | 
|  | address_family = AF_INET6; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | struct ifaddrmsg address_header = {address_family, prefix_length, ifa_flags, | 
|  | ifa_scope, interface_index}; | 
|  |  | 
|  | auto message = AddressMessage::New(operation, NLM_F_REQUEST | NLM_F_ACK, seq_, | 
|  | getpid(), &address_header); | 
|  |  | 
|  | for (const auto& attribute : additional_attributes) { | 
|  | if (attribute->rta_type == IFA_LOCAL) { | 
|  | continue; | 
|  | } | 
|  | message.AppendAttribute(attribute->rta_type, RTA_DATA(attribute), | 
|  | RTA_PAYLOAD(attribute)); | 
|  | } | 
|  |  | 
|  | message.AppendAttribute(IFA_LOCAL, address.ToPackedString().c_str(), | 
|  | address.ToPackedString().size()); | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | UnknownParser parser; | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "receive failed."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class RoutingRuleParser : public NetlinkParserInterface { | 
|  | public: | 
|  | explicit RoutingRuleParser(std::vector<Netlink::RoutingRule>* routing_rules) | 
|  | : routing_rules_(routing_rules) {} | 
|  |  | 
|  | void Run(struct nlmsghdr* netlink_message) override { | 
|  | if (netlink_message->nlmsg_type != RTM_NEWROUTE) { | 
|  | QUIC_LOG(WARNING) << QuicStrCat( | 
|  | "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, | 
|  | " expected: ", RTM_NEWROUTE); | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto* route = reinterpret_cast<struct rtmsg*>(NLMSG_DATA(netlink_message)); | 
|  | int payload_length = RTM_PAYLOAD(netlink_message); | 
|  |  | 
|  | if (route->rtm_family != AF_INET && route->rtm_family != AF_INET6) { | 
|  | QUIC_VLOG(2) << QuicStrCat("Uninteresting family: ", route->rtm_family); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Netlink::RoutingRule rule; | 
|  | rule.scope = route->rtm_scope; | 
|  | rule.table = route->rtm_table; | 
|  |  | 
|  | struct rtattr* rta; | 
|  | for (rta = RTM_RTA(route); RTA_OK(rta, payload_length); | 
|  | rta = RTA_NEXT(rta, payload_length)) { | 
|  | switch (rta->rta_type) { | 
|  | case RTA_TABLE: { | 
|  | rule.table = *reinterpret_cast<uint32_t*>(RTA_DATA(rta)); | 
|  | break; | 
|  | } | 
|  | case RTA_DST: { | 
|  | QuicIpAddress destination; | 
|  | destination.FromPackedString(reinterpret_cast<char*> RTA_DATA(rta), | 
|  | RTA_PAYLOAD(rta)); | 
|  | rule.destination_subnet = IpRange(destination, route->rtm_dst_len); | 
|  | break; | 
|  | } | 
|  | case RTA_PREFSRC: { | 
|  | QuicIpAddress preferred_source; | 
|  | rule.preferred_source.FromPackedString( | 
|  | reinterpret_cast<char*> RTA_DATA(rta), RTA_PAYLOAD(rta)); | 
|  | break; | 
|  | } | 
|  | case RTA_OIF: { | 
|  | rule.out_interface = *reinterpret_cast<int*>(RTA_DATA(rta)); | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | QUIC_VLOG(2) << QuicStrCat("Uninteresting attribute: ", | 
|  | rta->rta_type); | 
|  | } | 
|  | } | 
|  | } | 
|  | routing_rules_->push_back(rule); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::vector<Netlink::RoutingRule>* routing_rules_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Netlink::GetRouteInfo(std::vector<Netlink::RoutingRule>* routing_rules) { | 
|  | rtmsg route_message{}; | 
|  | // Only manipulate main routing table. | 
|  | route_message.rtm_table = RT_TABLE_MAIN; | 
|  |  | 
|  | auto message = RouteMessage::New(RtnetlinkMessage::Operation::GET, | 
|  | NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH, | 
|  | seq_, getpid(), &route_message); | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | RoutingRuleParser parser(routing_rules); | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "recv failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Netlink::ChangeRoute(Netlink::Verb verb, | 
|  | uint32_t table, | 
|  | const IpRange& destination_subnet, | 
|  | uint8_t scope, | 
|  | QuicIpAddress preferred_source, | 
|  | int32_t interface_index) { | 
|  | if (!destination_subnet.prefix().IsInitialized()) { | 
|  | return false; | 
|  | } | 
|  | if (destination_subnet.address_family() != IpAddressFamily::IP_V4 && | 
|  | destination_subnet.address_family() != IpAddressFamily::IP_V6) { | 
|  | return false; | 
|  | } | 
|  | if (preferred_source.IsInitialized() && | 
|  | preferred_source.address_family() != | 
|  | destination_subnet.address_family()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | RtnetlinkMessage::Operation operation; | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; | 
|  | switch (verb) { | 
|  | case Verb::kAdd: | 
|  | operation = RtnetlinkMessage::Operation::NEW; | 
|  | // Setting NLM_F_EXCL so that an existing entry for this subnet will fail | 
|  | // the request. NLM_F_CREATE is necessary to indicate this is trying to | 
|  | // create a new entry - simply having RTM_NEWROUTE is not enough even the | 
|  | // name suggests so. | 
|  | flags |= NLM_F_EXCL | NLM_F_CREATE; | 
|  | break; | 
|  | case Verb::kRemove: | 
|  | operation = RtnetlinkMessage::Operation::DEL; | 
|  | break; | 
|  | case Verb::kReplace: | 
|  | operation = RtnetlinkMessage::Operation::NEW; | 
|  | // Setting NLM_F_REPLACE to tell the kernel that existing entry for this | 
|  | // subnet should be replaced. | 
|  | flags |= NLM_F_REPLACE | NLM_F_CREATE; | 
|  | break; | 
|  | } | 
|  |  | 
|  | struct rtmsg route_message; | 
|  | memset(&route_message, 0, sizeof(route_message)); | 
|  | route_message.rtm_family = | 
|  | destination_subnet.address_family() == IpAddressFamily::IP_V4 ? AF_INET | 
|  | : AF_INET6; | 
|  | // rtm_dst_len and rtm_src_len are actually the subnet prefix lengths. Poor | 
|  | // naming. | 
|  | route_message.rtm_dst_len = destination_subnet.prefix_length(); | 
|  | // 0 means no source subnet for this rule. | 
|  | route_message.rtm_src_len = 0; | 
|  | // Only program the main table. Other tables are intended for the kernel to | 
|  | // manage. | 
|  | route_message.rtm_table = RT_TABLE_MAIN; | 
|  | // Use RTPROT_UNSPEC to match all the different protocol. Rules added by | 
|  | // kernel have RTPROT_KERNEL. Rules added by the root user have RTPROT_STATIC | 
|  | // instead. | 
|  | route_message.rtm_protocol = | 
|  | verb == Verb::kRemove ? RTPROT_UNSPEC : RTPROT_STATIC; | 
|  | route_message.rtm_scope = scope; | 
|  | // Only add unicast routing rule. | 
|  | route_message.rtm_type = RTN_UNICAST; | 
|  | auto message = | 
|  | RouteMessage::New(operation, flags, seq_, getpid(), &route_message); | 
|  |  | 
|  | message.AppendAttribute(RTA_TABLE, &table, sizeof(table)); | 
|  |  | 
|  | // RTA_OIF is the target interface for this rule. | 
|  | message.AppendAttribute(RTA_OIF, &interface_index, sizeof(interface_index)); | 
|  | // The actual destination subnet must be truncated of all the tailing zeros. | 
|  | message.AppendAttribute( | 
|  | RTA_DST, | 
|  | reinterpret_cast<const void*>( | 
|  | destination_subnet.prefix().ToPackedString().c_str()), | 
|  | destination_subnet.prefix().ToPackedString().size()); | 
|  | // This is the source address to use in the IP packet should this routing rule | 
|  | // is used. | 
|  | if (preferred_source.IsInitialized()) { | 
|  | message.AppendAttribute(RTA_PREFSRC, | 
|  | reinterpret_cast<const void*>( | 
|  | preferred_source.ToPackedString().c_str()), | 
|  | preferred_source.ToPackedString().size()); | 
|  | } | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | UnknownParser parser; | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "receive failed."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class IpRuleParser : public NetlinkParserInterface { | 
|  | public: | 
|  | explicit IpRuleParser(std::vector<Netlink::IpRule>* ip_rules) | 
|  | : ip_rules_(ip_rules) {} | 
|  |  | 
|  | void Run(struct nlmsghdr* netlink_message) override { | 
|  | if (netlink_message->nlmsg_type != RTM_NEWRULE) { | 
|  | QUIC_LOG(WARNING) << QuicStrCat( | 
|  | "Unexpected nlmsg_type: ", netlink_message->nlmsg_type, | 
|  | " expected: ", RTM_NEWRULE); | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto* rule = reinterpret_cast<rtmsg*>(NLMSG_DATA(netlink_message)); | 
|  | int payload_length = RTM_PAYLOAD(netlink_message); | 
|  |  | 
|  | if (rule->rtm_family != AF_INET6) { | 
|  | QUIC_LOG(ERROR) << QuicStrCat("Unexpected family: ", rule->rtm_family); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Netlink::IpRule ip_rule; | 
|  | ip_rule.table = rule->rtm_table; | 
|  |  | 
|  | struct rtattr* rta; | 
|  | for (rta = RTM_RTA(rule); RTA_OK(rta, payload_length); | 
|  | rta = RTA_NEXT(rta, payload_length)) { | 
|  | switch (rta->rta_type) { | 
|  | case RTA_TABLE: { | 
|  | ip_rule.table = *reinterpret_cast<uint32_t*>(RTA_DATA(rta)); | 
|  | break; | 
|  | } | 
|  | case RTA_SRC: { | 
|  | QuicIpAddress src_addr; | 
|  | src_addr.FromPackedString(reinterpret_cast<char*>(RTA_DATA(rta)), | 
|  | RTA_PAYLOAD(rta)); | 
|  | IpRange src_range(src_addr, rule->rtm_src_len); | 
|  | ip_rule.source_range = src_range; | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | QUIC_VLOG(2) << QuicStrCat("Uninteresting attribute: ", | 
|  | rta->rta_type); | 
|  | } | 
|  | } | 
|  | } | 
|  | ip_rules_->emplace_back(ip_rule); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::vector<Netlink::IpRule>* ip_rules_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Netlink::GetRuleInfo(std::vector<Netlink::IpRule>* ip_rules) { | 
|  | rtmsg rule_message{}; | 
|  | rule_message.rtm_family = AF_INET6; | 
|  |  | 
|  | auto message = RuleMessage::New(RtnetlinkMessage::Operation::GET, | 
|  | NLM_F_REQUEST | NLM_F_DUMP, seq_, getpid(), | 
|  | &rule_message); | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | IpRuleParser parser(ip_rules); | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "receive failed."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Netlink::ChangeRule(Verb verb, uint32_t table, IpRange source_range) { | 
|  | RtnetlinkMessage::Operation operation; | 
|  | uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; | 
|  |  | 
|  | rtmsg rule_message{}; | 
|  | rule_message.rtm_family = AF_INET6; | 
|  | rule_message.rtm_protocol = RTPROT_STATIC; | 
|  | rule_message.rtm_scope = RT_SCOPE_UNIVERSE; | 
|  | rule_message.rtm_table = RT_TABLE_UNSPEC; | 
|  |  | 
|  | rule_message.rtm_flags |= FIB_RULE_FIND_SADDR; | 
|  |  | 
|  | switch (verb) { | 
|  | case Verb::kAdd: | 
|  | if (!source_range.IsInitialized()) { | 
|  | QUIC_LOG(ERROR) << "Source range must be initialized."; | 
|  | return false; | 
|  | } | 
|  | operation = RtnetlinkMessage::Operation::NEW; | 
|  | flags |= NLM_F_EXCL | NLM_F_CREATE; | 
|  | rule_message.rtm_type = FRA_DST; | 
|  | rule_message.rtm_src_len = source_range.prefix_length(); | 
|  | break; | 
|  | case Verb::kRemove: | 
|  | operation = RtnetlinkMessage::Operation::DEL; | 
|  | break; | 
|  | case Verb::kReplace: | 
|  | QUIC_LOG(ERROR) << "Unsupported verb: kReplace"; | 
|  | return false; | 
|  | } | 
|  | auto message = | 
|  | RuleMessage::New(operation, flags, seq_, getpid(), &rule_message); | 
|  |  | 
|  | message.AppendAttribute(RTA_TABLE, &table, sizeof(table)); | 
|  |  | 
|  | if (source_range.IsInitialized()) { | 
|  | std::string packed_src = source_range.prefix().ToPackedString(); | 
|  | message.AppendAttribute(RTA_SRC, | 
|  | reinterpret_cast<const void*>(packed_src.c_str()), | 
|  | packed_src.size()); | 
|  | } | 
|  |  | 
|  | if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { | 
|  | QUIC_LOG(ERROR) << "send failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | UnknownParser parser; | 
|  | if (!Recv(seq_++, &parser)) { | 
|  | QUIC_LOG(ERROR) << "receive failed."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Netlink::Send(struct iovec* iov, size_t iovlen) { | 
|  | if (!OpenSocket()) { | 
|  | QUIC_LOG(ERROR) << "can't open socket"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // an address for communicating with the kernel netlink code | 
|  | sockaddr_nl netlink_address; | 
|  | memset(&netlink_address, 0, sizeof(netlink_address)); | 
|  | netlink_address.nl_family = AF_NETLINK; | 
|  | netlink_address.nl_pid = 0;     // destination is kernel | 
|  | netlink_address.nl_groups = 0;  // no multicast | 
|  |  | 
|  | struct msghdr msg = { | 
|  | &netlink_address, sizeof(netlink_address), iov, iovlen, nullptr, 0, 0}; | 
|  |  | 
|  | if (kernel_->sendmsg(socket_fd_, &msg, 0) < 0) { | 
|  | QUIC_LOG(ERROR) << "sendmsg failed"; | 
|  | CloseSocket(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Netlink::Recv(uint32_t seq, NetlinkParserInterface* parser) { | 
|  | sockaddr_nl netlink_address; | 
|  |  | 
|  | // replies can span multiple packets | 
|  | for (;;) { | 
|  | socklen_t address_length = sizeof(netlink_address); | 
|  |  | 
|  | // First, call recvfrom with buffer size of 0 and MSG_PEEK | MSG_TRUNC set | 
|  | // so that we know the size of the incoming packet before actually receiving | 
|  | // it. | 
|  | int next_packet_size = kernel_->recvfrom( | 
|  | socket_fd_, recvbuf_.get(), /* len = */ 0, MSG_PEEK | MSG_TRUNC, | 
|  | reinterpret_cast<struct sockaddr*>(&netlink_address), &address_length); | 
|  | if (next_packet_size < 0) { | 
|  | QUIC_LOG(ERROR) | 
|  | << "error recvfrom with MSG_PEEK | MSG_TRUNC to get packet length."; | 
|  | CloseSocket(); | 
|  | return false; | 
|  | } | 
|  | QUIC_VLOG(3) << "netlink packet size: " << next_packet_size; | 
|  | if (next_packet_size > recvbuf_length_) { | 
|  | QUIC_VLOG(2) << "resizing recvbuf to " << next_packet_size; | 
|  | ResetRecvBuf(next_packet_size); | 
|  | } | 
|  |  | 
|  | // Get the packet for real. | 
|  | memset(recvbuf_.get(), 0, recvbuf_length_); | 
|  | int len = kernel_->recvfrom( | 
|  | socket_fd_, recvbuf_.get(), recvbuf_length_, /* flags = */ 0, | 
|  | reinterpret_cast<struct sockaddr*>(&netlink_address), &address_length); | 
|  | QUIC_VLOG(3) << "recvfrom returned: " << len; | 
|  | if (len < 0) { | 
|  | QUIC_LOG(INFO) << "can't receive netlink packet"; | 
|  | CloseSocket(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // there may be multiple nlmsg's in each reply packet | 
|  | struct nlmsghdr* netlink_message; | 
|  | for (netlink_message = reinterpret_cast<struct nlmsghdr*>(recvbuf_.get()); | 
|  | NLMSG_OK(netlink_message, len); | 
|  | netlink_message = NLMSG_NEXT(netlink_message, len)) { | 
|  | QUIC_VLOG(3) << "netlink_message->nlmsg_type = " | 
|  | << netlink_message->nlmsg_type; | 
|  | // make sure this is to us | 
|  | if (netlink_message->nlmsg_seq != seq) { | 
|  | QUIC_LOG(INFO) << "netlink_message not meant for us." | 
|  | << " seq: " << seq | 
|  | << " nlmsg_seq: " << netlink_message->nlmsg_seq; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // done with this whole reply (not just this particular packet) | 
|  | if (netlink_message->nlmsg_type == NLMSG_DONE) { | 
|  | return true; | 
|  | } | 
|  | if (netlink_message->nlmsg_type == NLMSG_ERROR) { | 
|  | struct nlmsgerr* err = | 
|  | reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(netlink_message)); | 
|  | if (netlink_message->nlmsg_len < | 
|  | NLMSG_LENGTH(sizeof(struct nlmsgerr))) { | 
|  | QUIC_LOG(INFO) << "netlink_message ERROR truncated"; | 
|  | } else { | 
|  | // an ACK | 
|  | if (err->error == 0) { | 
|  | QUIC_VLOG(3) << "Netlink sent an ACK"; | 
|  | return true; | 
|  | } | 
|  | QUIC_LOG(INFO) << "netlink_message ERROR: " << err->error; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | parser->Run(netlink_message); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace quic |