| // Copyright 2018 The Chromium OS 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 "shill/net/rtnl_message.h" |
| |
| #include <net/if.h> // NB: order matters; this conflicts with <linux/if.h> |
| #include <arpa/inet.h> |
| #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 <map> |
| #include <memory> |
| #include <utility> |
| |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include "shill/net/ndisc.h" |
| |
| namespace shill { |
| |
| namespace { |
| |
| using flags_info_t = std::vector<std::pair<uint32_t, std::string>>; |
| |
| // Helper for pretty printing flags |
| std::string PrintFlags(uint32_t flags, |
| const flags_info_t& flags_info, |
| const std::string& separator = " | ") { |
| std::string str; |
| if (flags == 0) |
| return str; |
| std::string sep = ""; |
| for (const auto& flag_descr : flags_info) { |
| if ((flags & flag_descr.first) == 0) |
| continue; |
| str += sep; |
| str += flag_descr.second; |
| sep = separator; |
| } |
| return str; |
| } |
| |
| const flags_info_t 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. Defined in uapi/linux/if.h |
| const flags_info_t 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"}, |
| }; |
| |
| // Fine grained link types. Defined in uapi/linux/if_arp.h |
| std::map<uint16_t, std::string> kNetDeviceTypes = { |
| {ARPHRD_NETROM, "NETROM"}, |
| {ARPHRD_ETHER, "ETHER"}, |
| {ARPHRD_EETHER, "EETHER"}, |
| {ARPHRD_AX25, "AX25"}, |
| {ARPHRD_PRONET, "PRONET"}, |
| {ARPHRD_CHAOS, "CHAOS"}, |
| {ARPHRD_IEEE802, "IEEE802"}, |
| {ARPHRD_ARCNET, "ARCNET"}, |
| {ARPHRD_APPLETLK, "APPLETLK"}, |
| {ARPHRD_DLCI, "DLCI"}, |
| {ARPHRD_ATM, "ATM"}, |
| {ARPHRD_METRICOM, "METRICOM"}, |
| {ARPHRD_IEEE1394, "IEEE1394"}, |
| {ARPHRD_EUI64, "EUI64"}, |
| {ARPHRD_INFINIBAND, "INFINIBAND"}, |
| {ARPHRD_SLIP, "SLIP"}, |
| {ARPHRD_CSLIP, "CSLIP"}, |
| {ARPHRD_SLIP6, "SLIP6"}, |
| {ARPHRD_CSLIP6, "CSLIP6"}, |
| {ARPHRD_RSRVD, "RSRVD"}, |
| {ARPHRD_ADAPT, "ADAPT"}, |
| {ARPHRD_ROSE, "ROSE"}, |
| {ARPHRD_X25, "X25"}, |
| {ARPHRD_HWX25, "HWX25"}, |
| {ARPHRD_CAN, "CAN"}, |
| {ARPHRD_PPP, "PPP"}, |
| {ARPHRD_CISCO, "CISCO"}, |
| {ARPHRD_HDLC, "HDLC"}, |
| {ARPHRD_LAPB, "LAPB"}, |
| {ARPHRD_DDCMP, "DDCMP"}, |
| {ARPHRD_RAWHDLC, "RAWHDLC"}, |
| {ARPHRD_RAWIP, "RAWIP"}, |
| {ARPHRD_TUNNEL, "TUNNEL"}, |
| {ARPHRD_TUNNEL6, "TUNNEL6"}, |
| {ARPHRD_FRAD, "FRAD"}, |
| {ARPHRD_SKIP, "SKIP"}, |
| {ARPHRD_LOOPBACK, "LOOPBACK"}, |
| {ARPHRD_LOCALTLK, "LOCALTLK"}, |
| {ARPHRD_FDDI, "FDDI"}, |
| {ARPHRD_BIF, "BIF"}, |
| {ARPHRD_SIT, "SIT"}, |
| {ARPHRD_IPDDP, "IPDDP"}, |
| {ARPHRD_IPGRE, "IPGRE"}, |
| {ARPHRD_PIMREG, "PIMREG"}, |
| {ARPHRD_HIPPI, "HIPPI"}, |
| {ARPHRD_ASH, "ASH"}, |
| {ARPHRD_ECONET, "ECONET"}, |
| {ARPHRD_IRDA, "IRDA"}, |
| {ARPHRD_FCPP, "FCPP"}, |
| {ARPHRD_FCAL, "FCAL"}, |
| {ARPHRD_FCPL, "FCPL"}, |
| {ARPHRD_FCFABRIC, "FCFABRIC"}, |
| {ARPHRD_IEEE802_TR, "IEEE802_TR"}, |
| {ARPHRD_IEEE80211, "IEEE80211"}, |
| {ARPHRD_IEEE80211_PRISM, "IEEE80211_PRISM"}, |
| {ARPHRD_IEEE80211_RADIOTAP, "IEEE80211_RADIOTAP"}, |
| {ARPHRD_IEEE802154, "IEEE802154 "}, |
| {ARPHRD_IEEE802154_MONITOR, "IEEE802154_MONITOR"}, |
| {ARPHRD_PHONET, "PHONET"}, |
| {ARPHRD_PHONET_PIPE, "PHONET_PIPE"}, |
| {ARPHRD_CAIF, "CAIF"}, |
| {ARPHRD_IP6GRE, "IP6GRE"}, |
| {ARPHRD_NETLINK, "NETLINK"}, |
| {ARPHRD_6LOWPAN, "6LOWPAN"}, |
| {ARPHRD_VSOCKMON, "VSOCKMON"}, |
| {ARPHRD_VOID, "VOID"}, |
| {ARPHRD_NONE, "NONE"}, |
| }; |
| |
| // Route types. Defined in uapi/linux/rtnetlink.h |
| std::map<uint8_t, std::string> kRouteTypes = { |
| {RTN_UNSPEC, "UNSPEC"}, |
| {RTN_UNICAST, "UNICAST"}, |
| {RTN_LOCAL, "LOCAL"}, |
| {RTN_BROADCAST, "BROADCAST"}, |
| {RTN_ANYCAST, "ANYCAST"}, |
| {RTN_MULTICAST, "MULTICAST"}, |
| {RTN_BLACKHOLE, "BLACKHOLE"}, |
| {RTN_UNREACHABLE, "UNREACHABLE"}, |
| {RTN_PROHIBIT, "PROHIBIT"}, |
| {RTN_THROW, "THROW"}, |
| {RTN_NAT, "NAT"}, |
| {RTN_XRESOLVE, "XRESOLVE"}, |
| }; |
| |
| // Route protocols. Defined in uapi/linux/rtnetlink.h |
| std::map<uint8_t, std::string> kRouteProtocols = { |
| {RTPROT_UNSPEC, "UNSPEC"}, |
| {RTPROT_REDIRECT, "REDIRECT"}, |
| {RTPROT_KERNEL, "KERNEL"}, |
| {RTPROT_BOOT, "BOOT"}, |
| {RTPROT_STATIC, "STATIC"}, |
| {RTPROT_GATED, "GATED"}, |
| {RTPROT_RA, "RA"}, |
| {RTPROT_MRT, "MRT"}, |
| {RTPROT_ZEBRA, "ZEBRA"}, |
| {RTPROT_BIRD, "BIRD"}, |
| {RTPROT_DNROUTED, "DNROUTED"}, |
| {RTPROT_XORP, "XORP"}, |
| {RTPROT_NTK, "NTK"}, |
| {RTPROT_DHCP, "DHCP"}, |
| {RTPROT_MROUTED, "MROUTED"}, |
| {RTPROT_BABEL, "BABEL"}, |
| // The following protocols are not defined on Linux 4.14 |
| {186 /* RTPROT_BGP */, "BGP"}, |
| {187 /* RTPROT_ISIS */, "ISIS"}, |
| {188 /* RTPROT_OSPF */, "OSPF"}, |
| {189 /* RTPROT_RIP */, "RIP"}, |
| {192 /* RTPROT_EIGRP */, "EIGRP"}, |
| }; |
| |
| // Rule actions. Defined in 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. Possible values are defined in |
| // uapi/linux/fib_rules.h. |
| std::map<uint8_t, std::string> kRuleActions = { |
| {FR_ACT_UNSPEC, "UNSPEC"}, {FR_ACT_TO_TBL, "TO_TBL"}, |
| {FR_ACT_GOTO, "GOTO"}, {FR_ACT_NOP, "NOP"}, |
| {FR_ACT_RES3, "RES3"}, {FR_ACT_RES4, "RES4"}, |
| {FR_ACT_BLACKHOLE, "BLACKHOLE"}, {FR_ACT_UNREACHABLE, "UNREACHABLE"}, |
| {FR_ACT_PROHIBIT, "PROHIBIT"}, |
| }; |
| |
| // Helper function to return route protocol names defined by the kernel. |
| // User reserved protocol values are returned as decimal numbers. |
| std::string GetRouteProtocol(uint8_t protocol) { |
| const auto it = kRouteProtocols.find(protocol); |
| if (it == kRouteProtocols.end()) |
| return std::to_string(protocol); |
| return it->second; |
| } |
| |
| std::unique_ptr<RTNLAttrMap> ParseAttrs(struct rtattr* data, int len) { |
| RTNLAttrMap attrs; |
| ByteString attr_bytes(reinterpret_cast<const char*>(data), len); |
| |
| while (data && RTA_OK(data, len)) { |
| attrs[data->rta_type] = ByteString( |
| reinterpret_cast<unsigned char*>(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 <" << attr_bytes.HexEncode() |
| << ">, trailing length: " << len; |
| return nullptr; |
| } |
| |
| return std::make_unique<RTNLAttrMap>(attrs); |
| } |
| |
| ByteString PackAttrs(const RTNLAttrMap& attrs) { |
| ByteString attributes; |
| |
| for (const auto& pair : attrs) { |
| size_t len = RTA_LENGTH(pair.second.GetLength()); |
| 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, |
| }; |
| ByteString header(reinterpret_cast<unsigned char*>(&rt_attr), |
| sizeof(rt_attr)); |
| header.Resize(RTA_ALIGN(header.GetLength())); |
| attributes.Append(header); |
| |
| ByteString data(pair.second); |
| data.Resize(RTA_ALIGN(data.GetLength())); |
| attributes.Append(data); |
| } |
| |
| return attributes; |
| } |
| |
| // 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(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; |
| }; |
| }; |
| |
| std::string RTNLMessage::NeighborStatus::ToString() const { |
| return base::StringPrintf("NeighborStatus state %d flags %X type %d", state, |
| flags, type); |
| } |
| |
| std::string RTNLMessage::RdnssOption::ToString() const { |
| return base::StringPrintf("RdnssOption lifetime %d", lifetime); |
| } |
| |
| RTNLMessage::RTNLMessage() |
| : type_(kTypeUnknown), |
| mode_(kModeUnknown), |
| flags_(0), |
| seq_(0), |
| pid_(0), |
| interface_index_(0), |
| family_(IPAddress::kFamilyUnknown) {} |
| |
| RTNLMessage::RTNLMessage(Type type, |
| Mode mode, |
| uint16_t flags, |
| uint32_t seq, |
| uint32_t pid, |
| int32_t interface_index, |
| IPAddress::Family family) |
| : type_(type), |
| mode_(mode), |
| flags_(flags), |
| seq_(seq), |
| pid_(pid), |
| interface_index_(interface_index), |
| family_(family) {} |
| |
| bool RTNLMessage::Decode(const ByteString& msg) { |
| bool ret = DecodeInternal(msg); |
| if (!ret) { |
| Reset(); |
| } |
| return ret; |
| } |
| |
| bool RTNLMessage::DecodeInternal(const ByteString& msg) { |
| const RTNLHeader* hdr = |
| reinterpret_cast<const RTNLHeader*>(msg.GetConstData()); |
| |
| if (msg.GetLength() < sizeof(hdr->hdr) || |
| msg.GetLength() < hdr->hdr.nlmsg_len) |
| return false; |
| |
| mode_ = kModeUnknown; |
| switch (hdr->hdr.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 false; |
| } |
| |
| rtattr* attr_data = nullptr; |
| int attr_length = 0; |
| |
| switch (hdr->hdr.nlmsg_type) { |
| case RTM_NEWLINK: |
| case RTM_DELLINK: |
| if (!DecodeLink(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| if (!DecodeAddress(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| case RTM_NEWROUTE: |
| case RTM_DELROUTE: |
| if (!DecodeRoute(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| case RTM_NEWRULE: |
| case RTM_DELRULE: |
| if (!DecodeRule(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| case RTM_NEWNDUSEROPT: |
| if (!DecodeNdUserOption(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| case RTM_NEWNEIGH: |
| case RTM_DELNEIGH: |
| if (!DecodeNeighbor(hdr, &attr_data, &attr_length)) |
| return false; |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| |
| flags_ = hdr->hdr.nlmsg_flags; |
| seq_ = hdr->hdr.nlmsg_seq; |
| pid_ = hdr->hdr.nlmsg_pid; |
| |
| std::unique_ptr<RTNLAttrMap> attrs = ParseAttrs(attr_data, attr_length); |
| if (!attrs) { |
| attributes_.clear(); |
| return false; |
| } |
| |
| for (const auto& pair : *attrs) { |
| SetAttribute(pair.first, pair.second); |
| } |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeLink(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) { |
| return false; |
| } |
| |
| *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr)); |
| *attr_length = IFLA_PAYLOAD(&hdr->hdr); |
| |
| type_ = kTypeLink; |
| family_ = hdr->ifi.ifi_family; |
| interface_index_ = hdr->ifi.ifi_index; |
| |
| std::unique_ptr<RTNLAttrMap> attrs = ParseAttrs(*attr_data, *attr_length); |
| if (!attrs) |
| return false; |
| |
| base::Optional<std::string> kind_option; |
| |
| if (base::Contains(*attrs, IFLA_LINKINFO)) { |
| ByteString& bytes = attrs->find(IFLA_LINKINFO)->second; |
| struct rtattr* link_data = |
| reinterpret_cast<struct rtattr*>(bytes.GetData()); |
| size_t link_len = bytes.GetLength(); |
| std::unique_ptr<RTNLAttrMap> linkinfo = ParseAttrs(link_data, link_len); |
| |
| if (linkinfo && base::Contains(*linkinfo, IFLA_INFO_KIND)) { |
| ByteString& kindBytes = linkinfo->find(IFLA_INFO_KIND)->second; |
| const char* kind = reinterpret_cast<const char*>(kindBytes.GetData()); |
| std::string kind_string(kind, strnlen(kind, kindBytes.GetLength())); |
| if (base::IsStringASCII(kind_string)) |
| kind_option = kind_string; |
| else |
| LOG(ERROR) << base::StringPrintf( |
| "Invalid kind <%s>, interface index %d", |
| kindBytes.HexEncode().c_str(), interface_index_); |
| } |
| } |
| |
| set_link_status(LinkStatus(hdr->ifi.ifi_type, hdr->ifi.ifi_flags, |
| hdr->ifi.ifi_change, kind_option)); |
| |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeAddress(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) { |
| return false; |
| } |
| *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr)); |
| *attr_length = IFA_PAYLOAD(&hdr->hdr); |
| |
| type_ = kTypeAddress; |
| family_ = hdr->ifa.ifa_family; |
| interface_index_ = hdr->ifa.ifa_index; |
| set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen, hdr->ifa.ifa_flags, |
| hdr->ifa.ifa_scope)); |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeRoute(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) { |
| return false; |
| } |
| *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr)); |
| *attr_length = RTM_PAYLOAD(&hdr->hdr); |
| |
| type_ = kTypeRoute; |
| family_ = hdr->rtm.rtm_family; |
| set_route_status(RouteStatus(hdr->rtm.rtm_dst_len, hdr->rtm.rtm_src_len, |
| hdr->rtm.rtm_table, hdr->rtm.rtm_protocol, |
| hdr->rtm.rtm_scope, hdr->rtm.rtm_type, |
| hdr->rtm.rtm_flags)); |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeRule(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) { |
| return false; |
| } |
| *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr)); |
| *attr_length = RTM_PAYLOAD(&hdr->hdr); |
| |
| type_ = kTypeRule; |
| family_ = hdr->rtm.rtm_family; |
| set_route_status(RouteStatus(hdr->rtm.rtm_dst_len, hdr->rtm.rtm_src_len, |
| hdr->rtm.rtm_table, hdr->rtm.rtm_protocol, |
| hdr->rtm.rtm_scope, hdr->rtm.rtm_type, |
| hdr->rtm.rtm_flags)); |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeNdUserOption(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| size_t min_payload_len = sizeof(hdr->nd_user_opt) + |
| sizeof(struct nduseroptmsg) + |
| sizeof(NDUserOptionHeader); |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(min_payload_len)) { |
| return false; |
| } |
| |
| interface_index_ = hdr->nd_user_opt.nduseropt_ifindex; |
| family_ = hdr->nd_user_opt.nduseropt_family; |
| |
| // Verify IP family. |
| if (family_ != IPAddress::kFamilyIPv6) { |
| return false; |
| } |
| // Verify message must at-least contain the option header. |
| if (hdr->nd_user_opt.nduseropt_opts_len < sizeof(NDUserOptionHeader)) { |
| return false; |
| } |
| |
| // Parse the option header. |
| const NDUserOptionHeader* nd_user_option_header = |
| reinterpret_cast<const NDUserOptionHeader*>( |
| reinterpret_cast<const uint8_t*>(&hdr->nd_user_opt) + |
| sizeof(struct nduseroptmsg)); |
| uint32_t lifetime = ntohl(nd_user_option_header->lifetime); |
| |
| // Verify option length. |
| // The length field in the header is in units of 8 octets. |
| int opt_len = static_cast<int>(nd_user_option_header->length) * 8; |
| if (opt_len != hdr->nd_user_opt.nduseropt_opts_len) { |
| return false; |
| } |
| |
| // Determine option data pointer and data length. |
| const uint8_t* option_data = |
| reinterpret_cast<const uint8_t*>(nd_user_option_header + 1); |
| int data_len = opt_len - sizeof(NDUserOptionHeader); |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(min_payload_len + data_len)) { |
| return false; |
| } |
| |
| if (nd_user_option_header->type == ND_OPT_DNSSL) { |
| // TODO(zqiu): Parse DNSSL (DNS Search List) option. |
| type_ = kTypeDnssl; |
| return true; |
| } else if (nd_user_option_header->type == ND_OPT_RDNSS) { |
| // Parse RNDSS (Recursive DNS Server) option. |
| type_ = kTypeRdnss; |
| return ParseRdnssOption(option_data, data_len, lifetime); |
| } |
| |
| return false; |
| } |
| |
| bool RTNLMessage::ParseRdnssOption(const uint8_t* data, |
| int length, |
| uint32_t lifetime) { |
| const int addr_length = IPAddress::GetAddressLength(IPAddress::kFamilyIPv6); |
| |
| // Verify data size are multiple of individual address size. |
| if (length % addr_length != 0) { |
| return false; |
| } |
| |
| // Parse the DNS server addresses. |
| std::vector<IPAddress> dns_server_addresses; |
| while (length > 0) { |
| dns_server_addresses.push_back( |
| IPAddress(IPAddress::kFamilyIPv6, ByteString(data, addr_length))); |
| length -= addr_length; |
| data += addr_length; |
| } |
| set_rdnss_option(RdnssOption(lifetime, dns_server_addresses)); |
| return true; |
| } |
| |
| bool RTNLMessage::DecodeNeighbor(const RTNLHeader* hdr, |
| rtattr** attr_data, |
| int* attr_length) { |
| if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ndm))) { |
| return false; |
| } |
| |
| interface_index_ = hdr->ndm.ndm_ifindex; |
| family_ = hdr->ndm.ndm_family; |
| type_ = kTypeNeighbor; |
| |
| *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr)); |
| *attr_length = RTM_PAYLOAD(&hdr->hdr); |
| |
| set_neighbor_status(NeighborStatus(hdr->ndm.ndm_state, hdr->ndm.ndm_flags, |
| hdr->ndm.ndm_type)); |
| return true; |
| } |
| |
| ByteString RTNLMessage::Encode() const { |
| if (type_ != kTypeLink && type_ != kTypeAddress && type_ != kTypeRoute && |
| type_ != kTypeRule && type_ != kTypeNeighbor) { |
| return ByteString(); |
| } |
| |
| 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 ByteString(); |
| } |
| break; |
| |
| case kTypeAddress: |
| if (!EncodeAddress(&hdr)) { |
| return ByteString(); |
| } |
| break; |
| |
| case kTypeRoute: |
| case kTypeRule: |
| if (!EncodeRoute(&hdr)) { |
| return ByteString(); |
| } |
| break; |
| |
| case kTypeNeighbor: |
| if (!EncodeNeighbor(&hdr)) { |
| return ByteString(); |
| } |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| |
| if (mode_ == kModeGet) { |
| hdr.hdr.nlmsg_flags |= NLM_F_REQUEST | NLM_F_DUMP; |
| } |
| |
| size_t header_length = hdr.hdr.nlmsg_len; |
| ByteString attributes = PackAttrs(attributes_); |
| hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + attributes.GetLength(); |
| ByteString packet(reinterpret_cast<unsigned char*>(&hdr), header_length); |
| packet.Append(attributes); |
| |
| 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 = family_; |
| hdr->ifi.ifi_index = interface_index_; |
| hdr->ifi.ifi_type = 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 = 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 = 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 = 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 = 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; |
| } |
| |
| void RTNLMessage::Reset() { |
| type_ = kTypeUnknown; |
| mode_ = kModeUnknown; |
| flags_ = 0; |
| seq_ = 0; |
| pid_ = 0; |
| interface_index_ = 0; |
| family_ = IPAddress::kFamilyUnknown; |
| link_status_ = LinkStatus(); |
| address_status_ = AddressStatus(); |
| route_status_ = RouteStatus(); |
| neighbor_status_ = NeighborStatus(); |
| rdnss_option_ = RdnssOption(); |
| attributes_.clear(); |
| } |
| |
| uint32_t RTNLMessage::GetUint32Attribute(uint16_t attr) const { |
| uint32_t val = 0; |
| GetAttribute(attr).ConvertToCPUUInt32(&val); |
| return val; |
| } |
| |
| std::string RTNLMessage::GetStringAttribute(uint16_t attr) const { |
| if (!HasAttribute(attr)) |
| return ""; |
| ByteString bytes = GetAttribute(attr); |
| size_t len = strnlen(bytes.GetConstCString(), bytes.GetLength()); |
| return std::string(bytes.GetConstCString(), len); |
| } |
| |
| std::string RTNLMessage::GetIflaIfname() const { |
| return GetStringAttribute(IFLA_IFNAME); |
| } |
| |
| IPAddress RTNLMessage::GetIfaAddress() const { |
| return IPAddress(family_, GetAttribute(IFA_ADDRESS), |
| address_status_.prefix_len); |
| } |
| |
| uint32_t RTNLMessage::GetRtaTable() const { |
| return GetUint32Attribute(RTA_TABLE); |
| } |
| |
| IPAddress RTNLMessage::GetRtaDst() const { |
| return IPAddress(family_, GetAttribute(RTA_DST), route_status_.dst_prefix); |
| } |
| |
| IPAddress RTNLMessage::GetRtaSrc() const { |
| return IPAddress(family_, GetAttribute(RTA_SRC), route_status_.src_prefix); |
| } |
| |
| IPAddress RTNLMessage::GetRtaGateway() const { |
| return IPAddress(family_, GetAttribute(RTA_GATEWAY)); |
| } |
| |
| 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); |
| } |
| |
| IPAddress RTNLMessage::GetFraSrc() const { |
| return IPAddress(family_, GetAttribute(FRA_SRC), route_status_.src_prefix); |
| } |
| |
| IPAddress RTNLMessage::GetFraDst() const { |
| return IPAddress(family_, GetAttribute(FRA_DST), route_status_.dst_prefix); |
| } |
| |
| 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); |
| } |
| |
| // 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"; |
| default: |
| return "UnknownType"; |
| } |
| } |
| |
| std::string RTNLMessage::ToString() const { |
| // Include the space separator in |ip_family| to avoid double spaces for |
| // messages with family AF_UNSPEC. |
| std::string ip_family = " " + IPAddress::GetAddressFamilyName(family()); |
| std::string details; |
| switch (type()) { |
| case RTNLMessage::kTypeLink: |
| ip_family = ""; |
| details = base::StringPrintf( |
| "%s[%d] type %s flags <%s> change %X", GetIflaIfname().c_str(), |
| interface_index_, kNetDeviceTypes[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: |
| details = base::StringPrintf( |
| "%s/%d if %s[%d] flags %s scope %d", |
| GetIfaAddress().ToString().c_str(), address_status_.prefix_len, |
| IndexToName(interface_index_).c_str(), interface_index_, |
| address_status_.flags |
| ? PrintFlags(address_status_.flags, kIfaFlags).c_str() |
| : "0", |
| address_status_.scope); |
| break; |
| case RTNLMessage::kTypeRoute: |
| if (HasAttribute(RTA_SRC)) |
| details += |
| base::StringPrintf("src %s/%d ", GetRtaSrc().ToString().c_str(), |
| route_status_.src_prefix); |
| if (HasAttribute(RTA_DST)) |
| details += |
| base::StringPrintf("dst %s/%d ", GetRtaDst().ToString().c_str(), |
| route_status_.dst_prefix); |
| if (HasAttribute(RTA_GATEWAY)) |
| details += "via " + GetRtaGateway().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(), |
| kRouteTypes[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 (HasAttribute(FRA_SRC)) |
| details += |
| base::StringPrintf("src %s/%d ", GetFraSrc().ToString().c_str(), |
| route_status_.src_prefix); |
| if (HasAttribute(FRA_DST)) |
| details += |
| base::StringPrintf("dst %s/%d ", GetFraDst().ToString().c_str(), |
| route_status_.dst_prefix); |
| 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(), |
| kRuleActions[route_status_.type].c_str(), |
| route_status_.flags); |
| break; |
| case RTNLMessage::kTypeRdnss: |
| case RTNLMessage::kTypeDnssl: |
| details = rdnss_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.c_str(), TypeToString(type()).c_str(), |
| details.c_str()); |
| } |
| |
| } // namespace shill |