| // 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_message.h" |
| |
| #include <net/if.h> // NB: order matters; this conflicts with <linux/if.h> |
| #include <arpa/inet.h> // NOLINT(build/include_alpha) |
| #include <linux/fib_rules.h> |
| #include <linux/if_addr.h> |
| #include <linux/if_arp.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| |
| #include <array> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <base/notreached.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/strings/string_number_conversions.h> |
| |
| #include "net-base/byte_utils.h" |
| #include "net-base/http_url.h" |
| |
| namespace net_base { |
| namespace { |
| |
| // Defined in the kernel at include/net/ndisc.h and not exposed in user space |
| // headers. Neighbor Discovery user option type definition. |
| #define ND_OPT_RDNSS 25 /* RFC 5006 */ |
| #define ND_OPT_DNSSL 31 /* RFC 6106 */ |
| #define ND_OPT_CAPTIVE_PORTAL 37 /* RFC 8910 */ |
| |
| using flag_info_t = std::pair<uint32_t, const char*>; |
| |
| // Helper for pretty printing flags |
| template <std::size_t Dim> |
| std::string PrintFlags(uint32_t flags, |
| const std::array<flag_info_t, Dim>& flags_info, |
| const std::string& separator = " | ") { |
| std::string str = ""; |
| if (flags == 0) { |
| return str; |
| } |
| std::string sep = ""; |
| for (size_t i = 0; i < Dim; i++) { |
| if ((flags & flags_info[i].first) == 0) { |
| continue; |
| } |
| str += sep; |
| str += flags_info[i].second; |
| sep = separator; |
| } |
| return str; |
| } |
| |
| // Flag names for Address events (ifa_flags field of struct ifaddrmsg). Defined |
| // in uapi/linux/if_addr.h |
| constexpr std::array<flag_info_t, 12> kIfaFlags{{ |
| {IFA_F_TEMPORARY, "TEMPORARY"}, |
| {IFA_F_NODAD, "NODAD"}, |
| {IFA_F_OPTIMISTIC, "OPTIMISTIC"}, |
| {IFA_F_DADFAILED, "DADFAILED"}, |
| {IFA_F_HOMEADDRESS, "HOMEADDRESS"}, |
| {IFA_F_DEPRECATED, "DEPRECATED"}, |
| {IFA_F_TENTATIVE, "TENTATIVE"}, |
| {IFA_F_PERMANENT, "PERMANENT"}, |
| {IFA_F_MANAGETEMPADDR, "MANAGETEMPADDR"}, |
| {IFA_F_NOPREFIXROUTE, "NOPREFIXROUTE"}, |
| {IFA_F_MCAUTOJOIN, "MCAUTOJOIN"}, |
| {IFA_F_STABLE_PRIVACY, "STABLE_PRIVACY"}, |
| }}; |
| |
| // Flag names for Link events (ifi_flags field of struct ifinfomsg). Defined in |
| // uapi/linux/if.h |
| constexpr std::array<flag_info_t, 19> kNetDeviceFlags = {{ |
| {IFF_ALLMULTI, "ALLMULTI"}, |
| {IFF_AUTOMEDIA, "AUTOMEDIA"}, |
| {IFF_BROADCAST, "BROADCAST"}, |
| {IFF_DEBUG, "DEBUG"}, |
| {IFF_DORMANT, "DORMANT"}, |
| {IFF_DYNAMIC, "DYNAMIC"}, |
| {IFF_ECHO, "ECHO"}, |
| {IFF_LOOPBACK, "LOOPBACK"}, |
| {IFF_LOWER_UP, "LOWER_UP"}, |
| {IFF_MASTER, "MASTER"}, |
| {IFF_MULTICAST, "MULTICAST"}, |
| {IFF_NOARP, "NOARP"}, |
| {IFF_NOTRAILERS, "NOTRAILERS"}, |
| {IFF_POINTOPOINT, "POINTOPOINT"}, |
| {IFF_PORTSEL, "PORTSEL"}, |
| {IFF_PROMISC, "PROMISC"}, |
| {IFF_RUNNING, "RUNNING"}, |
| {IFF_SLAVE, "SLAVE"}, |
| {IFF_UP, "UP"}, |
| }}; |
| |
| // Returns the name associated with the give |ifi_type| corresponding to the |
| // ifi_type field of a struct ifinfomsg LINK message. The possible type values |
| // are defined in uapi/linux/if_arp.h. |
| std::string GetNetDeviceTypeName(unsigned int ifi_type) { |
| switch (ifi_type) { |
| case ARPHRD_NETROM: |
| return "NETROM"; |
| case ARPHRD_ETHER: |
| return "ETHER"; |
| case ARPHRD_EETHER: |
| return "EETHER"; |
| case ARPHRD_AX25: |
| return "AX25"; |
| case ARPHRD_PRONET: |
| return "PRONET"; |
| case ARPHRD_CHAOS: |
| return "CHAOS"; |
| case ARPHRD_IEEE802: |
| return "IEEE802"; |
| case ARPHRD_ARCNET: |
| return "ARCNET"; |
| case ARPHRD_APPLETLK: |
| return "APPLETLK"; |
| case ARPHRD_DLCI: |
| return "DLCI"; |
| case ARPHRD_ATM: |
| return "ATM"; |
| case ARPHRD_METRICOM: |
| return "METRICOM"; |
| case ARPHRD_IEEE1394: |
| return "IEEE1394"; |
| case ARPHRD_EUI64: |
| return "EUI64"; |
| case ARPHRD_INFINIBAND: |
| return "INFINIBAND"; |
| case ARPHRD_SLIP: |
| return "SLIP"; |
| case ARPHRD_CSLIP: |
| return "CSLIP"; |
| case ARPHRD_SLIP6: |
| return "SLIP6"; |
| case ARPHRD_CSLIP6: |
| return "CSLIP6"; |
| case ARPHRD_RSRVD: |
| return "RSRVD"; |
| case ARPHRD_ADAPT: |
| return "ADAPT"; |
| case ARPHRD_ROSE: |
| return "ROSE"; |
| case ARPHRD_X25: |
| return "X25"; |
| case ARPHRD_HWX25: |
| return "HWX25"; |
| case ARPHRD_CAN: |
| return "CAN"; |
| case ARPHRD_PPP: |
| return "PPP"; |
| case ARPHRD_CISCO: |
| return "CISCO"; // also ARPHRD_HDLC |
| case ARPHRD_LAPB: |
| return "LAPB"; |
| case ARPHRD_DDCMP: |
| return "DDCMP"; |
| case ARPHRD_RAWHDLC: |
| return "RAWHDLC"; |
| case ARPHRD_RAWIP: |
| return "RAWIP"; |
| case ARPHRD_TUNNEL: |
| return "TUNNEL"; |
| case ARPHRD_TUNNEL6: |
| return "TUNNEL6"; |
| case ARPHRD_FRAD: |
| return "FRAD"; |
| case ARPHRD_SKIP: |
| return "SKIP"; |
| case ARPHRD_LOOPBACK: |
| return "LOOPBACK"; |
| case ARPHRD_LOCALTLK: |
| return "LOCALTLK"; |
| case ARPHRD_FDDI: |
| return "FDDI"; |
| case ARPHRD_BIF: |
| return "BIF"; |
| case ARPHRD_SIT: |
| return "SIT"; |
| case ARPHRD_IPDDP: |
| return "IPDDP"; |
| case ARPHRD_IPGRE: |
| return "IPGRE"; |
| case ARPHRD_PIMREG: |
| return "PIMREG"; |
| case ARPHRD_HIPPI: |
| return "HIPPI"; |
| case ARPHRD_ASH: |
| return "ASH"; |
| case ARPHRD_ECONET: |
| return "ECONET"; |
| case ARPHRD_IRDA: |
| return "IRDA"; |
| case ARPHRD_FCPP: |
| return "FCPP"; |
| case ARPHRD_FCAL: |
| return "FCAL"; |
| case ARPHRD_FCPL: |
| return "FCPL"; |
| case ARPHRD_FCFABRIC: |
| return "FCFABRIC"; |
| case ARPHRD_IEEE802_TR: |
| return "IEEE802_TR"; |
| case ARPHRD_IEEE80211: |
| return "IEEE80211"; |
| case ARPHRD_IEEE80211_PRISM: |
| return "IEEE80211_PRISM"; |
| case ARPHRD_IEEE80211_RADIOTAP: |
| return "IEEE80211_RADIOTAP"; |
| case ARPHRD_IEEE802154: |
| return "IEEE802154 "; |
| case ARPHRD_IEEE802154_MONITOR: |
| return "IEEE802154_MONITOR"; |
| case ARPHRD_PHONET: |
| return "PHONET"; |
| case ARPHRD_PHONET_PIPE: |
| return "PHONET_PIPE"; |
| case ARPHRD_CAIF: |
| return "CAIF"; |
| case ARPHRD_IP6GRE: |
| return "IP6GRE"; |
| case ARPHRD_NETLINK: |
| return "NETLINK"; |
| case ARPHRD_6LOWPAN: |
| return "6LOWPAN"; |
| case ARPHRD_VSOCKMON: |
| return "VSOCKMON"; |
| case ARPHRD_VOID: |
| return "VOID"; |
| case ARPHRD_NONE: |
| return "NONE"; |
| default: |
| return std::to_string(ifi_type); |
| } |
| } |
| |
| // Returns the name associated with the give |rtm_type| corresponding to the |
| // rtm_type field of a struct rtmsg ROUTE message. The possible type values |
| // are defined in uapi/linux/rtnetlink.h. |
| std::string GetRouteTypeName(uint8_t rtm_type) { |
| switch (rtm_type) { |
| case RTN_UNSPEC: |
| return "UNSPEC"; |
| case RTN_UNICAST: |
| return "UNICAST"; |
| case RTN_LOCAL: |
| return "LOCAL"; |
| case RTN_BROADCAST: |
| return "BROADCAST"; |
| case RTN_ANYCAST: |
| return "ANYCAST"; |
| case RTN_MULTICAST: |
| return "MULTICAST"; |
| case RTN_BLACKHOLE: |
| return "BLACKHOLE"; |
| case RTN_UNREACHABLE: |
| return "UNREACHABLE"; |
| case RTN_PROHIBIT: |
| return "PROHIBIT"; |
| case RTN_THROW: |
| return "THROW"; |
| case RTN_NAT: |
| return "NAT"; |
| case RTN_XRESOLVE: |
| return "XRESOLVE"; |
| default: |
| return std::to_string(rtm_type); |
| } |
| } |
| |
| // Helper function to return route protocol names defined by the kernel. |
| // User reserved protocol values are returned as decimal numbers. |
| // Route protocols. Defined in uapi/linux/rtnetlink.h |
| std::string GetRouteProtocol(uint8_t protocol) { |
| switch (protocol) { |
| case RTPROT_UNSPEC: |
| return "UNSPEC"; |
| case RTPROT_REDIRECT: |
| return "REDIRECT"; |
| case RTPROT_KERNEL: |
| return "KERNEL"; |
| case RTPROT_BOOT: |
| return "BOOT"; |
| case RTPROT_STATIC: |
| return "STATIC"; |
| case RTPROT_GATED: |
| return "GATED"; |
| case RTPROT_RA: |
| return "RA"; |
| case RTPROT_MRT: |
| return "MRT"; |
| case RTPROT_ZEBRA: |
| return "ZEBRA"; |
| case RTPROT_BIRD: |
| return "BIRD"; |
| case RTPROT_DNROUTED: |
| return "DNROUTED"; |
| case RTPROT_XORP: |
| return "XORP"; |
| case RTPROT_NTK: |
| return "NTK"; |
| case RTPROT_DHCP: |
| return "DHCP"; |
| case RTPROT_MROUTED: |
| return "MROUTED"; |
| case RTPROT_BABEL: |
| return "BABEL"; |
| // The following protocols are not defined on Linux 4.14 |
| case 186 /* RTPROT_BGP */: |
| return "BGP"; |
| case 187 /* RTPROT_ISIS */: |
| return "ISIS"; |
| case 188 /* RTPROT_OSPF */: |
| return "OSPF"; |
| case 189 /* RTPROT_RIP */: |
| return "RIP"; |
| case 192 /* RTPROT_EIGRP */: |
| return "EIGRP"; |
| default: |
| return std::to_string(protocol); |
| } |
| } |
| |
| // Returns the name associated with the given |rule_rtm_type| routing rule |
| // action type corresponding to the rtm_type field of a struct rtmsg message. |
| // The possible rule action values are defined in uapi/linux/fib_rules.h. The |
| // struct fib_rule_hdr in uapi/linux/fib_rules.h such that it aligns with the |
| // |rtm_type| field of struct rtmsg defined in uapi/linux/rtnetlink.h. |
| std::string GetRuleActionName(uint16_t rule_rtm_type) { |
| switch (rule_rtm_type) { |
| case FR_ACT_UNSPEC: |
| return "UNSPEC"; |
| case FR_ACT_TO_TBL: |
| return "TO_TBL"; |
| case FR_ACT_GOTO: |
| return "GOTO"; |
| case FR_ACT_NOP: |
| return "NOP"; |
| case FR_ACT_RES3: |
| return "RES3"; |
| case FR_ACT_RES4: |
| return "RES4"; |
| case FR_ACT_BLACKHOLE: |
| return "BLACKHOLE"; |
| case FR_ACT_UNREACHABLE: |
| return "UNREACHABLE"; |
| case FR_ACT_PROHIBIT: |
| return "PROHIBIT"; |
| default: |
| return std::to_string(rule_rtm_type); |
| } |
| } |
| |
| std::unique_ptr<RTNLAttrMap> ParseAttrs(const struct rtattr* data, |
| size_t attr_len) { |
| const auto* attr_data = reinterpret_cast<const char*>(data); |
| if (attr_len > INT_MAX) { |
| LOG(ERROR) << "Buffer length " << attr_len << " is over INT_MAX"; |
| return nullptr; |
| } |
| int len = static_cast<int>(attr_len); |
| |
| RTNLAttrMap attrs; |
| while (data && RTA_OK(data, len)) { |
| attrs[data->rta_type] = { |
| reinterpret_cast<uint8_t*>(RTA_DATA(data)), |
| reinterpret_cast<uint8_t*>(RTA_DATA(data)) + RTA_PAYLOAD(data)}; |
| // Note: RTA_NEXT() performs subtraction on 'len'. It's important that |
| // 'len' is a signed integer, so underflow works properly. |
| data = RTA_NEXT(data, len); |
| } |
| |
| if (len) { |
| LOG(ERROR) << "Error parsing RTNL attributes <" |
| << base::HexEncode(attr_data, attr_len) |
| << ">, trailing length: " << len; |
| return nullptr; |
| } |
| |
| return std::make_unique<RTNLAttrMap>(attrs); |
| } |
| |
| // Returns the interface name for the device with interface index |ifindex|, or |
| // returns an empty string if it fails to find the interface. |
| std::string IndexToName(unsigned int ifindex) { |
| char buf[IFNAMSIZ] = {}; |
| if_indextoname(ifindex, buf); |
| return std::string(buf); |
| } |
| |
| } // namespace |
| |
| struct RTNLHeader { |
| RTNLHeader() { memset(this, 0, sizeof(*this)); } |
| struct nlmsghdr hdr; |
| union { |
| struct ifinfomsg ifi; |
| struct ifaddrmsg ifa; |
| struct rtmsg rtm; |
| struct nduseroptmsg nd_user_opt; |
| struct ndmsg ndm; |
| }; |
| }; |
| |
| // Neighbor Discovery user option header definition. |
| struct NDUserOptionHeader { |
| uint8_t type; |
| uint8_t length; |
| } __attribute__((__packed__)); |
| |
| std::string RTNLMessage::NeighborStatus::ToString() const { |
| return base::StringPrintf("NeighborStatus state %d flags %X type %d", state, |
| flags, type); |
| } |
| |
| // |lifetime| is unsigned. Printing as signed so that infinity (0xfffffff) get |
| // printed as -1. Same below. |
| std::string RTNLMessage::RdnssOption::ToString() const { |
| return base::StringPrintf("RdnssOption lifetime %d", lifetime); |
| } |
| |
| std::string RTNLMessage::DnsslOption::ToString() const { |
| return base::StringPrintf("DnsslOption lifetime %d", lifetime); |
| } |
| |
| std::string RTNLMessage::NdUserOption::ToString() const { |
| return base::StringPrintf("NdUserOption type %u", type); |
| } |
| |
| // static |
| std::vector<uint8_t> RTNLMessage::PackAttrs(const RTNLAttrMap& attrs) { |
| std::vector<uint8_t> attributes; |
| |
| for (const auto& pair : attrs) { |
| size_t len = RTA_LENGTH(pair.second.size()); |
| struct rtattr rt_attr = { |
| // Linter discourages 'unsigned short', but 'unsigned short' is used in |
| // the UAPI. |
| static_cast<unsigned short>(len), // NOLINT(runtime/int) |
| pair.first, |
| }; |
| std::vector<uint8_t> header = |
| net_base::byte_utils::ToBytes<struct rtattr>(rt_attr); |
| header.resize(RTA_ALIGN(header.size()), 0); |
| attributes.insert(attributes.end(), header.begin(), header.end()); |
| |
| std::vector<uint8_t> data = pair.second; |
| data.resize(RTA_ALIGN(data.size()), 0); |
| attributes.insert(attributes.end(), data.begin(), data.end()); |
| } |
| |
| return attributes; |
| } |
| |
| RTNLMessage::RTNLMessage(Type type, |
| Mode mode, |
| uint16_t flags, |
| uint32_t seq, |
| uint32_t pid, |
| int32_t interface_index, |
| sa_family_t family) |
| : type_(type), |
| mode_(mode), |
| flags_(flags), |
| seq_(seq), |
| pid_(pid), |
| interface_index_(interface_index), |
| family_(family) {} |
| |
| // static |
| std::unique_ptr<RTNLMessage> RTNLMessage::Decode( |
| base::span<const uint8_t> data) { |
| // Parse the nlmsghdr, and trim the data to the size |nlmsg_len|. |
| if (data.size() < sizeof(struct nlmsghdr)) { |
| return nullptr; |
| } |
| const struct nlmsghdr* header = |
| reinterpret_cast<const struct nlmsghdr*>(data.data()); |
| if (data.size() < header->nlmsg_len) { |
| return nullptr; |
| } |
| data = data.subspan(0, header->nlmsg_len); |
| |
| // Split the remaining data after the header. |
| if (data.size() < NLMSG_HDRLEN) { |
| return nullptr; |
| } |
| const base::span<const uint8_t> payload = data.subspan(NLMSG_HDRLEN); |
| |
| Mode mode = kModeUnknown; |
| switch (header->nlmsg_type) { |
| case RTM_NEWLINK: |
| case RTM_NEWADDR: |
| case RTM_NEWROUTE: |
| case RTM_NEWRULE: |
| case RTM_NEWNDUSEROPT: |
| case RTM_NEWNEIGH: |
| mode = kModeAdd; |
| break; |
| |
| case RTM_DELLINK: |
| case RTM_DELADDR: |
| case RTM_DELROUTE: |
| case RTM_DELRULE: |
| case RTM_DELNEIGH: |
| mode = kModeDelete; |
| break; |
| |
| default: |
| return nullptr; |
| } |
| |
| rtattr* attr_data = nullptr; |
| size_t attr_length = 0; |
| std::unique_ptr<RTNLMessage> msg; |
| switch (header->nlmsg_type) { |
| case RTM_NEWLINK: |
| case RTM_DELLINK: |
| attr_data = IFLA_RTA(NLMSG_DATA(header)); |
| attr_length = IFLA_PAYLOAD(header); |
| msg = DecodeLink(mode, payload); |
| break; |
| |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| attr_data = IFA_RTA(NLMSG_DATA(header)); |
| attr_length = IFA_PAYLOAD(header); |
| msg = DecodeAddress(mode, payload); |
| break; |
| |
| case RTM_NEWROUTE: |
| case RTM_DELROUTE: |
| attr_data = RTM_RTA(NLMSG_DATA(header)); |
| attr_length = RTM_PAYLOAD(header); |
| msg = DecodeRoute(mode, payload); |
| break; |
| |
| case RTM_NEWRULE: |
| case RTM_DELRULE: |
| attr_data = RTM_RTA(NLMSG_DATA(header)); |
| attr_length = RTM_PAYLOAD(header); |
| msg = DecodeRule(mode, payload); |
| break; |
| |
| case RTM_NEWNDUSEROPT: |
| msg = DecodeNdUserOption(mode, payload); |
| break; |
| |
| case RTM_NEWNEIGH: |
| case RTM_DELNEIGH: |
| attr_data = RTM_RTA(NLMSG_DATA(header)); |
| attr_length = RTM_PAYLOAD(header); |
| msg = DecodeNeighbor(mode, payload); |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| if (!msg) { |
| return nullptr; |
| } |
| |
| msg->flags_ = header->nlmsg_flags; |
| msg->seq_ = header->nlmsg_seq; |
| msg->pid_ = header->nlmsg_pid; |
| |
| const std::unique_ptr<RTNLAttrMap> attrs = ParseAttrs(attr_data, attr_length); |
| if (!attrs) { |
| return nullptr; |
| } |
| for (const auto& pair : *attrs) { |
| msg->SetAttribute(pair.first, pair.second); |
| } |
| return msg; |
| } |
| |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeLink( |
| Mode mode, base::span<const uint8_t> payload) { |
| // Parse ifinfomsg struct. |
| if (payload.size() < NLMSG_ALIGN(sizeof(struct ifinfomsg))) { |
| return nullptr; |
| } |
| const struct ifinfomsg* ifi = |
| reinterpret_cast<const struct ifinfomsg*>(payload.data()); |
| payload = payload.subspan(NLMSG_ALIGN(sizeof(struct ifinfomsg))); |
| |
| // Parse the attributes. |
| // Note: |payload.data()| here is equivalent to IFLA_RTA() with the original |
| // payload. |
| std::unique_ptr<RTNLAttrMap> attrs = ParseAttrs( |
| reinterpret_cast<const rtattr*>(payload.data()), payload.size()); |
| if (!attrs) { |
| return nullptr; |
| } |
| |
| std::optional<std::string> kind_option; |
| if (base::Contains(*attrs, IFLA_LINKINFO)) { |
| auto& bytes = attrs->find(IFLA_LINKINFO)->second; |
| struct rtattr* link_data = reinterpret_cast<struct rtattr*>(bytes.data()); |
| size_t link_len = bytes.size(); |
| std::unique_ptr<RTNLAttrMap> linkinfo = ParseAttrs(link_data, link_len); |
| |
| if (linkinfo && base::Contains(*linkinfo, IFLA_INFO_KIND)) { |
| const auto& kind_bytes = linkinfo->find(IFLA_INFO_KIND)->second; |
| const auto kind_string = |
| net_base::byte_utils::StringFromCStringBytes(kind_bytes); |
| if (base::IsStringASCII(kind_string)) { |
| kind_option = kind_string; |
| } else { |
| LOG(ERROR) << base::StringPrintf( |
| "Invalid kind <%s>, interface index %d", |
| base::HexEncode(kind_bytes).c_str(), ifi->ifi_index); |
| } |
| } |
| } |
| |
| auto msg = std::make_unique<RTNLMessage>(kTypeLink, mode, 0, 0, 0, |
| ifi->ifi_index, ifi->ifi_family); |
| msg->set_link_status( |
| LinkStatus(ifi->ifi_type, ifi->ifi_flags, ifi->ifi_change, kind_option)); |
| return msg; |
| } |
| |
| // static |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeAddress( |
| Mode mode, base::span<const uint8_t> payload) { |
| if (payload.size() < sizeof(struct ifaddrmsg)) { |
| return nullptr; |
| } |
| const struct ifaddrmsg* ifa = |
| reinterpret_cast<const struct ifaddrmsg*>(payload.data()); |
| |
| auto msg = std::make_unique<RTNLMessage>(kTypeAddress, mode, 0, 0, 0, |
| ifa->ifa_index, ifa->ifa_family); |
| msg->set_address_status( |
| AddressStatus(ifa->ifa_prefixlen, ifa->ifa_flags, ifa->ifa_scope)); |
| return msg; |
| } |
| |
| // static |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeRoute( |
| Mode mode, base::span<const uint8_t> payload) { |
| if (payload.size() < sizeof(struct rtmsg)) { |
| return nullptr; |
| } |
| const struct rtmsg* rtm = |
| reinterpret_cast<const struct rtmsg*>(payload.data()); |
| |
| auto msg = std::make_unique<RTNLMessage>(kTypeRoute, mode, 0, 0, 0, 0, |
| rtm->rtm_family); |
| msg->set_route_status(RouteStatus( |
| rtm->rtm_dst_len, rtm->rtm_src_len, rtm->rtm_table, rtm->rtm_protocol, |
| rtm->rtm_scope, rtm->rtm_type, rtm->rtm_flags)); |
| return msg; |
| } |
| |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeRule( |
| Mode mode, base::span<const uint8_t> payload) { |
| if (payload.size() < sizeof(struct rtmsg)) { |
| return nullptr; |
| } |
| const struct rtmsg* rtm = |
| reinterpret_cast<const struct rtmsg*>(payload.data()); |
| |
| auto msg = std::make_unique<RTNLMessage>(kTypeRule, mode, 0, 0, 0, 0, |
| rtm->rtm_family); |
| msg->set_route_status(RouteStatus( |
| rtm->rtm_dst_len, rtm->rtm_src_len, rtm->rtm_table, rtm->rtm_protocol, |
| rtm->rtm_scope, rtm->rtm_type, rtm->rtm_flags)); |
| return msg; |
| } |
| |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeNdUserOption( |
| Mode mode, base::span<const uint8_t> payload) { |
| // Parse the nduseroptmsg struct. |
| if (payload.size() < sizeof(struct nduseroptmsg)) { |
| return nullptr; |
| } |
| const struct nduseroptmsg* nd_user_opt = |
| reinterpret_cast<const struct nduseroptmsg*>(payload.data()); |
| payload = payload.subspan(sizeof(struct nduseroptmsg)); |
| |
| // Verify IP family. |
| const int32_t interface_index = nd_user_opt->nduseropt_ifindex; |
| const sa_family_t family = nd_user_opt->nduseropt_family; |
| if (FromSAFamily(family) != IPFamily::kIPv6) { |
| return nullptr; |
| } |
| |
| // Parse the option header. |
| if (payload.size() < sizeof(NDUserOptionHeader)) { |
| return nullptr; |
| } |
| const NDUserOptionHeader* nd_user_option_header = |
| reinterpret_cast<const NDUserOptionHeader*>(payload.data()); |
| payload = payload.subspan(sizeof(NDUserOptionHeader)); |
| |
| // Verify option length. |
| // The length field in the header is in units of 8 octets, which indicates the |
| // size of payload, including the NDUserOptionHeader. |
| const size_t opt_len = nd_user_option_header->length * 8; |
| if (opt_len != nd_user_opt->nduseropt_opts_len) { |
| return nullptr; |
| } |
| if (payload.size() < opt_len - sizeof(NDUserOptionHeader)) { |
| return nullptr; |
| } |
| payload = payload.subspan(0, opt_len - sizeof(NDUserOptionHeader)); |
| |
| std::unique_ptr<RTNLMessage> msg; |
| switch (nd_user_option_header->type) { |
| case ND_OPT_DNSSL: { |
| msg = std::make_unique<RTNLMessage>(kTypeDnssl, mode, 0, 0, 0, |
| interface_index, family); |
| if (!msg->ParseDnsslOption(payload)) { |
| LOG(ERROR) << "Invalid DNSSL RTNL packet."; |
| return nullptr; |
| } |
| return msg; |
| } |
| case ND_OPT_RDNSS: { |
| // Parse RNDSS (Recursive DNS Server) option. |
| msg = std::make_unique<RTNLMessage>(kTypeRdnss, mode, 0, 0, 0, |
| interface_index, family); |
| if (!msg->ParseRdnssOption(payload)) { |
| LOG(ERROR) << "Invalid RDNSS RTNL packet."; |
| return nullptr; |
| } |
| return msg; |
| } |
| case ND_OPT_CAPTIVE_PORTAL: { |
| // Parse Captive portal URI option. |
| msg = std::make_unique<RTNLMessage>(kTypeCaptivePortal, mode, 0, 0, 0, |
| interface_index, family); |
| if (!msg->ParseCaptivePortalOption(payload)) { |
| LOG(ERROR) << "Invalid captive portal URI RTNL packet."; |
| return nullptr; |
| } |
| return msg; |
| } |
| default: |
| msg = std::make_unique<RTNLMessage>(kTypeNdUserOption, mode, 0, 0, 0, |
| interface_index, family); |
| msg->SetNdUserOptionBytes(base::span( |
| reinterpret_cast<const uint8_t*>(nd_user_option_header), opt_len)); |
| return msg; |
| } |
| } |
| |
| void RTNLMessage::SetNdUserOptionBytes(base::span<const uint8_t> data) { |
| LOG_IF(DFATAL, data.empty()) << "ND user option data should not be empty"; |
| nd_user_option_.type = data[0]; |
| nd_user_option_.option_bytes.assign(std::begin(data), std::end(data)); |
| } |
| |
| bool RTNLMessage::ParseDnsslOption(base::span<const uint8_t> data) { |
| // Section 5.2 of RFC8106. |
| // The layout of DNSSL option after the type and length field is: |
| // - Reserved: 2 bytes |
| // - Lifetime: 4 bytes |
| // - Domain names of DNS search list: one or more domain name that is encoded |
| // as Section 3.1 of RFC1035. |
| |
| if (data.size() < 2 + 4) { |
| return false; |
| } |
| |
| // Skip the reserved field. |
| data = data.subspan(2); |
| |
| // Parse the lifetime. |
| const uint32_t lifetime = |
| ntohl(*byte_utils::FromBytes<uint32_t>(data.subspan(0, 4))); |
| data = data.subspan(4); |
| |
| std::vector<std::string> domains; |
| std::vector<std::string_view> tokens; |
| while (!data.empty()) { |
| const uint8_t token_size = data[0]; |
| data = data.subspan(1); |
| |
| if (data.size() < token_size) { |
| return false; |
| } |
| if (token_size > 0) { |
| tokens.push_back(std::string_view( |
| reinterpret_cast<const char*>(data.data()), token_size)); |
| data = data.subspan(token_size); |
| } else if (!tokens.empty()) { |
| domains.push_back(base::JoinString(tokens, ".")); |
| tokens.clear(); |
| } |
| } |
| if (!tokens.empty()) { |
| domains.push_back(base::JoinString(tokens, ".")); |
| } |
| dnssl_option_.domains = std::move(domains); |
| dnssl_option_.lifetime = lifetime; |
| return true; |
| } |
| |
| bool RTNLMessage::ParseRdnssOption(base::span<const uint8_t> data) { |
| // Section 5.1 of RFC8106. |
| // The layout of RDNSS option after the type and length field is: |
| // - Reserved: 2 bytes |
| // - Lifetime: 4 bytes |
| // - IPv6 Recursive DNS servers: one or more 16-byte IPv6 addresses |
| const size_t addr_length = IPv6Address::kAddressLength; |
| |
| if (data.size() < 2 + 4 + addr_length) { |
| return false; |
| } |
| |
| // Skip the reserved field. |
| data = data.subspan(2); |
| |
| // Parse the lifetime. |
| const uint32_t lifetime = |
| ntohl(*byte_utils::FromBytes<uint32_t>(data.subspan(0, 4))); |
| data = data.subspan(4); |
| |
| // Parse the recursive DNS servers. |
| // Verify data size are multiple of individual address size. |
| if (data.size() % addr_length != 0) { |
| return false; |
| } |
| |
| // Parse the DNS server addresses. |
| std::vector<IPv6Address> dns_server_addresses; |
| while (!data.empty()) { |
| dns_server_addresses.push_back( |
| *IPv6Address::CreateFromBytes(data.subspan(0, addr_length))); |
| data = data.subspan(addr_length); |
| } |
| set_rdnss_option(RdnssOption(lifetime, dns_server_addresses)); |
| return true; |
| } |
| |
| bool RTNLMessage::ParseCaptivePortalOption(base::span<const uint8_t> data) { |
| // Section 2.3 of RFC8910. |
| // The layout of RDNSS option after the type and length field is: |
| // - URI: padded with 0 to make the total option length a multiple of 8 bytes |
| if (data.empty()) { |
| LOG(ERROR) << "Empty payload for captive portal URI"; |
| return false; |
| } |
| |
| // The string is not guaranteed to be null terminated, so we need to find the |
| // length by strnlen(). |
| const char* str = reinterpret_cast<const char*>(data.data()); |
| const std::string_view str_view(str, strnlen(str, data.size())); |
| const std::optional<HttpUrl> url = HttpUrl::CreateFromString(str_view); |
| if (!url.has_value() || url->protocol() != HttpUrl::Protocol::kHttps) { |
| LOG(ERROR) << "Invalid captive portal URI: " << str_view; |
| return false; |
| } |
| set_captive_portal_uri(*url); |
| return true; |
| } |
| |
| std::unique_ptr<RTNLMessage> RTNLMessage::DecodeNeighbor( |
| Mode mode, base::span<const uint8_t> payload) { |
| if (payload.size() < sizeof(struct ndmsg)) { |
| return nullptr; |
| } |
| const struct ndmsg* ndm = |
| reinterpret_cast<const struct ndmsg*>(payload.data()); |
| |
| auto msg = std::make_unique<RTNLMessage>(kTypeNeighbor, mode, 0, 0, 0, |
| ndm->ndm_ifindex, ndm->ndm_family); |
| msg->set_neighbor_status( |
| NeighborStatus(ndm->ndm_state, ndm->ndm_flags, ndm->ndm_type)); |
| return msg; |
| } |
| |
| std::vector<uint8_t> RTNLMessage::Encode() const { |
| if (type_ != kTypeLink && type_ != kTypeAddress && type_ != kTypeRoute && |
| type_ != kTypeRule && type_ != kTypeNeighbor) { |
| return {}; |
| } |
| |
| RTNLHeader hdr; |
| hdr.hdr.nlmsg_flags = flags_; |
| hdr.hdr.nlmsg_seq = seq_; |
| hdr.hdr.nlmsg_pid = pid_; |
| |
| switch (type_) { |
| case kTypeLink: |
| if (!EncodeLink(&hdr)) { |
| return {}; |
| } |
| break; |
| |
| case kTypeAddress: |
| if (!EncodeAddress(&hdr)) { |
| return {}; |
| } |
| break; |
| |
| case kTypeRoute: |
| case kTypeRule: |
| if (!EncodeRoute(&hdr)) { |
| return {}; |
| } |
| break; |
| |
| case kTypeNeighbor: |
| if (!EncodeNeighbor(&hdr)) { |
| return {}; |
| } |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| |
| if (mode_ == kModeGet) { |
| hdr.hdr.nlmsg_flags |= NLM_F_REQUEST | NLM_F_DUMP; |
| } |
| |
| uint32_t header_length = hdr.hdr.nlmsg_len; |
| const std::vector<uint8_t> attributes = PackAttrs(attributes_); |
| hdr.hdr.nlmsg_len = |
| NLMSG_ALIGN(hdr.hdr.nlmsg_len) + static_cast<uint32_t>(attributes.size()); |
| std::vector<uint8_t> packet(reinterpret_cast<uint8_t*>(&hdr), |
| reinterpret_cast<uint8_t*>(&hdr) + header_length); |
| packet.insert(packet.end(), attributes.begin(), attributes.end()); |
| |
| return packet; |
| } |
| |
| bool RTNLMessage::EncodeLink(RTNLHeader* hdr) const { |
| switch (mode_) { |
| case kModeAdd: |
| hdr->hdr.nlmsg_type = RTM_NEWLINK; |
| break; |
| case kModeDelete: |
| hdr->hdr.nlmsg_type = RTM_DELLINK; |
| break; |
| case kModeGet: |
| case kModeQuery: |
| hdr->hdr.nlmsg_type = RTM_GETLINK; |
| break; |
| default: |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi)); |
| hdr->ifi.ifi_family = static_cast<unsigned char>(family_); |
| hdr->ifi.ifi_index = interface_index_; |
| hdr->ifi.ifi_type = static_cast<uint16_t>(link_status_.type); |
| hdr->ifi.ifi_flags = link_status_.flags; |
| hdr->ifi.ifi_change = link_status_.change; |
| return true; |
| } |
| |
| bool RTNLMessage::EncodeAddress(RTNLHeader* hdr) const { |
| switch (mode_) { |
| case kModeAdd: |
| hdr->hdr.nlmsg_type = RTM_NEWADDR; |
| break; |
| case kModeDelete: |
| hdr->hdr.nlmsg_type = RTM_DELADDR; |
| break; |
| case kModeGet: |
| case kModeQuery: |
| hdr->hdr.nlmsg_type = RTM_GETADDR; |
| break; |
| default: |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa)); |
| hdr->ifa.ifa_family = static_cast<unsigned char>(family_); |
| hdr->ifa.ifa_prefixlen = address_status_.prefix_len; |
| hdr->ifa.ifa_flags = address_status_.flags; |
| hdr->ifa.ifa_scope = address_status_.scope; |
| hdr->ifa.ifa_index = static_cast<uint32_t>(interface_index_); |
| return true; |
| } |
| |
| bool RTNLMessage::EncodeRoute(RTNLHeader* hdr) const { |
| // Routes and routing rules are both based on struct rtm |
| switch (mode_) { |
| case kModeAdd: |
| hdr->hdr.nlmsg_type = (type_ == kTypeRoute) ? RTM_NEWROUTE : RTM_NEWRULE; |
| break; |
| case kModeDelete: |
| hdr->hdr.nlmsg_type = (type_ == kTypeRoute) ? RTM_DELROUTE : RTM_DELRULE; |
| break; |
| case kModeGet: |
| case kModeQuery: |
| hdr->hdr.nlmsg_type = (type_ == kTypeRoute) ? RTM_GETROUTE : RTM_GETRULE; |
| break; |
| default: |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm)); |
| hdr->rtm.rtm_family = static_cast<unsigned char>(family_); |
| hdr->rtm.rtm_dst_len = route_status_.dst_prefix; |
| hdr->rtm.rtm_src_len = route_status_.src_prefix; |
| hdr->rtm.rtm_table = route_status_.table; |
| hdr->rtm.rtm_protocol = route_status_.protocol; |
| hdr->rtm.rtm_scope = route_status_.scope; |
| hdr->rtm.rtm_type = route_status_.type; |
| hdr->rtm.rtm_flags = route_status_.flags; |
| return true; |
| } |
| |
| bool RTNLMessage::EncodeNeighbor(RTNLHeader* hdr) const { |
| switch (mode_) { |
| case kModeAdd: |
| hdr->hdr.nlmsg_type = RTM_NEWNEIGH; |
| break; |
| case kModeDelete: |
| hdr->hdr.nlmsg_type = RTM_DELNEIGH; |
| break; |
| case kModeGet: |
| case kModeQuery: |
| hdr->hdr.nlmsg_type = RTM_GETNEIGH; |
| break; |
| default: |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ndm)); |
| hdr->ndm.ndm_family = static_cast<unsigned char>(family_); |
| hdr->ndm.ndm_ifindex = interface_index_; |
| hdr->ndm.ndm_state = neighbor_status_.state; |
| hdr->ndm.ndm_flags = neighbor_status_.flags; |
| hdr->ndm.ndm_type = neighbor_status_.type; |
| return true; |
| } |
| |
| uint32_t RTNLMessage::GetUint32Attribute(uint16_t attr) const { |
| return net_base::byte_utils::FromBytes<uint32_t>(GetAttribute(attr)) |
| .value_or(0); |
| } |
| |
| std::string RTNLMessage::GetStringAttribute(uint16_t attr) const { |
| if (!HasAttribute(attr)) |
| return ""; |
| return net_base::byte_utils::StringFromCStringBytes(GetAttribute(attr)); |
| } |
| |
| std::string RTNLMessage::GetIflaIfname() const { |
| return GetStringAttribute(IFLA_IFNAME); |
| } |
| |
| std::optional<IPCIDR> RTNLMessage::GetAddress() const { |
| // Use IFA_LOCAL if it's not empty and fallback to IFA_ADDRESS. According the |
| // kernel comment (/usr/include/linux/if_addr.h), for a point-to-point link, |
| // IFA_LOCAL is the local address on the interface while IFA_ADDRESS is the |
| // peer one. Since IFA_LOCAL won't always be set (e.g., for some IPv6 |
| // addresses), we also need to query IFA_ADDRESS. |
| std::vector<uint8_t> addr_bytes = HasAttribute(IFA_LOCAL) |
| ? GetAttribute(IFA_LOCAL) |
| : GetAttribute(IFA_ADDRESS); |
| return IPCIDR::CreateFromBytesAndPrefix( |
| addr_bytes, address_status_.prefix_len, FromSAFamily(family_)); |
| } |
| |
| uint32_t RTNLMessage::GetRtaTable() const { |
| return GetUint32Attribute(RTA_TABLE); |
| } |
| |
| std::optional<IPCIDR> RTNLMessage::GetRtaDst() const { |
| return IPCIDR::CreateFromBytesAndPrefix( |
| GetAttribute(RTA_DST), route_status_.dst_prefix, FromSAFamily(family_)); |
| } |
| |
| std::optional<IPCIDR> RTNLMessage::GetRtaSrc() const { |
| return IPCIDR::CreateFromBytesAndPrefix( |
| GetAttribute(RTA_SRC), route_status_.src_prefix, FromSAFamily(family_)); |
| } |
| |
| std::optional<IPAddress> RTNLMessage::GetRtaGateway() const { |
| return IPAddress::CreateFromBytes(GetAttribute(RTA_GATEWAY), |
| FromSAFamily(family_)); |
| } |
| |
| std::optional<IPAddress> RTNLMessage::GetRtaPrefSrc() const { |
| return IPAddress::CreateFromBytes(GetAttribute(RTA_PREFSRC), |
| FromSAFamily(family_)); |
| } |
| |
| uint32_t RTNLMessage::GetRtaOif() const { |
| return GetUint32Attribute(RTA_OIF); |
| } |
| |
| std::string RTNLMessage::GetRtaOifname() const { |
| return IndexToName(GetRtaOif()); |
| } |
| |
| uint32_t RTNLMessage::GetRtaPriority() const { |
| return GetUint32Attribute(RTA_PRIORITY); |
| } |
| |
| uint32_t RTNLMessage::GetFraTable() const { |
| return GetUint32Attribute(FRA_TABLE); |
| } |
| |
| std::string RTNLMessage::GetFraOifname() const { |
| return GetStringAttribute(FRA_OIFNAME); |
| } |
| |
| std::string RTNLMessage::GetFraIifname() const { |
| return GetStringAttribute(FRA_IIFNAME); |
| } |
| |
| std::optional<IPCIDR> RTNLMessage::GetFraSrc() const { |
| return IPCIDR::CreateFromBytesAndPrefix( |
| GetAttribute(FRA_SRC), route_status_.src_prefix, FromSAFamily(family_)); |
| } |
| |
| std::optional<IPCIDR> RTNLMessage::GetFraDst() const { |
| return IPCIDR::CreateFromBytesAndPrefix( |
| GetAttribute(FRA_DST), route_status_.dst_prefix, FromSAFamily(family_)); |
| } |
| |
| uint32_t RTNLMessage::GetFraFwmark() const { |
| return GetUint32Attribute(FRA_FWMARK); |
| } |
| |
| uint32_t RTNLMessage::GetFraFwmask() const { |
| return GetUint32Attribute(FRA_FWMASK); |
| } |
| |
| uint32_t RTNLMessage::GetFraPriority() const { |
| return GetUint32Attribute(FRA_PRIORITY); |
| } |
| |
| void RTNLMessage::SetIflaInfoKind(const std::string& link_kind, |
| base::span<const uint8_t> info_data) { |
| // The maximum length of IFLA_INFO_KIND attribute is MODULE_NAME_LEN, defined |
| // in /include/linux/module.h, as (64 - sizeof(unsigned long)). Set it to a |
| // fixed value here. |
| constexpr uint32_t kMaxModuleNameLen = 56; |
| if (link_kind.length() >= kMaxModuleNameLen) { |
| LOG(DFATAL) << "link_kind is too long: " << link_kind; |
| } |
| link_status_.kind = link_kind; |
| RTNLAttrMap link_info_map; |
| link_info_map[IFLA_INFO_KIND] = |
| net_base::byte_utils::StringToCStringBytes(link_kind.c_str()); |
| if (!info_data.empty()) { |
| link_info_map[IFLA_INFO_DATA] = {info_data.data(), |
| info_data.data() + info_data.size()}; |
| } |
| if (HasAttribute(IFLA_LINKINFO)) { |
| LOG(DFATAL) << "IFLA_LINKINFO has already been set."; |
| } |
| SetAttribute(IFLA_LINKINFO, PackAttrs(link_info_map)); |
| } |
| |
| // static |
| std::string RTNLMessage::ModeToString(RTNLMessage::Mode mode) { |
| switch (mode) { |
| case RTNLMessage::kModeGet: |
| return "Get"; |
| case RTNLMessage::kModeAdd: |
| return "Add"; |
| case RTNLMessage::kModeDelete: |
| return "Delete"; |
| case RTNLMessage::kModeQuery: |
| return "Query"; |
| default: |
| return "UnknownMode"; |
| } |
| } |
| |
| // static |
| std::string RTNLMessage::TypeToString(RTNLMessage::Type type) { |
| switch (type) { |
| case RTNLMessage::kTypeLink: |
| return "Link"; |
| case RTNLMessage::kTypeAddress: |
| return "Address"; |
| case RTNLMessage::kTypeRoute: |
| return "Route"; |
| case RTNLMessage::kTypeRule: |
| return "Rule"; |
| case RTNLMessage::kTypeRdnss: |
| return "Rdnss"; |
| case RTNLMessage::kTypeDnssl: |
| return "Dnssl"; |
| case RTNLMessage::kTypeNeighbor: |
| return "Neighbor"; |
| case RTNLMessage::kTypeNdUserOption: |
| return "NdUserOption"; |
| default: |
| return "UnknownType"; |
| } |
| } |
| |
| std::string RTNLMessage::ToString() const { |
| // Include the space separator in |ip_family_str| to avoid double spaces for |
| // messages with family AF_UNSPEC. |
| const auto ip_family = FromSAFamily(family()); |
| std::string ip_family_str = |
| " " + (ip_family ? net_base::ToString(*ip_family) : "unknown"); |
| std::string details; |
| switch (type()) { |
| case RTNLMessage::kTypeLink: |
| ip_family_str = ""; |
| details = base::StringPrintf( |
| "%s[%d] type %s flags <%s> change %X", GetIflaIfname().c_str(), |
| interface_index_, GetNetDeviceTypeName(link_status_.type).c_str(), |
| PrintFlags(link_status_.flags, kNetDeviceFlags, ",").c_str(), |
| link_status_.change); |
| if (link_status_.kind.has_value()) |
| details += " kind " + link_status_.kind.value(); |
| break; |
| case RTNLMessage::kTypeAddress: |
| if (const auto addr = GetAddress(); addr.has_value()) { |
| details = base::StringPrintf( |
| "%s if %s[%d] flags %s scope %d", addr->ToString().c_str(), |
| IndexToName(static_cast<unsigned int>(interface_index_)).c_str(), |
| interface_index_, |
| address_status_.flags |
| ? PrintFlags(address_status_.flags, kIfaFlags).c_str() |
| : "0", |
| address_status_.scope); |
| } else { |
| LOG(ERROR) |
| << "RTNL address message does not have a valid local address"; |
| } |
| break; |
| case RTNLMessage::kTypeRoute: |
| if (const auto addr = GetRtaSrc(); addr.has_value()) |
| details += base::StringPrintf("src %s ", addr->ToString().c_str()); |
| if (const auto addr = GetRtaDst(); addr.has_value()) |
| details += base::StringPrintf("dst %s ", addr->ToString().c_str()); |
| if (const auto addr = GetRtaGateway(); addr.has_value()) |
| details += "via " + addr->ToString() + " "; |
| if (HasAttribute(RTA_OIF)) |
| details += base::StringPrintf("if %s[%d] ", GetRtaOifname().c_str(), |
| GetRtaOif()); |
| details += base::StringPrintf( |
| "table %d priority %d protocol %s type %s", GetRtaTable(), |
| GetRtaPriority(), GetRouteProtocol(route_status_.protocol).c_str(), |
| GetRouteTypeName(route_status_.type).c_str()); |
| break; |
| case RTNLMessage::kTypeRule: |
| // Rules are serialized via struct fib_rule_hdr which aligns with struct |
| // rtmsg used for routes such that |type| corresponds to the rule action. |
| // |protocol| and |scope| are currently unused as of Linux 5.6. |
| if (HasAttribute(FRA_IIFNAME)) |
| details += "iif " + GetFraIifname() + " "; |
| if (HasAttribute(FRA_OIFNAME)) |
| details += "oif " + GetFraOifname() = " "; |
| if (const auto addr = GetFraSrc(); addr.has_value()) |
| details += base::StringPrintf("src %s ", addr->ToString().c_str()); |
| if (const auto addr = GetFraDst(); addr.has_value()) |
| details += base::StringPrintf("dst %s ", addr->ToString().c_str()); |
| if (HasAttribute(FRA_FWMARK)) |
| details += base::StringPrintf("fwmark 0x%X/0x%X ", GetFraFwmark(), |
| GetFraFwmask()); |
| details += base::StringPrintf( |
| "table %d priority %d action %s flags %X", GetFraTable(), |
| GetFraPriority(), GetRuleActionName(route_status_.type).c_str(), |
| route_status_.flags); |
| break; |
| case RTNLMessage::kTypeRdnss: |
| details = rdnss_option_.ToString(); |
| break; |
| case RTNLMessage::kTypeDnssl: |
| details = dnssl_option_.ToString(); |
| break; |
| case RTNLMessage::kTypeNdUserOption: |
| details = nd_user_option_.ToString(); |
| break; |
| case RTNLMessage::kTypeNeighbor: |
| details = neighbor_status_.ToString(); |
| break; |
| default: |
| break; |
| } |
| return base::StringPrintf("%s%s %s: %s", ModeToString(mode()).c_str(), |
| ip_family_str.c_str(), TypeToString(type()).c_str(), |
| details.c_str()); |
| } |
| |
| } // namespace net_base |