842 lines
30 KiB
C++
842 lines
30 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/netlink_manager.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <queue>
|
|
|
|
#include <base/location.h>
|
|
#include <base/logging.h>
|
|
#include <base/memory/weak_ptr.h>
|
|
#include <base/message_loop/message_loop.h>
|
|
#include <base/stl_util.h>
|
|
|
|
#include "shill/net/attribute_list.h"
|
|
#include "shill/net/generic_netlink_message.h"
|
|
#include "shill/net/io_handler.h"
|
|
#include "shill/net/netlink_message.h"
|
|
#include "shill/net/netlink_packet.h"
|
|
#include "shill/net/nl80211_message.h"
|
|
#include "shill/net/shill_time.h"
|
|
#include "shill/net/sockets.h"
|
|
|
|
using base::Bind;
|
|
using base::LazyInstance;
|
|
using base::MessageLoop;
|
|
using std::list;
|
|
using std::map;
|
|
using std::string;
|
|
|
|
namespace shill {
|
|
|
|
namespace {
|
|
LazyInstance<NetlinkManager> g_netlink_manager = LAZY_INSTANCE_INITIALIZER;
|
|
} // namespace
|
|
|
|
const char NetlinkManager::kEventTypeConfig[] = "config";
|
|
const char NetlinkManager::kEventTypeScan[] = "scan";
|
|
const char NetlinkManager::kEventTypeRegulatory[] = "regulatory";
|
|
const char NetlinkManager::kEventTypeMlme[] = "mlme";
|
|
const long NetlinkManager::kMaximumNewFamilyWaitSeconds = 1; // NOLINT
|
|
const long NetlinkManager::kMaximumNewFamilyWaitMicroSeconds = 0; // NOLINT
|
|
const long NetlinkManager::kResponseTimeoutSeconds = 5; // NOLINT
|
|
const long NetlinkManager::kResponseTimeoutMicroSeconds = 0; // NOLINT
|
|
const long NetlinkManager::kPendingDumpTimeoutMilliseconds = 500; // NOLINT
|
|
const long NetlinkManager::kNlMessageRetryDelayMilliseconds = 300; // NOLINT
|
|
const int NetlinkManager::kMaxNlMessageRetries = 1; // NOLINT
|
|
|
|
NetlinkManager::NetlinkResponseHandler::NetlinkResponseHandler(
|
|
const NetlinkManager::NetlinkAckHandler& ack_handler,
|
|
const NetlinkManager::NetlinkAuxilliaryMessageHandler& error_handler)
|
|
: ack_handler_(ack_handler),
|
|
error_handler_(error_handler) {}
|
|
|
|
NetlinkManager::NetlinkResponseHandler::~NetlinkResponseHandler() {}
|
|
|
|
void NetlinkManager::NetlinkResponseHandler::HandleError(
|
|
AuxilliaryMessageType type, const NetlinkMessage* netlink_message) const {
|
|
if (!error_handler_.is_null())
|
|
error_handler_.Run(type, netlink_message);
|
|
}
|
|
|
|
bool NetlinkManager::NetlinkResponseHandler::HandleAck() const {
|
|
if (!ack_handler_.is_null()) {
|
|
// Default behavior is not to remove callbacks. In the case where the
|
|
// callback is not successfully invoked, this is safe as it does not
|
|
// prevent any further responses from behind handled.
|
|
bool remove_callbacks = false;
|
|
ack_handler_.Run(&remove_callbacks);
|
|
// If there are no other handlers other than the Ack handler, then force
|
|
// the callback to be removed after handling the Ack.
|
|
return remove_callbacks || error_handler_.is_null();
|
|
} else {
|
|
// If there is no Ack handler, do not delete registered callbacks
|
|
// for this function because we are not explicitly told to do so.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class ControlResponseHandler : public NetlinkManager::NetlinkResponseHandler {
|
|
public:
|
|
ControlResponseHandler(
|
|
const NetlinkManager::NetlinkAckHandler& ack_handler,
|
|
const NetlinkManager::NetlinkAuxilliaryMessageHandler& error_handler,
|
|
const NetlinkManager::ControlNetlinkMessageHandler& handler)
|
|
: NetlinkManager::NetlinkResponseHandler(ack_handler, error_handler),
|
|
handler_(handler) {}
|
|
|
|
bool HandleMessage(const NetlinkMessage& netlink_message) const override {
|
|
if (netlink_message.message_type() !=
|
|
ControlNetlinkMessage::GetMessageType()) {
|
|
LOG(ERROR) << "Message is type " << netlink_message.message_type()
|
|
<< ", not " << ControlNetlinkMessage::GetMessageType()
|
|
<< " (Control).";
|
|
return false;
|
|
}
|
|
if (!handler_.is_null()) {
|
|
const ControlNetlinkMessage* message =
|
|
static_cast<const ControlNetlinkMessage*>(&netlink_message);
|
|
handler_.Run(*message);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HandleAck() const override {
|
|
if (handler_.is_null()) {
|
|
return NetlinkManager::NetlinkResponseHandler::HandleAck();
|
|
} else {
|
|
bool remove_callbacks = false;
|
|
NetlinkManager::NetlinkResponseHandler::ack_handler_.Run(
|
|
&remove_callbacks);
|
|
return remove_callbacks;
|
|
}
|
|
}
|
|
|
|
private:
|
|
NetlinkManager::ControlNetlinkMessageHandler handler_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ControlResponseHandler);
|
|
};
|
|
|
|
class Nl80211ResponseHandler : public NetlinkManager::NetlinkResponseHandler {
|
|
public:
|
|
Nl80211ResponseHandler(
|
|
const NetlinkManager::NetlinkAckHandler& ack_handler,
|
|
const NetlinkManager::NetlinkAuxilliaryMessageHandler& error_handler,
|
|
const NetlinkManager::Nl80211MessageHandler& handler)
|
|
: NetlinkManager::NetlinkResponseHandler(ack_handler, error_handler),
|
|
handler_(handler) {}
|
|
|
|
bool HandleMessage(const NetlinkMessage& netlink_message) const override {
|
|
if (netlink_message.message_type() != Nl80211Message::GetMessageType()) {
|
|
LOG(ERROR) << "Message is type " << netlink_message.message_type()
|
|
<< ", not " << Nl80211Message::GetMessageType()
|
|
<< " (Nl80211).";
|
|
return false;
|
|
}
|
|
if (!handler_.is_null()) {
|
|
const Nl80211Message* message =
|
|
static_cast<const Nl80211Message*>(&netlink_message);
|
|
handler_.Run(*message);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HandleAck() const override {
|
|
if (handler_.is_null()) {
|
|
return NetlinkManager::NetlinkResponseHandler::HandleAck();
|
|
} else {
|
|
bool remove_callbacks = false;
|
|
NetlinkManager::NetlinkResponseHandler::ack_handler_.Run(
|
|
&remove_callbacks);
|
|
return remove_callbacks;
|
|
}
|
|
}
|
|
|
|
private:
|
|
NetlinkManager::Nl80211MessageHandler handler_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Nl80211ResponseHandler);
|
|
};
|
|
|
|
|
|
NetlinkManager::MessageType::MessageType() :
|
|
family_id(NetlinkMessage::kIllegalMessageType) {}
|
|
|
|
NetlinkManager::NetlinkManager()
|
|
: weak_ptr_factory_(this),
|
|
dispatcher_callback_(Bind(&NetlinkManager::OnRawNlMessageReceived,
|
|
weak_ptr_factory_.GetWeakPtr())),
|
|
time_(Time::GetInstance()),
|
|
io_handler_factory_(
|
|
IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
|
|
dump_pending_(false) {}
|
|
|
|
NetlinkManager::~NetlinkManager() {}
|
|
|
|
NetlinkManager* NetlinkManager::GetInstance() {
|
|
return g_netlink_manager.Pointer();
|
|
}
|
|
|
|
void NetlinkManager::Reset(bool full) {
|
|
ClearBroadcastHandlers();
|
|
message_handlers_.clear();
|
|
message_types_.clear();
|
|
while (!pending_messages_.empty()) {
|
|
pending_messages_.pop();
|
|
}
|
|
pending_dump_timeout_callback_.Cancel();
|
|
resend_dump_message_callback_.Cancel();
|
|
dump_pending_ = false;
|
|
if (full) {
|
|
sock_.reset();
|
|
}
|
|
}
|
|
|
|
void NetlinkManager::OnNewFamilyMessage(const ControlNetlinkMessage& message) {
|
|
uint16_t family_id;
|
|
string family_name;
|
|
|
|
if (!message.const_attributes()->GetU16AttributeValue(CTRL_ATTR_FAMILY_ID,
|
|
&family_id)) {
|
|
LOG(ERROR) << __func__ << ": Couldn't get family_id attribute";
|
|
return;
|
|
}
|
|
|
|
if (!message.const_attributes()->GetStringAttributeValue(
|
|
CTRL_ATTR_FAMILY_NAME, &family_name)) {
|
|
LOG(ERROR) << __func__ << ": Couldn't get family_name attribute";
|
|
return;
|
|
}
|
|
|
|
VLOG(3) << "Socket family '" << family_name << "' has id=" << family_id;
|
|
|
|
// Extract the available multicast groups from the message.
|
|
AttributeListConstRefPtr multicast_groups;
|
|
if (message.const_attributes()->ConstGetNestedAttributeList(
|
|
CTRL_ATTR_MCAST_GROUPS, &multicast_groups)) {
|
|
AttributeListConstRefPtr current_group;
|
|
|
|
for (int i = 1;
|
|
multicast_groups->ConstGetNestedAttributeList(i, ¤t_group);
|
|
++i) {
|
|
string group_name;
|
|
uint32_t group_id;
|
|
if (!current_group->GetStringAttributeValue(CTRL_ATTR_MCAST_GRP_NAME,
|
|
&group_name)) {
|
|
LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_NAME, found none";
|
|
continue;
|
|
}
|
|
if (!current_group->GetU32AttributeValue(CTRL_ATTR_MCAST_GRP_ID,
|
|
&group_id)) {
|
|
LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_ID, found none";
|
|
continue;
|
|
}
|
|
VLOG(3) << " Adding group '" << group_name << "' = " << group_id;
|
|
message_types_[family_name].groups[group_name] = group_id;
|
|
}
|
|
}
|
|
|
|
message_types_[family_name].family_id = family_id;
|
|
}
|
|
|
|
// static
|
|
void NetlinkManager::OnNetlinkMessageError(AuxilliaryMessageType type,
|
|
const NetlinkMessage* raw_message) {
|
|
switch (type) {
|
|
case kErrorFromKernel:
|
|
if (!raw_message) {
|
|
LOG(ERROR) << "Unknown error from kernel.";
|
|
break;
|
|
}
|
|
if (raw_message->message_type() == ErrorAckMessage::GetMessageType()) {
|
|
const ErrorAckMessage* error_ack_message =
|
|
static_cast<const ErrorAckMessage*>(raw_message);
|
|
// error_ack_message->error() should be non-zero (i.e. not an ACK),
|
|
// since ACKs would be routed to a NetlinkAckHandler in
|
|
// NetlinkManager::OnNlMessageReceived.
|
|
LOG(ERROR) << __func__
|
|
<< ": Message (seq: " << error_ack_message->sequence_number()
|
|
<< ") failed: " << error_ack_message->ToString();
|
|
}
|
|
break;
|
|
|
|
case kUnexpectedResponseType:
|
|
LOG(ERROR) << "Message not handled by regular message handler:";
|
|
if (raw_message) {
|
|
raw_message->Print(0, 0);
|
|
}
|
|
break;
|
|
|
|
case kTimeoutWaitingForResponse:
|
|
LOG(WARNING) << "Timeout waiting for response";
|
|
break;
|
|
|
|
default:
|
|
LOG(ERROR) << "Unexpected auxilliary message type: " << type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool NetlinkManager::Init() {
|
|
// Install message factory for control class of messages, which has
|
|
// statically-known message type.
|
|
message_factory_.AddFactoryMethod(
|
|
ControlNetlinkMessage::kMessageType,
|
|
Bind(&ControlNetlinkMessage::CreateMessage));
|
|
if (!sock_) {
|
|
sock_.reset(new NetlinkSocket);
|
|
if (!sock_) {
|
|
LOG(ERROR) << "No memory";
|
|
return false;
|
|
}
|
|
|
|
if (!sock_->Init()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void NetlinkManager::Start() {
|
|
// Create an IO handler for receiving messages on the netlink socket.
|
|
// IO handler will be installed to the current message loop.
|
|
dispatcher_handler_.reset(io_handler_factory_->CreateIOInputHandler(
|
|
file_descriptor(),
|
|
dispatcher_callback_,
|
|
Bind(&NetlinkManager::OnReadError, weak_ptr_factory_.GetWeakPtr())));
|
|
}
|
|
|
|
int NetlinkManager::file_descriptor() const {
|
|
return (sock_ ? sock_->file_descriptor() : Sockets::kInvalidFileDescriptor);
|
|
}
|
|
|
|
uint16_t NetlinkManager::GetFamily(const string& name,
|
|
const NetlinkMessageFactory::FactoryMethod& message_factory) {
|
|
MessageType& message_type = message_types_[name];
|
|
if (message_type.family_id != NetlinkMessage::kIllegalMessageType) {
|
|
return message_type.family_id;
|
|
}
|
|
if (!sock_) {
|
|
LOG(FATAL) << "Must call |Init| before this method.";
|
|
return false;
|
|
}
|
|
|
|
GetFamilyMessage msg;
|
|
if (!msg.attributes()->SetStringAttributeValue(CTRL_ATTR_FAMILY_NAME, name)) {
|
|
LOG(ERROR) << "Couldn't set string attribute";
|
|
return false;
|
|
}
|
|
SendControlMessage(&msg,
|
|
Bind(&NetlinkManager::OnNewFamilyMessage,
|
|
weak_ptr_factory_.GetWeakPtr()),
|
|
Bind(&NetlinkManager::OnAckDoNothing),
|
|
Bind(&NetlinkManager::OnNetlinkMessageError));
|
|
|
|
// Wait for a response. The code absolutely needs family_ids for its
|
|
// message types so we do a synchronous wait. It's OK to do this because
|
|
// a) libnl does a synchronous wait (so there's prior art), b) waiting
|
|
// asynchronously would add significant and unnecessary complexity to the
|
|
// code that deals with pending messages that could, potentially, be waiting
|
|
// for a message type, and c) it really doesn't take very long for the
|
|
// GETFAMILY / NEWFAMILY transaction to transpire (this transaction was timed
|
|
// over 20 times and found a maximum duration of 11.1 microseconds and an
|
|
// average of 4.0 microseconds).
|
|
struct timeval now, end_time;
|
|
struct timeval maximum_wait_duration = {kMaximumNewFamilyWaitSeconds,
|
|
kMaximumNewFamilyWaitMicroSeconds};
|
|
time_->GetTimeMonotonic(&now);
|
|
timeradd(&now, &maximum_wait_duration, &end_time);
|
|
|
|
do {
|
|
// Wait with timeout for a message from the netlink socket.
|
|
fd_set read_fds;
|
|
FD_ZERO(&read_fds);
|
|
|
|
int socket = file_descriptor();
|
|
if (socket >= FD_SETSIZE)
|
|
LOG(FATAL) << "Invalid file_descriptor.";
|
|
FD_SET(socket, &read_fds);
|
|
|
|
struct timeval wait_duration;
|
|
timersub(&end_time, &now, &wait_duration);
|
|
int result = sock_->sockets()->Select(file_descriptor() + 1,
|
|
&read_fds,
|
|
nullptr,
|
|
nullptr,
|
|
&wait_duration);
|
|
if (result < 0) {
|
|
PLOG(ERROR) << "Select failed";
|
|
return NetlinkMessage::kIllegalMessageType;
|
|
}
|
|
if (result == 0) {
|
|
LOG(WARNING) << "Timed out waiting for family_id for family '"
|
|
<< name << "'.";
|
|
return NetlinkMessage::kIllegalMessageType;
|
|
}
|
|
|
|
// Read and process any messages.
|
|
ByteString received;
|
|
sock_->RecvMessage(&received);
|
|
InputData input_data(received.GetData(), received.GetLength());
|
|
OnRawNlMessageReceived(&input_data);
|
|
if (message_type.family_id != NetlinkMessage::kIllegalMessageType) {
|
|
uint16_t family_id = message_type.family_id;
|
|
if (family_id != NetlinkMessage::kIllegalMessageType) {
|
|
message_factory_.AddFactoryMethod(family_id, message_factory);
|
|
}
|
|
return message_type.family_id;
|
|
}
|
|
time_->GetTimeMonotonic(&now);
|
|
} while (timercmp(&now, &end_time, <));
|
|
|
|
LOG(ERROR) << "Timed out waiting for family_id for family '" << name << "'.";
|
|
return NetlinkMessage::kIllegalMessageType;
|
|
}
|
|
|
|
bool NetlinkManager::AddBroadcastHandler(const NetlinkMessageHandler& handler) {
|
|
if (FindBroadcastHandler(handler)) {
|
|
LOG(WARNING) << "Trying to re-add a handler";
|
|
return false; // Should only be one copy in the list.
|
|
}
|
|
if (handler.is_null()) {
|
|
LOG(WARNING) << "Trying to add a NULL handler";
|
|
return false;
|
|
}
|
|
// And add the handler to the list.
|
|
VLOG(3) << "NetlinkManager::" << __func__ << " - adding handler";
|
|
broadcast_handlers_.push_back(handler);
|
|
return true;
|
|
}
|
|
|
|
bool NetlinkManager::RemoveBroadcastHandler(
|
|
const NetlinkMessageHandler& handler) {
|
|
list<NetlinkMessageHandler>::iterator i;
|
|
for (i = broadcast_handlers_.begin(); i != broadcast_handlers_.end(); ++i) {
|
|
if ((*i).Equals(handler)) {
|
|
broadcast_handlers_.erase(i);
|
|
// Should only be one copy in the list so we don't have to continue
|
|
// looking for another one.
|
|
return true;
|
|
}
|
|
}
|
|
LOG(WARNING) << "NetlinkMessageHandler not found.";
|
|
return false;
|
|
}
|
|
|
|
bool NetlinkManager::FindBroadcastHandler(const NetlinkMessageHandler& handler)
|
|
const {
|
|
for (const auto& broadcast_handler : broadcast_handlers_) {
|
|
if (broadcast_handler.Equals(handler)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void NetlinkManager::ClearBroadcastHandlers() {
|
|
broadcast_handlers_.clear();
|
|
}
|
|
|
|
bool NetlinkManager::SendControlMessage(
|
|
ControlNetlinkMessage* message,
|
|
const ControlNetlinkMessageHandler& message_handler,
|
|
const NetlinkAckHandler& ack_handler,
|
|
const NetlinkAuxilliaryMessageHandler& error_handler) {
|
|
return SendOrPostMessage(message,
|
|
new ControlResponseHandler(ack_handler,
|
|
error_handler,
|
|
message_handler));
|
|
}
|
|
|
|
bool NetlinkManager::SendNl80211Message(
|
|
Nl80211Message* message,
|
|
const Nl80211MessageHandler& message_handler,
|
|
const NetlinkAckHandler& ack_handler,
|
|
const NetlinkAuxilliaryMessageHandler& error_handler) {
|
|
return SendOrPostMessage(message,
|
|
new Nl80211ResponseHandler(ack_handler,
|
|
error_handler,
|
|
message_handler));
|
|
}
|
|
|
|
bool NetlinkManager::SendOrPostMessage(
|
|
NetlinkMessage* message,
|
|
NetlinkManager::NetlinkResponseHandler* response_handler) {
|
|
if (!message) {
|
|
LOG(ERROR) << "Message is NULL.";
|
|
return false;
|
|
}
|
|
|
|
const uint32_t sequence_number = this->GetSequenceNumber();
|
|
const bool is_dump_msg = message->flags() & NLM_F_DUMP;
|
|
NetlinkPendingMessage pending_message(
|
|
sequence_number, is_dump_msg, message->Encode(sequence_number),
|
|
NetlinkResponseHandlerRefPtr(response_handler));
|
|
|
|
// TODO(samueltan): print this debug message above the actual call to
|
|
// NetlinkSocket::SendMessage in NetlinkManager::SendMessageInternal.
|
|
VLOG(5) << "NL Message " << pending_message.sequence_number << " to send ("
|
|
<< pending_message.message_string.GetLength() << " bytes) ===>";
|
|
message->Print(6, 7);
|
|
NetlinkMessage::PrintBytes(8, pending_message.message_string.GetConstData(),
|
|
pending_message.message_string.GetLength());
|
|
|
|
if (is_dump_msg) {
|
|
pending_messages_.push(pending_message);
|
|
if (IsDumpPending()) {
|
|
VLOG(5) << "Dump pending -- will send message after dump is complete";
|
|
return true;
|
|
}
|
|
}
|
|
return RegisterHandlersAndSendMessage(pending_message);
|
|
}
|
|
|
|
bool NetlinkManager::RegisterHandlersAndSendMessage(
|
|
const NetlinkPendingMessage& pending_message) {
|
|
// Clean out timed-out message handlers. The list of outstanding messages
|
|
// should be small so the time wasted by looking through all of them should
|
|
// be small.
|
|
struct timeval now;
|
|
time_->GetTimeMonotonic(&now);
|
|
map<uint32_t, NetlinkResponseHandlerRefPtr>::iterator handler_it =
|
|
message_handlers_.begin();
|
|
while (handler_it != message_handlers_.end()) {
|
|
if (timercmp(&now, &handler_it->second->delete_after(), >)) {
|
|
// A timeout isn't always unexpected so this is not a warning.
|
|
VLOG(3) << "Removing timed-out handler for sequence number "
|
|
<< handler_it->first;
|
|
handler_it->second->HandleError(kTimeoutWaitingForResponse, nullptr);
|
|
handler_it = message_handlers_.erase(handler_it);
|
|
} else {
|
|
++handler_it;
|
|
}
|
|
}
|
|
|
|
// Register handlers for replies to this message.
|
|
if (!pending_message.handler) {
|
|
VLOG(3) << "Handler for message was null.";
|
|
} else if (ContainsKey(message_handlers_, pending_message.sequence_number)) {
|
|
LOG(ERROR) << "A handler already existed for sequence: "
|
|
<< pending_message.sequence_number;
|
|
return false;
|
|
} else {
|
|
struct timeval response_timeout = {kResponseTimeoutSeconds,
|
|
kResponseTimeoutMicroSeconds};
|
|
struct timeval delete_after;
|
|
timeradd(&now, &response_timeout, &delete_after);
|
|
pending_message.handler->set_delete_after(delete_after);
|
|
|
|
message_handlers_[pending_message.sequence_number] =
|
|
pending_message.handler;
|
|
}
|
|
return SendMessageInternal(pending_message);
|
|
}
|
|
|
|
bool NetlinkManager::SendMessageInternal(
|
|
const NetlinkPendingMessage& pending_message) {
|
|
VLOG(5) << "Sending NL message " << pending_message.sequence_number;
|
|
|
|
if (!sock_->SendMessage(pending_message.message_string)) {
|
|
LOG(ERROR) << "Failed to send Netlink message.";
|
|
return false;
|
|
}
|
|
if (pending_message.is_dump_request) {
|
|
VLOG(5) << "Waiting for replies to NL dump message "
|
|
<< pending_message.sequence_number;
|
|
dump_pending_ = true;
|
|
pending_dump_timeout_callback_.Reset(Bind(
|
|
&NetlinkManager::OnPendingDumpTimeout, weak_ptr_factory_.GetWeakPtr()));
|
|
MessageLoop::current()->PostDelayedTask(
|
|
FROM_HERE, pending_dump_timeout_callback_.callback(),
|
|
base::TimeDelta::FromMilliseconds(kPendingDumpTimeoutMilliseconds));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NetlinkMessage::MessageContext NetlinkManager::InferMessageContext(
|
|
const NetlinkPacket& packet) {
|
|
NetlinkMessage::MessageContext context;
|
|
|
|
const uint32_t sequence_number = packet.GetMessageSequence();
|
|
if (!ContainsKey(message_handlers_, sequence_number) &&
|
|
packet.GetMessageType() != ErrorAckMessage::kMessageType) {
|
|
context.is_broadcast = true;
|
|
}
|
|
|
|
genlmsghdr genl_header;
|
|
if (packet.GetMessageType() == Nl80211Message::GetMessageType() &&
|
|
packet.GetGenlMsgHdr(&genl_header)) {
|
|
context.nl80211_cmd = genl_header.cmd;
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
void NetlinkManager::OnPendingDumpTimeout() {
|
|
VLOG(3) << "Timed out waiting for replies to NL dump message "
|
|
<< PendingDumpSequenceNumber();
|
|
CallErrorHandler(PendingDumpSequenceNumber(), kTimeoutWaitingForResponse,
|
|
nullptr);
|
|
OnPendingDumpComplete();
|
|
}
|
|
|
|
void NetlinkManager::OnPendingDumpComplete() {
|
|
VLOG(3) << __func__;
|
|
dump_pending_ = false;
|
|
pending_dump_timeout_callback_.Cancel();
|
|
resend_dump_message_callback_.Cancel();
|
|
pending_messages_.pop();
|
|
if (!pending_messages_.empty()) {
|
|
VLOG(3) << "Sending next pending message";
|
|
NetlinkPendingMessage to_send = pending_messages_.front();
|
|
RegisterHandlersAndSendMessage(to_send);
|
|
}
|
|
}
|
|
|
|
bool NetlinkManager::IsDumpPending() {
|
|
return dump_pending_ && !pending_messages_.empty();
|
|
}
|
|
|
|
uint32_t NetlinkManager::PendingDumpSequenceNumber() {
|
|
if (!IsDumpPending()) {
|
|
LOG(ERROR) << __func__ << ": no pending dump";
|
|
return 0;
|
|
}
|
|
return pending_messages_.front().sequence_number;
|
|
}
|
|
|
|
bool NetlinkManager::RemoveMessageHandler(const NetlinkMessage& message) {
|
|
if (!ContainsKey(message_handlers_, message.sequence_number())) {
|
|
return false;
|
|
}
|
|
message_handlers_.erase(message.sequence_number());
|
|
return true;
|
|
}
|
|
|
|
uint32_t NetlinkManager::GetSequenceNumber() {
|
|
return sock_ ?
|
|
sock_->GetSequenceNumber() : NetlinkMessage::kBroadcastSequenceNumber;
|
|
}
|
|
|
|
bool NetlinkManager::SubscribeToEvents(const string& family_id,
|
|
const string& group_name) {
|
|
if (!ContainsKey(message_types_, family_id)) {
|
|
LOG(ERROR) << "Family '" << family_id << "' doesn't exist";
|
|
return false;
|
|
}
|
|
|
|
if (!ContainsKey(message_types_[family_id].groups, group_name)) {
|
|
LOG(ERROR) << "Group '" << group_name << "' doesn't exist in family '"
|
|
<< family_id << "'";
|
|
return false;
|
|
}
|
|
|
|
uint32_t group_id = message_types_[family_id].groups[group_name];
|
|
if (!sock_) {
|
|
LOG(FATAL) << "Need to call |Init| first.";
|
|
}
|
|
return sock_->SubscribeToEvents(group_id);
|
|
}
|
|
|
|
void NetlinkManager::OnRawNlMessageReceived(InputData* data) {
|
|
if (!data) {
|
|
LOG(ERROR) << __func__ << "() called with null header.";
|
|
return;
|
|
}
|
|
unsigned char* buf = data->buf;
|
|
unsigned char* end = buf + data->len;
|
|
while (buf < end) {
|
|
NetlinkPacket packet(buf, end - buf);
|
|
if (!packet.IsValid()) {
|
|
break;
|
|
}
|
|
buf += packet.GetLength();
|
|
OnNlMessageReceived(&packet);
|
|
}
|
|
}
|
|
|
|
void NetlinkManager::OnNlMessageReceived(NetlinkPacket* packet) {
|
|
if (!packet) {
|
|
LOG(ERROR) << __func__ << "() called with null packet.";
|
|
return;
|
|
}
|
|
const uint32_t sequence_number = packet->GetMessageSequence();
|
|
|
|
std::unique_ptr<NetlinkMessage> message(
|
|
message_factory_.CreateMessage(packet, InferMessageContext(*packet)));
|
|
if (message == nullptr) {
|
|
VLOG(3) << "NL Message " << sequence_number << " <===";
|
|
VLOG(3) << __func__ << "(msg:NULL)";
|
|
return; // Skip current message, continue parsing buffer.
|
|
}
|
|
VLOG(5) << "NL Message " << sequence_number << " Received ("
|
|
<< packet->GetLength() << " bytes) <===";
|
|
message->Print(6, 7);
|
|
NetlinkMessage::PrintPacket(8, *packet);
|
|
|
|
bool is_error_ack_message = false;
|
|
uint32_t error_code = 0;
|
|
if (message->message_type() == ErrorAckMessage::GetMessageType()) {
|
|
is_error_ack_message = true;
|
|
const ErrorAckMessage* error_ack_message =
|
|
static_cast<const ErrorAckMessage*>(message.get());
|
|
error_code = error_ack_message->error();
|
|
}
|
|
|
|
// Note: assumes we only receive one reply to a dump request: an error
|
|
// message, an ACK, or a single multi-part reply. If we receive two replies,
|
|
// then we will stop waiting for replies after the first reply is processed
|
|
// here. This assumption should hold unless the NLM_F_ACK or NLM_F_ECHO
|
|
// flags are explicitly added to the dump request.
|
|
if (IsDumpPending() &&
|
|
(message->sequence_number() == PendingDumpSequenceNumber()) &&
|
|
!((message->flags() & NLM_F_MULTI) &&
|
|
(message->message_type() != NLMSG_DONE))) {
|
|
// Dump currently in progress, this message's sequence number matches that
|
|
// of the pending dump request, and we are not in the middle of receiving a
|
|
// multi-part reply.
|
|
if (is_error_ack_message && (error_code == static_cast<uint32_t>(-EBUSY))) {
|
|
VLOG(3) << "EBUSY reply received for NL dump message "
|
|
<< PendingDumpSequenceNumber();
|
|
if (pending_messages_.front().retries_left) {
|
|
pending_messages_.front().last_received_error = error_code;
|
|
pending_dump_timeout_callback_.Cancel();
|
|
ResendPendingDumpMessageAfterDelay();
|
|
// Since we will resend the message, do not invoke error handler.
|
|
return;
|
|
} else {
|
|
VLOG(3) << "No more resend attempts left for NL dump message "
|
|
<< PendingDumpSequenceNumber() << " -- stop waiting "
|
|
"for replies";
|
|
OnPendingDumpComplete();
|
|
}
|
|
} else {
|
|
VLOG(3) << "Reply received for NL dump message "
|
|
<< PendingDumpSequenceNumber() << " -- stop waiting for replies";
|
|
OnPendingDumpComplete();
|
|
}
|
|
}
|
|
|
|
if (is_error_ack_message) {
|
|
VLOG(3) << "Error/ACK response to message " << sequence_number;
|
|
if (error_code) {
|
|
CallErrorHandler(sequence_number, kErrorFromKernel, message.get());
|
|
} else {
|
|
if (ContainsKey(message_handlers_, sequence_number)) {
|
|
VLOG(6) << "Found message-specific ACK handler";
|
|
if (message_handlers_[sequence_number]->HandleAck()) {
|
|
VLOG(6) << "ACK handler invoked -- removing callback";
|
|
message_handlers_.erase(sequence_number);
|
|
} else {
|
|
VLOG(6) << "ACK handler invoked -- not removing callback";
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ContainsKey(message_handlers_, sequence_number)) {
|
|
VLOG(6) << "Found message-specific handler";
|
|
if ((message->flags() & NLM_F_MULTI) &&
|
|
(message->message_type() == NLMSG_DONE)) {
|
|
message_handlers_[sequence_number]->HandleError(kDone, message.get());
|
|
} else if (!message_handlers_[sequence_number]->HandleMessage(*message)) {
|
|
LOG(ERROR) << "Couldn't call message handler for " << sequence_number;
|
|
// Call the error handler but, since we don't have an |ErrorAckMessage|,
|
|
// we'll have to pass a nullptr.
|
|
message_handlers_[sequence_number]->HandleError(kUnexpectedResponseType,
|
|
nullptr);
|
|
}
|
|
if ((message->flags() & NLM_F_MULTI) &&
|
|
(message->message_type() != NLMSG_DONE)) {
|
|
VLOG(6) << "Multi-part message -- not removing callback";
|
|
} else {
|
|
VLOG(6) << "Removing callbacks";
|
|
message_handlers_.erase(sequence_number);
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (const auto& handler : broadcast_handlers_) {
|
|
VLOG(6) << "Calling broadcast handler";
|
|
if (!handler.is_null()) {
|
|
handler.Run(*message);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NetlinkManager::ResendPendingDumpMessage() {
|
|
if (!IsDumpPending()) {
|
|
VLOG(3) << "No pending dump, so do not resend dump message";
|
|
return;
|
|
}
|
|
--pending_messages_.front().retries_left;
|
|
if (SendMessageInternal(pending_messages_.front())) {
|
|
VLOG(3) << "NL message " << PendingDumpSequenceNumber()
|
|
<< " sent again successfully";
|
|
return;
|
|
}
|
|
VLOG(3) << "Failed to resend NL message " << PendingDumpSequenceNumber();
|
|
if (pending_messages_.front().retries_left) {
|
|
ResendPendingDumpMessageAfterDelay();
|
|
} else {
|
|
VLOG(3) << "No more resend attempts left for NL dump message "
|
|
<< PendingDumpSequenceNumber() << " -- stop waiting "
|
|
"for replies";
|
|
ErrorAckMessage err_message(pending_messages_.front().last_received_error);
|
|
CallErrorHandler(PendingDumpSequenceNumber(), kErrorFromKernel,
|
|
&err_message);
|
|
OnPendingDumpComplete();
|
|
}
|
|
}
|
|
|
|
void NetlinkManager::CallErrorHandler(uint32_t sequence_number,
|
|
AuxilliaryMessageType type,
|
|
const NetlinkMessage* netlink_message) {
|
|
if (ContainsKey(message_handlers_, sequence_number)) {
|
|
VLOG(6) << "Found message-specific error handler";
|
|
message_handlers_[sequence_number]->HandleError(type, netlink_message);
|
|
message_handlers_.erase(sequence_number);
|
|
}
|
|
}
|
|
|
|
void NetlinkManager::OnReadError(const string& error_msg) {
|
|
// TODO(wdg): When netlink_manager is used for scan, et al., this should
|
|
// either be LOG(FATAL) or the code should properly deal with errors,
|
|
// e.g., dropped messages due to the socket buffer being full.
|
|
LOG(ERROR) << "NetlinkManager's netlink Socket read returns error: "
|
|
<< error_msg;
|
|
}
|
|
|
|
void NetlinkManager::ResendPendingDumpMessageAfterDelay() {
|
|
VLOG(3) << "Resending NL dump message " << PendingDumpSequenceNumber()
|
|
<< " after " << kNlMessageRetryDelayMilliseconds << " ms";
|
|
resend_dump_message_callback_.Reset(
|
|
Bind(&NetlinkManager::ResendPendingDumpMessage,
|
|
weak_ptr_factory_.GetWeakPtr()));
|
|
MessageLoop::current()->PostDelayedTask(
|
|
FROM_HERE, resend_dump_message_callback_.callback(),
|
|
base::TimeDelta::FromMilliseconds(kNlMessageRetryDelayMilliseconds));
|
|
}
|
|
|
|
} // namespace shill.
|