511 lines
15 KiB
C++
511 lines
15 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_handler.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/ether.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/logging.h>
|
|
#include <base/stl_util.h>
|
|
|
|
#include "shill/net/io_handler.h"
|
|
#include "shill/net/ip_address.h"
|
|
#include "shill/net/ndisc.h"
|
|
#include "shill/net/rtnl_listener.h"
|
|
#include "shill/net/rtnl_message.h"
|
|
#include "shill/net/sockets.h"
|
|
|
|
using base::Bind;
|
|
using base::Unretained;
|
|
using std::string;
|
|
|
|
namespace shill {
|
|
|
|
// Keep this large enough to avoid overflows on IPv6 SNM routing update spikes
|
|
const int RTNLHandler::kReceiveBufferSize = 512 * 1024;
|
|
const int RTNLHandler::kInvalidSocket = -1;
|
|
const int RTNLHandler::kErrorWindowSize = 16;
|
|
|
|
namespace {
|
|
base::LazyInstance<RTNLHandler> g_rtnl_handler = LAZY_INSTANCE_INITIALIZER;
|
|
} // namespace
|
|
|
|
RTNLHandler::RTNLHandler()
|
|
: sockets_(new Sockets()),
|
|
in_request_(false),
|
|
rtnl_socket_(kInvalidSocket),
|
|
request_flags_(0),
|
|
request_sequence_(0),
|
|
last_dump_sequence_(0),
|
|
rtnl_callback_(Bind(&RTNLHandler::ParseRTNL, Unretained(this))),
|
|
io_handler_factory_(
|
|
IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()) {
|
|
error_mask_window_.resize(kErrorWindowSize);
|
|
VLOG(2) << "RTNLHandler created";
|
|
}
|
|
|
|
RTNLHandler::~RTNLHandler() {
|
|
VLOG(2) << "RTNLHandler removed";
|
|
Stop();
|
|
}
|
|
|
|
RTNLHandler* RTNLHandler::GetInstance() {
|
|
return g_rtnl_handler.Pointer();
|
|
}
|
|
|
|
void RTNLHandler::Start(uint32_t netlink_groups_mask) {
|
|
struct sockaddr_nl addr;
|
|
|
|
if (rtnl_socket_ != kInvalidSocket) {
|
|
return;
|
|
}
|
|
|
|
rtnl_socket_ = sockets_->Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
|
if (rtnl_socket_ < 0) {
|
|
LOG(ERROR) << "Failed to open rtnl socket";
|
|
return;
|
|
}
|
|
|
|
if (sockets_->SetReceiveBuffer(rtnl_socket_, kReceiveBufferSize)) {
|
|
LOG(ERROR) << "Failed to increase receive buffer size";
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.nl_family = AF_NETLINK;
|
|
addr.nl_groups = netlink_groups_mask;
|
|
|
|
if (sockets_->Bind(rtnl_socket_,
|
|
reinterpret_cast<struct sockaddr*>(&addr),
|
|
sizeof(addr)) < 0) {
|
|
sockets_->Close(rtnl_socket_);
|
|
rtnl_socket_ = kInvalidSocket;
|
|
LOG(ERROR) << "RTNL socket bind failed";
|
|
return;
|
|
}
|
|
|
|
rtnl_handler_.reset(io_handler_factory_->CreateIOInputHandler(
|
|
rtnl_socket_,
|
|
rtnl_callback_,
|
|
Bind(&RTNLHandler::OnReadError, Unretained(this))));
|
|
|
|
NextRequest(last_dump_sequence_);
|
|
VLOG(2) << "RTNLHandler started";
|
|
}
|
|
|
|
void RTNLHandler::Stop() {
|
|
rtnl_handler_.reset();
|
|
// Close the socket if it is currently open.
|
|
if (rtnl_socket_ != kInvalidSocket) {
|
|
sockets_->Close(rtnl_socket_);
|
|
rtnl_socket_ = kInvalidSocket;
|
|
}
|
|
in_request_ = false;
|
|
request_flags_ = 0;
|
|
VLOG(2) << "RTNLHandler stopped";
|
|
}
|
|
|
|
void RTNLHandler::AddListener(RTNLListener* to_add) {
|
|
for (const auto& listener : listeners_) {
|
|
if (to_add == listener)
|
|
return;
|
|
}
|
|
listeners_.push_back(to_add);
|
|
VLOG(2) << "RTNLHandler added listener";
|
|
}
|
|
|
|
void RTNLHandler::RemoveListener(RTNLListener* to_remove) {
|
|
for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
|
|
if (to_remove == *it) {
|
|
listeners_.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
VLOG(2) << "RTNLHandler removed listener";
|
|
}
|
|
|
|
void RTNLHandler::SetInterfaceFlags(int interface_index, unsigned int flags,
|
|
unsigned int change) {
|
|
if (rtnl_socket_ == kInvalidSocket) {
|
|
LOG(ERROR) << __func__ << " called while not started. "
|
|
"Assuming we are in unit tests.";
|
|
return;
|
|
}
|
|
|
|
RTNLMessage msg(
|
|
RTNLMessage::kTypeLink,
|
|
RTNLMessage::kModeAdd,
|
|
NLM_F_REQUEST,
|
|
0, // sequence to be filled in by RTNLHandler::SendMessage().
|
|
0, // pid.
|
|
interface_index,
|
|
IPAddress::kFamilyUnknown);
|
|
|
|
msg.set_link_status(RTNLMessage::LinkStatus(ARPHRD_VOID, flags, change));
|
|
|
|
ErrorMask error_mask;
|
|
if ((flags & IFF_UP) == 0) {
|
|
error_mask.insert(ENODEV);
|
|
}
|
|
|
|
SendMessageWithErrorMask(&msg, error_mask);
|
|
}
|
|
|
|
void RTNLHandler::SetInterfaceMTU(int interface_index, unsigned int mtu) {
|
|
RTNLMessage msg(
|
|
RTNLMessage::kTypeLink,
|
|
RTNLMessage::kModeAdd,
|
|
NLM_F_REQUEST,
|
|
0, // sequence to be filled in by RTNLHandler::SendMessage().
|
|
0, // pid.
|
|
interface_index,
|
|
IPAddress::kFamilyUnknown);
|
|
|
|
msg.SetAttribute(
|
|
IFLA_MTU,
|
|
ByteString(reinterpret_cast<unsigned char*>(&mtu), sizeof(mtu)));
|
|
|
|
CHECK(SendMessage(&msg));
|
|
}
|
|
|
|
void RTNLHandler::RequestDump(int request_flags) {
|
|
if (rtnl_socket_ == kInvalidSocket) {
|
|
LOG(ERROR) << __func__ << " called while not started. "
|
|
"Assuming we are in unit tests.";
|
|
return;
|
|
}
|
|
|
|
request_flags_ |= request_flags;
|
|
|
|
VLOG(2) << "RTNLHandler got request to dump "
|
|
<< std::showbase << std::hex
|
|
<< request_flags
|
|
<< std::dec << std::noshowbase;
|
|
|
|
if (!in_request_) {
|
|
NextRequest(last_dump_sequence_);
|
|
}
|
|
}
|
|
|
|
void RTNLHandler::DispatchEvent(int type, const RTNLMessage& msg) {
|
|
for (const auto& listener : listeners_) {
|
|
listener->NotifyEvent(type, msg);
|
|
}
|
|
}
|
|
|
|
void RTNLHandler::NextRequest(uint32_t seq) {
|
|
int flag = 0;
|
|
RTNLMessage::Type type;
|
|
|
|
VLOG(2) << "RTNLHandler nextrequest " << seq << " "
|
|
<< last_dump_sequence_
|
|
<< std::showbase << std::hex
|
|
<< " " << request_flags_
|
|
<< std::dec << std::noshowbase;
|
|
|
|
if (seq != last_dump_sequence_)
|
|
return;
|
|
|
|
IPAddress::Family family = IPAddress::kFamilyUnknown;
|
|
if ((request_flags_ & kRequestAddr) != 0) {
|
|
type = RTNLMessage::kTypeAddress;
|
|
flag = kRequestAddr;
|
|
} else if ((request_flags_ & kRequestRoute) != 0) {
|
|
type = RTNLMessage::kTypeRoute;
|
|
flag = kRequestRoute;
|
|
} else if ((request_flags_ & kRequestLink) != 0) {
|
|
type = RTNLMessage::kTypeLink;
|
|
flag = kRequestLink;
|
|
} else if ((request_flags_ & kRequestNeighbor) != 0) {
|
|
type = RTNLMessage::kTypeNeighbor;
|
|
flag = kRequestNeighbor;
|
|
} else if ((request_flags_ & kRequestBridgeNeighbor) != 0) {
|
|
type = RTNLMessage::kTypeNeighbor;
|
|
flag = kRequestBridgeNeighbor;
|
|
family = AF_BRIDGE;
|
|
} else {
|
|
VLOG(2) << "Done with requests";
|
|
in_request_ = false;
|
|
return;
|
|
}
|
|
|
|
RTNLMessage msg(
|
|
type,
|
|
RTNLMessage::kModeGet,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
family);
|
|
CHECK(SendMessage(&msg));
|
|
|
|
last_dump_sequence_ = msg.seq();
|
|
request_flags_ &= ~flag;
|
|
in_request_ = true;
|
|
}
|
|
|
|
void RTNLHandler::ParseRTNL(InputData* data) {
|
|
unsigned char* buf = data->buf;
|
|
unsigned char* end = buf + data->len;
|
|
|
|
while (buf < end) {
|
|
struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf);
|
|
if (!NLMSG_OK(hdr, static_cast<unsigned int>(end - buf)))
|
|
break;
|
|
|
|
VLOG(5) << __func__ << ": received payload (" << end - buf << ")";
|
|
|
|
RTNLMessage msg;
|
|
ByteString payload(reinterpret_cast<unsigned char*>(hdr), hdr->nlmsg_len);
|
|
VLOG(5) << "RTNL received payload length " << payload.GetLength()
|
|
<< ": \"" << payload.HexEncode() << "\"";
|
|
if (!msg.Decode(payload)) {
|
|
VLOG(5) << __func__ << ": rtnl packet type "
|
|
<< hdr->nlmsg_type
|
|
<< " length " << hdr->nlmsg_len
|
|
<< " sequence " << hdr->nlmsg_seq;
|
|
|
|
switch (hdr->nlmsg_type) {
|
|
case NLMSG_NOOP:
|
|
case NLMSG_OVERRUN:
|
|
break;
|
|
case NLMSG_DONE:
|
|
GetAndClearErrorMask(hdr->nlmsg_seq); // Clear any queued error mask.
|
|
NextRequest(hdr->nlmsg_seq);
|
|
break;
|
|
case NLMSG_ERROR:
|
|
{
|
|
struct nlmsgerr* err =
|
|
reinterpret_cast<nlmsgerr*>(NLMSG_DATA(hdr));
|
|
int error_number = -err->error;
|
|
std::ostringstream message;
|
|
message << "sequence " << hdr->nlmsg_seq << " received error "
|
|
<< error_number << " ("
|
|
<< strerror(error_number) << ")";
|
|
if (!ContainsValue(GetAndClearErrorMask(hdr->nlmsg_seq),
|
|
error_number)) {
|
|
LOG(ERROR) << message.str();
|
|
} else {
|
|
VLOG(3) << message.str();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
NOTIMPLEMENTED() << "Unknown NL message type.";
|
|
}
|
|
} else {
|
|
switch (msg.type()) {
|
|
case RTNLMessage::kTypeLink:
|
|
DispatchEvent(kRequestLink, msg);
|
|
break;
|
|
case RTNLMessage::kTypeAddress:
|
|
DispatchEvent(kRequestAddr, msg);
|
|
break;
|
|
case RTNLMessage::kTypeRoute:
|
|
DispatchEvent(kRequestRoute, msg);
|
|
break;
|
|
case RTNLMessage::kTypeRdnss:
|
|
DispatchEvent(kRequestRdnss, msg);
|
|
break;
|
|
case RTNLMessage::kTypeNeighbor:
|
|
DispatchEvent(kRequestNeighbor, msg);
|
|
break;
|
|
case RTNLMessage::kTypeDnssl:
|
|
NOTIMPLEMENTED();
|
|
break;
|
|
default:
|
|
NOTIMPLEMENTED() << "Unknown RTNL message type.";
|
|
}
|
|
}
|
|
buf += hdr->nlmsg_len;
|
|
}
|
|
}
|
|
|
|
bool RTNLHandler::AddressRequest(int interface_index,
|
|
RTNLMessage::Mode mode,
|
|
int flags,
|
|
const IPAddress& local,
|
|
const IPAddress& broadcast,
|
|
const IPAddress& peer) {
|
|
CHECK(local.family() == broadcast.family());
|
|
CHECK(local.family() == peer.family());
|
|
|
|
RTNLMessage msg(
|
|
RTNLMessage::kTypeAddress,
|
|
mode,
|
|
NLM_F_REQUEST | flags,
|
|
0,
|
|
0,
|
|
interface_index,
|
|
local.family());
|
|
|
|
msg.set_address_status(RTNLMessage::AddressStatus(
|
|
local.prefix(),
|
|
0,
|
|
0));
|
|
|
|
msg.SetAttribute(IFA_LOCAL, local.address());
|
|
if (!broadcast.IsDefault()) {
|
|
msg.SetAttribute(IFA_BROADCAST, broadcast.address());
|
|
}
|
|
if (!peer.IsDefault()) {
|
|
msg.SetAttribute(IFA_ADDRESS, peer.address());
|
|
}
|
|
|
|
return SendMessage(&msg);
|
|
}
|
|
|
|
bool RTNLHandler::AddInterfaceAddress(int interface_index,
|
|
const IPAddress& local,
|
|
const IPAddress& broadcast,
|
|
const IPAddress& peer) {
|
|
return AddressRequest(interface_index,
|
|
RTNLMessage::kModeAdd,
|
|
NLM_F_CREATE | NLM_F_EXCL | NLM_F_ECHO,
|
|
local,
|
|
broadcast,
|
|
peer);
|
|
}
|
|
|
|
bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
|
|
const IPAddress& local) {
|
|
return AddressRequest(interface_index,
|
|
RTNLMessage::kModeDelete,
|
|
NLM_F_ECHO,
|
|
local,
|
|
IPAddress(local.family()),
|
|
IPAddress(local.family()));
|
|
}
|
|
|
|
bool RTNLHandler::RemoveInterface(int interface_index) {
|
|
RTNLMessage msg(
|
|
RTNLMessage::kTypeLink,
|
|
RTNLMessage::kModeDelete,
|
|
NLM_F_REQUEST,
|
|
0,
|
|
0,
|
|
interface_index,
|
|
IPAddress::kFamilyUnknown);
|
|
return SendMessage(&msg);
|
|
}
|
|
|
|
int RTNLHandler::GetInterfaceIndex(const string& interface_name) {
|
|
if (interface_name.empty()) {
|
|
LOG(ERROR) << "Empty interface name -- unable to obtain index.";
|
|
return -1;
|
|
}
|
|
struct ifreq ifr;
|
|
if (interface_name.size() >= sizeof(ifr.ifr_name)) {
|
|
LOG(ERROR) << "Interface name too long: " << interface_name.size() << " >= "
|
|
<< sizeof(ifr.ifr_name);
|
|
return -1;
|
|
}
|
|
int socket = sockets_->Socket(PF_INET, SOCK_DGRAM, 0);
|
|
if (socket < 0) {
|
|
PLOG(ERROR) << "Unable to open INET socket";
|
|
return -1;
|
|
}
|
|
ScopedSocketCloser socket_closer(sockets_.get(), socket);
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name));
|
|
if (sockets_->Ioctl(socket, SIOCGIFINDEX, &ifr) < 0) {
|
|
PLOG(ERROR) << "SIOCGIFINDEX error for " << interface_name;
|
|
return -1;
|
|
}
|
|
return ifr.ifr_ifindex;
|
|
}
|
|
|
|
bool RTNLHandler::SendMessageWithErrorMask(RTNLMessage* message,
|
|
const ErrorMask& error_mask) {
|
|
VLOG(5) << __func__ << " sequence " << request_sequence_
|
|
<< " message type " << message->type()
|
|
<< " mode " << message->mode()
|
|
<< " with error mask size " << error_mask.size();
|
|
|
|
SetErrorMask(request_sequence_, error_mask);
|
|
message->set_seq(request_sequence_);
|
|
ByteString msgdata = message->Encode();
|
|
|
|
if (msgdata.GetLength() == 0) {
|
|
return false;
|
|
}
|
|
|
|
VLOG(5) << "RTNL sending payload with request sequence "
|
|
<< request_sequence_ << ", length " << msgdata.GetLength()
|
|
<< ": \"" << msgdata.HexEncode() << "\"";
|
|
|
|
request_sequence_++;
|
|
|
|
if (sockets_->Send(rtnl_socket_,
|
|
msgdata.GetConstData(),
|
|
msgdata.GetLength(),
|
|
0) < 0) {
|
|
PLOG(ERROR) << "RTNL send failed";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RTNLHandler::SendMessage(RTNLMessage* message) {
|
|
ErrorMask error_mask;
|
|
if (message->mode() == RTNLMessage::kModeAdd) {
|
|
error_mask = { EEXIST };
|
|
} else if (message->mode() == RTNLMessage::kModeDelete) {
|
|
error_mask = { ESRCH, ENODEV };
|
|
if (message->type() == RTNLMessage::kTypeAddress) {
|
|
error_mask.insert(EADDRNOTAVAIL);
|
|
}
|
|
}
|
|
return SendMessageWithErrorMask(message, error_mask);
|
|
}
|
|
|
|
bool RTNLHandler::IsSequenceInErrorMaskWindow(uint32_t sequence) {
|
|
return (request_sequence_ - sequence) < kErrorWindowSize;
|
|
}
|
|
|
|
void RTNLHandler::SetErrorMask(uint32_t sequence, const ErrorMask& error_mask) {
|
|
if (IsSequenceInErrorMaskWindow(sequence)) {
|
|
error_mask_window_[sequence % kErrorWindowSize] = error_mask;
|
|
}
|
|
}
|
|
|
|
RTNLHandler::ErrorMask RTNLHandler::GetAndClearErrorMask(uint32_t sequence) {
|
|
ErrorMask error_mask;
|
|
if (IsSequenceInErrorMaskWindow(sequence)) {
|
|
error_mask.swap(error_mask_window_[sequence % kErrorWindowSize]);
|
|
}
|
|
return error_mask;
|
|
}
|
|
|
|
void RTNLHandler::OnReadError(const string& error_msg) {
|
|
LOG(FATAL) << "RTNL Socket read returns error: "
|
|
<< error_msg;
|
|
}
|
|
|
|
} // namespace shill
|