526 lines
14 KiB
C++
526 lines
14 KiB
C++
//
|
|
// Copyright (C) 2012 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include "shill/net/rtnl_message.h"
|
|
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <base/logging.h>
|
|
|
|
#include "shill/net/ndisc.h"
|
|
|
|
namespace shill {
|
|
|
|
struct RTNLHeader {
|
|
RTNLHeader() {
|
|
memset(this, 0, sizeof(*this));
|
|
}
|
|
struct nlmsghdr hdr;
|
|
union {
|
|
struct ifinfomsg ifi;
|
|
struct ifaddrmsg ifa;
|
|
struct rtmsg rtm;
|
|
struct rtgenmsg gen;
|
|
struct nduseroptmsg nd_user_opt;
|
|
struct ndmsg ndm;
|
|
};
|
|
};
|
|
|
|
RTNLMessage::RTNLMessage()
|
|
: type_(kTypeUnknown),
|
|
mode_(kModeUnknown),
|
|
flags_(0),
|
|
seq_(0),
|
|
pid_(0),
|
|
interface_index_(0),
|
|
family_(IPAddress::kFamilyUnknown) {}
|
|
|
|
RTNLMessage::RTNLMessage(Type type,
|
|
Mode mode,
|
|
unsigned int flags,
|
|
uint32_t seq,
|
|
uint32_t pid,
|
|
int 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 mode = kModeUnknown;
|
|
switch (hdr->hdr.nlmsg_type) {
|
|
case RTM_NEWLINK:
|
|
case RTM_NEWADDR:
|
|
case RTM_NEWROUTE:
|
|
case RTM_NEWNDUSEROPT:
|
|
case RTM_NEWNEIGH:
|
|
mode = kModeAdd;
|
|
break;
|
|
|
|
case RTM_DELLINK:
|
|
case RTM_DELADDR:
|
|
case RTM_DELROUTE:
|
|
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, mode, &attr_data, &attr_length))
|
|
return false;
|
|
break;
|
|
|
|
case RTM_NEWADDR:
|
|
case RTM_DELADDR:
|
|
if (!DecodeAddress(hdr, mode, &attr_data, &attr_length))
|
|
return false;
|
|
break;
|
|
|
|
case RTM_NEWROUTE:
|
|
case RTM_DELROUTE:
|
|
if (!DecodeRoute(hdr, mode, &attr_data, &attr_length))
|
|
return false;
|
|
break;
|
|
|
|
case RTM_NEWNDUSEROPT:
|
|
if (!DecodeNdUserOption(hdr, mode, &attr_data, &attr_length))
|
|
return false;
|
|
break;
|
|
|
|
case RTM_NEWNEIGH:
|
|
case RTM_DELNEIGH:
|
|
if (!DecodeNeighbor(hdr, mode, &attr_data, &attr_length))
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
|
|
flags_ = hdr->hdr.nlmsg_flags;
|
|
seq_ = hdr->hdr.nlmsg_seq;
|
|
pid_ = hdr->hdr.nlmsg_pid;
|
|
|
|
while (attr_data && RTA_OK(attr_data, attr_length)) {
|
|
SetAttribute(
|
|
attr_data->rta_type,
|
|
ByteString(reinterpret_cast<unsigned char*>(RTA_DATA(attr_data)),
|
|
RTA_PAYLOAD(attr_data)));
|
|
attr_data = RTA_NEXT(attr_data, attr_length);
|
|
}
|
|
|
|
if (attr_length) {
|
|
// We hit a parse error while going through the attributes
|
|
attributes_.clear();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RTNLMessage::DecodeLink(const RTNLHeader* hdr,
|
|
Mode mode,
|
|
rtattr** attr_data,
|
|
int* attr_length) {
|
|
if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
|
|
return false;
|
|
}
|
|
|
|
mode_ = mode;
|
|
*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;
|
|
set_link_status(LinkStatus(hdr->ifi.ifi_type,
|
|
hdr->ifi.ifi_flags,
|
|
hdr->ifi.ifi_change));
|
|
return true;
|
|
}
|
|
|
|
bool RTNLMessage::DecodeAddress(const RTNLHeader* hdr,
|
|
Mode mode,
|
|
rtattr** attr_data,
|
|
int* attr_length) {
|
|
if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
|
|
return false;
|
|
}
|
|
mode_ = mode;
|
|
*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,
|
|
Mode mode,
|
|
rtattr** attr_data,
|
|
int* attr_length) {
|
|
if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
|
|
return false;
|
|
}
|
|
mode_ = mode;
|
|
*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::DecodeNdUserOption(const RTNLHeader* hdr,
|
|
Mode mode,
|
|
rtattr** attr_data,
|
|
int* attr_length) {
|
|
if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->nd_user_opt))) {
|
|
return false;
|
|
}
|
|
|
|
mode_ = mode;
|
|
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 (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,
|
|
Mode mode,
|
|
rtattr** attr_data,
|
|
int* attr_length) {
|
|
if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ndm))) {
|
|
return false;
|
|
}
|
|
|
|
mode_ = mode;
|
|
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_ != kTypeNeighbor) {
|
|
return ByteString();
|
|
}
|
|
|
|
RTNLHeader hdr;
|
|
hdr.hdr.nlmsg_flags = flags_;
|
|
hdr.hdr.nlmsg_seq = seq_;
|
|
hdr.hdr.nlmsg_pid = pid_;
|
|
|
|
if (mode_ == kModeGet) {
|
|
if (type_ == kTypeLink) {
|
|
hdr.hdr.nlmsg_type = RTM_GETLINK;
|
|
} else if (type_ == kTypeAddress) {
|
|
hdr.hdr.nlmsg_type = RTM_GETADDR;
|
|
} else if (type_ == kTypeRoute) {
|
|
hdr.hdr.nlmsg_type = RTM_GETROUTE;
|
|
} else if (type_ == kTypeNeighbor) {
|
|
hdr.hdr.nlmsg_type = RTM_GETNEIGH;
|
|
} else {
|
|
NOTIMPLEMENTED();
|
|
return ByteString();
|
|
}
|
|
hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
|
|
hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
|
hdr.gen.rtgen_family = family_;
|
|
} else {
|
|
switch (type_) {
|
|
case kTypeLink:
|
|
if (!EncodeLink(&hdr)) {
|
|
return ByteString();
|
|
}
|
|
break;
|
|
|
|
case kTypeAddress:
|
|
if (!EncodeAddress(&hdr)) {
|
|
return ByteString();
|
|
}
|
|
break;
|
|
|
|
case kTypeRoute:
|
|
if (!EncodeRoute(&hdr)) {
|
|
return ByteString();
|
|
}
|
|
break;
|
|
|
|
case kTypeNeighbor:
|
|
if (!EncodeNeighbor(&hdr)) {
|
|
return ByteString();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
|
|
size_t header_length = hdr.hdr.nlmsg_len;
|
|
ByteString attributes;
|
|
|
|
for (auto attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
|
|
size_t len = RTA_LENGTH(attr->second.GetLength());
|
|
hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
|
|
|
|
struct rtattr rt_attr = {
|
|
static_cast<unsigned short>(len), // NOLINT(runtime/int)
|
|
attr->first
|
|
};
|
|
ByteString attr_header(reinterpret_cast<unsigned char*>(&rt_attr),
|
|
sizeof(rt_attr));
|
|
attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
|
|
attributes.Append(attr_header);
|
|
|
|
ByteString attr_data(attr->second);
|
|
attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
|
|
attributes.Append(attr_data);
|
|
}
|
|
|
|
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 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 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 {
|
|
switch (mode_) {
|
|
case kModeAdd:
|
|
hdr->hdr.nlmsg_type = RTM_NEWROUTE;
|
|
break;
|
|
case kModeDelete:
|
|
hdr->hdr.nlmsg_type = RTM_DELROUTE;
|
|
break;
|
|
case kModeQuery:
|
|
hdr->hdr.nlmsg_type = RTM_GETROUTE;
|
|
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 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() {
|
|
mode_ = kModeUnknown;
|
|
type_ = kTypeUnknown;
|
|
flags_ = 0;
|
|
seq_ = 0;
|
|
pid_ = 0;
|
|
interface_index_ = 0;
|
|
family_ = IPAddress::kFamilyUnknown;
|
|
link_status_ = LinkStatus();
|
|
address_status_ = AddressStatus();
|
|
route_status_ = RouteStatus();
|
|
attributes_.clear();
|
|
}
|
|
|
|
} // namespace shill
|