454 lines
19 KiB
C++
454 lines
19 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.
|
|
//
|
|
|
|
// This software provides an abstracted interface to the netlink socket
|
|
// interface. In its current implementation it is used, primarily, to
|
|
// communicate with the cfg80211 kernel module and mac80211 drivers:
|
|
//
|
|
// [shill]--[nl80211 library]
|
|
// |
|
|
// (netlink socket)
|
|
// |
|
|
// [cfg80211 kernel module]
|
|
// |
|
|
// [mac80211 drivers]
|
|
//
|
|
// In order to send a message and handle it's response, do the following:
|
|
// - Create a handler (it'll want to verify that it's the kind of message you
|
|
// want, cast it to the appropriate type, and get attributes from the cast
|
|
// message):
|
|
//
|
|
// #include "nl80211_message.h"
|
|
// class SomeClass {
|
|
// static void MyMessageHandler(const NetlinkMessage& raw) {
|
|
// if (raw.message_type() != ControlNetlinkMessage::kMessageType)
|
|
// return;
|
|
// const ControlNetlinkMessage* message =
|
|
// reinterpret_cast<const ControlNetlinkMessage*>(&raw);
|
|
// if (message.command() != NewFamilyMessage::kCommand)
|
|
// return;
|
|
// uint16_t my_attribute;
|
|
// message->const_attributes()->GetU16AttributeValue(
|
|
// CTRL_ATTR_FAMILY_ID, &my_attribute);
|
|
// } // MyMessageHandler.
|
|
// } // class SomeClass.
|
|
//
|
|
// - Instantiate a message:
|
|
//
|
|
// #include "nl80211_message.h"
|
|
// GetFamilyMessage msg;
|
|
//
|
|
// - And set attributes:
|
|
//
|
|
// msg.attributes()->SetStringAttributeValue(CTRL_ATTR_FAMILY_NAME, "foo");
|
|
//
|
|
// - Then send the message, passing-in a closure to the handler you created:
|
|
//
|
|
// NetlinkManager* netlink_manager = NetlinkManager::GetInstance();
|
|
// netlink_manager->SendMessage(&msg, Bind(&SomeClass::MyMessageHandler));
|
|
//
|
|
// NetlinkManager will then save your handler and send your message. When a
|
|
// response to your message arrives, it'll call your handler.
|
|
//
|
|
|
|
#ifndef SHILL_NET_NETLINK_MANAGER_H_
|
|
#define SHILL_NET_NETLINK_MANAGER_H_
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/cancelable_callback.h>
|
|
#include <base/lazy_instance.h>
|
|
#include <base/macros.h>
|
|
#include <gtest/gtest_prod.h> // for FRIEND_TEST
|
|
|
|
#include "shill/net/generic_netlink_message.h"
|
|
#include "shill/net/io_handler_factory_container.h"
|
|
#include "shill/net/netlink_message.h"
|
|
#include "shill/net/netlink_socket.h"
|
|
#include "shill/net/shill_export.h"
|
|
#include "shill/net/shill_time.h"
|
|
|
|
namespace shill {
|
|
|
|
class ControlNetlinkMessage;
|
|
struct InputData;
|
|
class NetlinkPacket;
|
|
class Nl80211Message;
|
|
|
|
// NetlinkManager is a singleton that coordinates sending netlink messages to,
|
|
// and receiving netlink messages from, the kernel. The first use of this is
|
|
// to communicate between user-space and the cfg80211 module that manages wifi
|
|
// drivers. Bring NetlinkManager up as follows:
|
|
// NetlinkManager* netlink_manager_ = NetlinkManager::GetInstance();
|
|
// netlink_manager_->Init(); // Initialize the socket.
|
|
// // Get message types for all dynamic message types.
|
|
// Nl80211Message::SetMessageType(
|
|
// netlink_manager_->GetFamily(Nl80211Message::kMessageTypeString,
|
|
// Bind(&Nl80211Message::CreateMessage)));
|
|
// netlink_manager_->Start();
|
|
class SHILL_EXPORT NetlinkManager {
|
|
public:
|
|
enum AuxilliaryMessageType {
|
|
kDone,
|
|
kErrorFromKernel,
|
|
kTimeoutWaitingForResponse,
|
|
kUnexpectedResponseType
|
|
};
|
|
typedef base::Callback<void(const NetlinkMessage&)> NetlinkMessageHandler;
|
|
typedef base::Callback<void(const ControlNetlinkMessage&)>
|
|
ControlNetlinkMessageHandler;
|
|
typedef base::Callback<void(const Nl80211Message&)> Nl80211MessageHandler;
|
|
// NetlinkAuxilliaryMessageHandler handles netlink error messages, things
|
|
// like the DoneMessage at the end of a multi-part message, and any errors
|
|
// discovered by |NetlinkManager| (which are passed as NULL pointers because
|
|
// there is no way to reserve a part of the ErrorAckMessage space for
|
|
// non-netlink errors).
|
|
typedef base::Callback<void(AuxilliaryMessageType type,
|
|
const NetlinkMessage*)>
|
|
NetlinkAuxilliaryMessageHandler;
|
|
// NetlinkAckHandler handles netlink Ack messages, which are a special type
|
|
// of netlink error message carrying an error code of 0. Since Ack messages
|
|
// contain no useful data (other than the error code of 0 to differentiate
|
|
// it from an actual error message), the handler is not passed a message.
|
|
// as an argument. The boolean value filled in by the handler (via the
|
|
// pointer) indicates whether or not the callbacks registered for the message
|
|
// (identified by sequence number) that this handler was invoked for should be
|
|
// removed after this callback is executed. This allows a sender of an NL80211
|
|
// message to handle both an Ack and another response message, rather than
|
|
// handle only the first response received.
|
|
typedef base::Callback<void(bool*)> NetlinkAckHandler;
|
|
|
|
// ResponseHandlers provide a polymorphic context for the base::Callback
|
|
// message handlers so that handlers for different types of messages can be
|
|
// kept in the same container (namely, |message_handlers_|).
|
|
class NetlinkResponseHandler :
|
|
public base::RefCounted<NetlinkResponseHandler> {
|
|
public:
|
|
explicit NetlinkResponseHandler(
|
|
const NetlinkAckHandler& ack_handler,
|
|
const NetlinkAuxilliaryMessageHandler& error_handler);
|
|
virtual ~NetlinkResponseHandler();
|
|
// Calls wrapper-type-specific callback for |netlink_message|. Returns
|
|
// false if |netlink_message| is not the correct type. Calls callback
|
|
// (which is declared in the private area of derived classes) with
|
|
// properly cast version of |netlink_message|.
|
|
virtual bool HandleMessage(const NetlinkMessage& netlink_message) const = 0;
|
|
void HandleError(AuxilliaryMessageType type,
|
|
const NetlinkMessage* netlink_message) const;
|
|
virtual bool HandleAck() const;
|
|
void set_delete_after(const timeval& time) { delete_after_ = time; }
|
|
const struct timeval& delete_after() const { return delete_after_; }
|
|
|
|
protected:
|
|
NetlinkResponseHandler();
|
|
NetlinkAckHandler ack_handler_;
|
|
|
|
private:
|
|
NetlinkAuxilliaryMessageHandler error_handler_;
|
|
struct timeval delete_after_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(NetlinkResponseHandler);
|
|
};
|
|
|
|
// Encapsulates all the different things we know about a specific message
|
|
// type like its name, and its id.
|
|
struct MessageType {
|
|
MessageType();
|
|
|
|
uint16_t family_id;
|
|
|
|
// Multicast groups supported by the family. The string and mapping to
|
|
// a group id are extracted from the CTRL_CMD_NEWFAMILY message.
|
|
std::map<std::string, uint32_t> groups;
|
|
};
|
|
|
|
// Various kinds of events to which we can subscribe (and receive) from
|
|
// cfg80211.
|
|
static const char kEventTypeConfig[];
|
|
static const char kEventTypeScan[];
|
|
static const char kEventTypeRegulatory[];
|
|
static const char kEventTypeMlme[];
|
|
|
|
// NetlinkManager is a singleton and this is the way to access it.
|
|
static NetlinkManager* GetInstance();
|
|
|
|
virtual ~NetlinkManager();
|
|
|
|
// Performs non-trivial object initialization of the NetlinkManager singleton.
|
|
virtual bool Init();
|
|
|
|
// Passes the job of waiting for, and the subsequent reading from, the
|
|
// netlink socket to the current message loop.
|
|
virtual void Start();
|
|
|
|
// The following methods deal with the network family table. This table
|
|
// associates netlink family names with family_ids (also called message
|
|
// types). Note that some families have static ids assigned to them but
|
|
// others require the kernel to resolve a string describing the family into
|
|
// a dynamically-determined id.
|
|
|
|
// Returns the family_id (message type) associated with |family_name|,
|
|
// calling the kernel if needed. Returns
|
|
// |NetlinkMessage::kIllegalMessageType| if the message type could not be
|
|
// determined. May block so |GetFamily| should be called before entering the
|
|
// event loop.
|
|
virtual uint16_t GetFamily(const std::string& family_name,
|
|
const NetlinkMessageFactory::FactoryMethod& message_factory);
|
|
|
|
// Install a NetlinkManager NetlinkMessageHandler. The handler is a
|
|
// user-supplied object to be called by the system for user-bound messages
|
|
// that do not have a corresponding messaage-specific callback.
|
|
// |AddBroadcastHandler| should be called before |SubscribeToEvents| since
|
|
// the result of this call are used for that call.
|
|
virtual bool AddBroadcastHandler(
|
|
const NetlinkMessageHandler& message_handler);
|
|
|
|
// Uninstall a NetlinkMessage Handler.
|
|
virtual bool RemoveBroadcastHandler(
|
|
const NetlinkMessageHandler& message_handler);
|
|
|
|
// Determines whether a handler is in the list of broadcast handlers.
|
|
bool FindBroadcastHandler(const NetlinkMessageHandler& message_handler) const;
|
|
|
|
// Uninstall all broadcast netlink message handlers.
|
|
void ClearBroadcastHandlers();
|
|
|
|
// Sends a netlink message to the kernel using the NetlinkManager socket after
|
|
// installing a handler to deal with the kernel's response to the message.
|
|
// TODO(wdg): Eventually, this should also include a timeout and a callback
|
|
// to call in case of timeout.
|
|
virtual bool SendControlMessage(
|
|
ControlNetlinkMessage* message,
|
|
const ControlNetlinkMessageHandler& message_handler,
|
|
const NetlinkAckHandler& ack_handler,
|
|
const NetlinkAuxilliaryMessageHandler& error_handler);
|
|
virtual bool SendNl80211Message(
|
|
Nl80211Message* message,
|
|
const Nl80211MessageHandler& message_handler,
|
|
const NetlinkAckHandler& ack_handler,
|
|
const NetlinkAuxilliaryMessageHandler& error_handler);
|
|
|
|
// Generic erroneous message handler everyone can use.
|
|
static void OnNetlinkMessageError(AuxilliaryMessageType type,
|
|
const NetlinkMessage* raw_message);
|
|
|
|
// Generic Ack handler that does nothing. Other callbacks registered for the
|
|
// message are not deleted after this function is executed.
|
|
static void OnAckDoNothing(bool* remove_callbacks) {
|
|
*remove_callbacks = false;
|
|
}
|
|
|
|
// Uninstall the handler for a specific netlink message.
|
|
bool RemoveMessageHandler(const NetlinkMessage& message);
|
|
|
|
// Sign-up to receive and log multicast events of a specific type (once wifi
|
|
// is up).
|
|
virtual bool SubscribeToEvents(const std::string& family,
|
|
const std::string& group);
|
|
|
|
// Gets the next sequence number for a NetlinkMessage to be sent over
|
|
// NetlinkManager's netlink socket.
|
|
uint32_t GetSequenceNumber();
|
|
|
|
protected:
|
|
friend struct base::DefaultLazyInstanceTraits<NetlinkManager>;
|
|
|
|
NetlinkManager();
|
|
|
|
private:
|
|
friend class NetlinkManagerTest;
|
|
friend class NetlinkMessageTest;
|
|
friend class ShillDaemonTest;
|
|
friend class ChromeosDaemonTest;
|
|
FRIEND_TEST(NetlinkManagerTest, AddLinkTest);
|
|
FRIEND_TEST(NetlinkManagerTest, BroadcastHandler);
|
|
FRIEND_TEST(NetlinkManagerTest, GetFamilyOneInterstitialMessage);
|
|
FRIEND_TEST(NetlinkManagerTest, GetFamilyTimeout);
|
|
FRIEND_TEST(NetlinkManagerTest, MessageHandler);
|
|
FRIEND_TEST(NetlinkManagerTest, AckHandler);
|
|
FRIEND_TEST(NetlinkManagerTest, ErrorHandler);
|
|
FRIEND_TEST(NetlinkManagerTest, MultipartMessageHandler);
|
|
FRIEND_TEST(NetlinkManagerTest, OnInvalidRawNlMessageReceived);
|
|
FRIEND_TEST(NetlinkManagerTest, TimeoutResponseHandlers);
|
|
FRIEND_TEST(NetlinkManagerTest, PendingDump);
|
|
FRIEND_TEST(NetlinkManagerTest, PendingDump_Timeout);
|
|
FRIEND_TEST(NetlinkManagerTest, PendingDump_Retry);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_ASSOCIATE);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_AUTHENTICATE);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_CONNECT);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DEAUTHENTICATE);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISASSOCIATE);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISCONNECT);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_SCAN_RESULTS);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_STATION);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NOTIFY_CQM);
|
|
FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_TRIGGER_SCAN);
|
|
|
|
typedef scoped_refptr<NetlinkResponseHandler> NetlinkResponseHandlerRefPtr;
|
|
|
|
// Container for information we need to send a netlink message out on a
|
|
// netlink socket.
|
|
struct NetlinkPendingMessage {
|
|
NetlinkPendingMessage(uint32_t sequence_number_arg,
|
|
bool is_dump_request_arg,
|
|
ByteString message_string_arg,
|
|
NetlinkResponseHandlerRefPtr handler_arg)
|
|
: retries_left(kMaxNlMessageRetries),
|
|
sequence_number(sequence_number_arg),
|
|
is_dump_request(is_dump_request_arg),
|
|
message_string(message_string_arg),
|
|
handler(handler_arg) {}
|
|
|
|
int retries_left;
|
|
uint32_t sequence_number;
|
|
bool is_dump_request;
|
|
ByteString message_string;
|
|
NetlinkResponseHandlerRefPtr handler;
|
|
uint32_t last_received_error;
|
|
};
|
|
|
|
// These need to be member variables, even though they're only used once in
|
|
// the code, since they're needed for unittests.
|
|
static const long kMaximumNewFamilyWaitSeconds; // NOLINT
|
|
static const long kMaximumNewFamilyWaitMicroSeconds; // NOLINT
|
|
static const long kResponseTimeoutSeconds; // NOLINT
|
|
static const long kResponseTimeoutMicroSeconds; // NOLINT
|
|
static const long kPendingDumpTimeoutMilliseconds; // NOLINT
|
|
static const long kNlMessageRetryDelayMilliseconds; // NOLINT
|
|
static const int kMaxNlMessageRetries; // NOLINT
|
|
|
|
// Returns the file descriptor of socket used to read wifi data.
|
|
int file_descriptor() const;
|
|
|
|
// MessageLoop calls this when data is available on our socket. This
|
|
// method passes each, individual, message in the input to
|
|
// |OnNlMessageReceived|. Each part of a multipart message gets handled,
|
|
// individually, by this method.
|
|
void OnRawNlMessageReceived(InputData* data);
|
|
|
|
// This method processes a message from |OnRawNlMessageReceived| by passing
|
|
// the message to either the NetlinkManager callback that matches the sequence
|
|
// number of the message or, if there isn't one, to all of the default
|
|
// NetlinkManager callbacks in |broadcast_handlers_|.
|
|
void OnNlMessageReceived(NetlinkPacket* packet);
|
|
|
|
// Sends the pending dump message, and decrement the message's retry count if
|
|
// it was resent successfully.
|
|
void ResendPendingDumpMessage();
|
|
|
|
// If a NetlinkResponseHandler registered for the message identified by
|
|
// |sequence_number| exists, calls the error handler with the arguments |type|
|
|
// and |netlink_message|, then erases the NetlinkResponseHandler from
|
|
// |message_handlers_|.
|
|
void CallErrorHandler(uint32_t sequence_number, AuxilliaryMessageType type,
|
|
const NetlinkMessage* netlink_message);
|
|
|
|
// Called by InputHandler on exceptional events.
|
|
void OnReadError(const std::string& error_msg);
|
|
|
|
// Utility function that posts a task to the message loop to call
|
|
// NetlinkManager::ResendPendingDumpMessage kNlMessageRetryDelayMilliseconds
|
|
// from now.
|
|
void ResendPendingDumpMessageAfterDelay();
|
|
|
|
// Just for tests, this method turns off WiFi and clears the subscribed
|
|
// events list. If |full| is true, also clears state set by Init.
|
|
void Reset(bool full);
|
|
|
|
// Handles a CTRL_CMD_NEWFAMILY message from the kernel.
|
|
void OnNewFamilyMessage(const ControlNetlinkMessage& message);
|
|
|
|
// Sends a netlink message if |pending_dump_| is false. Otherwise, post
|
|
// a message to |pending_messages_| to be sent later.
|
|
bool SendOrPostMessage(
|
|
NetlinkMessage* message,
|
|
NetlinkResponseHandler* message_wrapper); // Passes ownership.
|
|
|
|
// Install a handler to deal with kernel's response to the message contained
|
|
// in |pending_message|, then sends the message by calling
|
|
// NetlinkManager::SendMessageInternal.
|
|
bool RegisterHandlersAndSendMessage(
|
|
const NetlinkPendingMessage& pending_message);
|
|
|
|
// Sends the netlink message whose bytes are contained in |pending_message| to
|
|
// the kernel using the NetlinkManager socket. If |pending_message| is a dump
|
|
// request and the message is sent successfully, a timeout timer is started to
|
|
// limit the amount of time we wait for responses to that message. Adds a
|
|
// serial number to |message| before it is sent.
|
|
bool SendMessageInternal(const NetlinkPendingMessage& pending_message);
|
|
|
|
// Given a netlink packet |packet|, infers the context of this netlink
|
|
// message (for message parsing purposes) and returns a MessageContext
|
|
// describing this context.
|
|
NetlinkMessage::MessageContext InferMessageContext(
|
|
const NetlinkPacket& packet);
|
|
|
|
// Called when we time out waiting for a response to a netlink dump message.
|
|
// Invokes the error handler with kTimeoutWaitingForResponse, deletes the
|
|
// error handler, then calls NetlinkManager::OnPendingDumpComplete.
|
|
void OnPendingDumpTimeout();
|
|
|
|
// Cancels |pending_dump_timeout_callback_|, deletes the currently pending
|
|
// dump request message from the front of |pending_messages_| since we have
|
|
// finished waiting for replies, then sends the next message in
|
|
// |pending_messages_| (if any).
|
|
void OnPendingDumpComplete();
|
|
|
|
// Returns true iff there we are waiting for replies to a netlink dump
|
|
// message, false otherwise.
|
|
bool IsDumpPending();
|
|
|
|
// Returns the sequence number of the pending netlink dump request message iff
|
|
// there is a pending dump. Otherwise, returns 0.
|
|
uint32_t PendingDumpSequenceNumber();
|
|
|
|
// NetlinkManager Handlers, OnRawNlMessageReceived invokes each of these
|
|
// User-supplied callback object when _it_ gets called to read netlink data.
|
|
std::list<NetlinkMessageHandler> broadcast_handlers_;
|
|
|
|
// Message-specific callbacks, mapped by message ID.
|
|
std::map<uint32_t, NetlinkResponseHandlerRefPtr> message_handlers_;
|
|
|
|
// Netlink messages due to be sent to the kernel. If a dump is pending,
|
|
// the first element in this queue will contain the netlink dump request
|
|
// message that we are waiting on replies for.
|
|
std::queue<NetlinkPendingMessage> pending_messages_;
|
|
|
|
base::WeakPtrFactory<NetlinkManager> weak_ptr_factory_;
|
|
base::CancelableClosure pending_dump_timeout_callback_;
|
|
base::CancelableClosure resend_dump_message_callback_;
|
|
base::Callback<void(InputData*)> dispatcher_callback_;
|
|
std::unique_ptr<IOHandler> dispatcher_handler_;
|
|
|
|
std::unique_ptr<NetlinkSocket> sock_;
|
|
std::map<const std::string, MessageType> message_types_;
|
|
NetlinkMessageFactory message_factory_;
|
|
Time* time_;
|
|
IOHandlerFactory* io_handler_factory_;
|
|
bool dump_pending_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(NetlinkManager);
|
|
};
|
|
|
|
} // namespace shill
|
|
|
|
#endif // SHILL_NET_NETLINK_MANAGER_H_
|