blob: 83c790d6fb552bd3f660dbd34230fff14fc87f84 [file] [log] [blame]
// 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