| // 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 |