blob: 6d2f7407fc0f78d6e409dced1fd8692c0b28d425 [file] [log] [blame]
// Copyright (c) 2012 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.
#ifndef SHILL_USER_BOUND_NLMESSAGE_H_
#define SHILL_USER_BOUND_NLMESSAGE_H_
#include <linux/nl80211.h>
#include <iomanip>
#include <map>
#include <string>
#include <vector>
#include <base/basictypes.h>
#include <base/bind.h>
#include <base/lazy_instance.h>
#include <gtest/gtest.h>
#include "shill/attribute_list.h"
#include "shill/byte_string.h"
#include "shill/refptr_types.h"
struct nlattr;
struct nlmsghdr;
namespace shill {
class Config80211;
// Netlink messages are sent over netlink sockets to talk between user-space
// programs (like shill) and kernel modules (like the cfg80211 module). Each
// kernel module that talks netlink potentially adds its own family header to
// the nlmsghdr (the top-level netlink message header) and, potentially, uses a
// different payload format. The NetlinkMessage class represents that which
// is common between the different types of netlink message.
//
// The common portions of Netlink Messages start with a |nlmsghdr|. Those
// messages look something like the following (the functions, macros, and
// datatypes described are provided by libnl -- see also
// http://www.infradead.org/~tgr/libnl/doc/core.html):
//
// |<--------------nlmsg_total_size()----------->|
// | |<------nlmsg_datalen()-------------->|
// | | |
// -----+-----+-+-----------------------------------+-+----
// ... | | | netlink payload | |
// | | +------------+-+--------------------+ |
// | nl | | | | | | nl
// | msg |p| (optional) |p| |p| msg ...
// | hdr |a| family |a| family payload |a| hdr
// | |d| header |d| |d|
// | | | | | | |
// -----+-----+-+------------+-+--------------------+-+----
// ^
// |
// +-- nlmsg_data()
//
// All NetlinkMessages sent to the kernel need a valid message type (which is
// found in the nlmsghdr structure) and all NetlinkMessages received from the
// kernel have a valid message type. Some message types (NLMSG_NOOP,
// NLMSG_ERROR, and GENL_ID_CTRL, for example) are allocated statically; for
// those, the |message_type_| is assigned directly.
//
// Other message types ("nl80211", for example), are assigned by the kernel
// dynamically. To get the message type, pass a closure to assign the
// message_type along with the sting to Config80211::AddFamilyByString:
//
// config80211_->AddFamilyByString(Nl80211Message::kMessageType,
// Bind(&Nl80211Message::SetFamilyId));
//
// Do all of this before you start to create NetlinkMessages so that
// NetlinkMessage can be instantiated with a valid |message_type_|.
class NetlinkMessage {
public:
static const uint32_t kBroadcastSequenceNumber;
static const uint16_t kIllegalMessageType;
explicit NetlinkMessage(uint16_t message_type) :
flags_(0), message_type_(message_type),
sequence_number_(kBroadcastSequenceNumber) {}
virtual ~NetlinkMessage() {}
// Returns a string of bytes representing the message (with it headers) and
// any necessary padding. These bytes are appropriately formatted to be
// written to a netlink socket.
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type) = 0;
// Initializes the |NetlinkMessage| from a complete and legal message
// (potentially received from the kernel via a netlink socket).
virtual bool InitFromNlmsg(const nlmsghdr *msg);
uint16_t message_type() const { return message_type_; }
void AddFlag(uint16_t new_flag) { flags_ |= new_flag; }
uint16_t flags() const { return flags_; }
uint32_t sequence_number() const { return sequence_number_; }
virtual void Print(int log_level) const = 0;
// Logs the message's raw bytes (with minimal interpretation).
static void PrintBytes(int log_level, const unsigned char *buf,
size_t num_bytes);
protected:
friend class Config80211Test;
FRIEND_TEST(Config80211Test, NL80211_CMD_NOTIFY_CQM);
// Returns a string of bytes representing an |nlmsghdr|, filled-in, and its
// padding.
virtual ByteString EncodeHeader(uint32_t sequence_number,
uint16_t nlmsg_type);
// Reads the |nlmsghdr| and removes it from |input|.
virtual bool InitAndStripHeader(ByteString *input);
uint16_t flags_;
uint16_t message_type_;
uint32_t sequence_number_;
private:
DISALLOW_COPY_AND_ASSIGN(NetlinkMessage);
};
// The Error and Ack messages are received from the kernel and are combined,
// here, because they look so much alike (the only difference is that the
// error code is 0 for the Ack messages). Error messages are received from
// the kernel in response to a sent message when there's a problem (such as
// a malformed message or a busy kernel module). Ack messages are received
// from the kernel when a sent message has the NLM_F_ACK flag set, indicating
// that an Ack is requested.
class ErrorAckMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
ErrorAckMessage() : NetlinkMessage(kMessageType), error_(0) {}
virtual bool InitFromNlmsg(const nlmsghdr *const_msg);
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
virtual void Print(int log_level) const;
std::string ToString() const;
uint32_t error() const { return -error_; }
private:
uint32_t error_;
DISALLOW_COPY_AND_ASSIGN(ErrorAckMessage);
};
class NoopMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
NoopMessage() : NetlinkMessage(kMessageType) {}
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
virtual void Print(int log_level) const;
std::string ToString() const { return "<NOOP>"; }
private:
DISALLOW_COPY_AND_ASSIGN(NoopMessage);
};
class DoneMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
DoneMessage() : NetlinkMessage(kMessageType) {}
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
virtual void Print(int log_level) const;
std::string ToString() const { return "<DONE with multipart message>"; }
private:
DISALLOW_COPY_AND_ASSIGN(DoneMessage);
};
class OverrunMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
OverrunMessage() : NetlinkMessage(kMessageType) {}
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
virtual void Print(int log_level) const;
std::string ToString() const { return "<OVERRUN - data lost>"; }
private:
DISALLOW_COPY_AND_ASSIGN(OverrunMessage);
};
// Objects of the |GenericNetlinkMessage| type represent messages that contain
// a |genlmsghdr| after a |nlmsghdr|. These messages seem to all contain a
// payload that consists of a list of structured attributes (it's possible that
// some messages might have a genlmsghdr and a different kind of payload but I
// haven't seen one, yet). The genlmsghdr contains a command id that, when
// combined with the family_id (from the nlmsghdr), describes the ultimate use
// for the netlink message.
//
// An attribute contains a header and a chunk of data. The header contains an
// id which is an enumerated value that describes the use of the attribute's
// data (the datatype of the attribute's data is implied by the attribute id)
// and the length of the header+data in bytes. The attribute id is,
// confusingly, called the type (or nla_type -- this is _not_ the data type of
// the attribute). Each family defines the meaning of the nla_types in the
// context of messages in that family (for example, the nla_type with the
// value 3 will always mean the same thing for attributes in the same family).
// EXCEPTION: Some attributes are nested (that is, they contain a list of other
// attributes rather than a single value). Each nested attribute defines the
// meaning of the nla_types in the context of attributes that are nested under
// this attribute (for example, the nla_type with the value 3 will have a
// different meaning when nested under another attribute -- that meaning is
// defined by the attribute under which it is nested). Fun.
//
// The GenericNetlink messages look like this:
//
// -----+-----+-+-------------------------------------------------+-+--
// ... | | | message payload | |
// | | +------+-+----------------------------------------+ |
// | nl | | | | attributes | |
// | msg |p| genl |p+-----------+-+---------+-+--------+-----+p| ...
// | hdr |a| msg |a| struct |p| attrib |p| struct | ... |a|
// | |d| hdr |d| nlattr |a| payload |a| nlattr | |d|
// | | | | | |d| |d| | | |
// -----+-----+-+------+-+-----------+-+---------+-+--------+-----+-+--
// | ^ | |
// |<-NLA_HDRLEN->| | |
// | +---nla_data()
// |<----nla_attr_size---->| |
// |<-----nla_total_size---->|
class GenericNetlinkMessage : public NetlinkMessage {
public:
GenericNetlinkMessage(uint16_t my_message_type, uint8 command,
const char *command_string)
: NetlinkMessage(my_message_type),
attributes_(new AttributeList),
command_(command),
command_string_(command_string) {}
virtual ~GenericNetlinkMessage() {}
virtual ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
uint8 command() const { return command_; }
const char *command_string() const { return command_string_; }
AttributeListConstRefPtr const_attributes() const { return attributes_; }
AttributeListRefPtr attributes() { return attributes_; }
virtual void Print(int log_level) const;
protected:
// Returns a string of bytes representing _both_ an |nlmsghdr| and a
// |genlmsghdr|, filled-in, and its padding.
virtual ByteString EncodeHeader(uint32_t sequence_number,
uint16_t nlmsg_type);
// Reads the |nlmsghdr| and |genlmsghdr| headers and removes them from
// |input|.
virtual bool InitAndStripHeader(ByteString *input);
AttributeListRefPtr attributes_;
const uint8 command_;
const char *command_string_;
private:
DISALLOW_COPY_AND_ASSIGN(GenericNetlinkMessage);
};
// Control Messages
class ControlNetlinkMessage : public GenericNetlinkMessage {
public:
static const uint16_t kMessageType;
ControlNetlinkMessage(uint8 command, const char *command_string)
: GenericNetlinkMessage(kMessageType, command, command_string) {}
virtual bool InitFromNlmsg(const nlmsghdr *msg);
};
class NewFamilyMessage : public ControlNetlinkMessage {
public:
static const uint8_t kCommand;
static const char kCommandString[];
NewFamilyMessage() : ControlNetlinkMessage(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(NewFamilyMessage);
};
class GetFamilyMessage : public ControlNetlinkMessage {
public:
static const uint8_t kCommand;
static const char kCommandString[];
GetFamilyMessage() : ControlNetlinkMessage(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(GetFamilyMessage);
};
// Class for messages received from the mac80211 drivers by way of the
// cfg80211 kernel module.
class Nl80211Message : public GenericNetlinkMessage {
public:
static const char kMessageTypeString[];
static const unsigned int kEthernetAddressBytes;
static const char kBogusMacAddress[];
Nl80211Message(uint8 command, const char *command_string)
: GenericNetlinkMessage(nl80211_message_type_, command, command_string) {}
virtual ~Nl80211Message() {}
virtual bool InitFromNlmsg(const nlmsghdr *msg);
uint8 command() const { return command_; }
const char *command_string() const { return command_string_; }
uint16_t message_type() const { return message_type_; }
uint32_t sequence_number() const { return sequence_number_; }
void set_sequence_number(uint32_t seq) { sequence_number_ = seq; }
// TODO(wdg): This needs to be moved to AttributeMac.
// Helper function to provide a string for a MAC address. If no attribute
// is found, this method returns 'false'. On any error with a non-NULL
// |value|, this method sets |value| to a bogus MAC address.
bool GetMacAttributeString(int id, std::string *value) const;
// TODO(wdg): This needs to be moved to AttributeScanFrequencies.
// Helper function to provide a vector of scan frequencies for attributes
// that contain them (such as NL80211_ATTR_SCAN_FREQUENCIES).
bool GetScanFrequenciesAttribute(int id, std::vector<uint32_t> *value) const;
// TODO(wdg): This needs to be moved to AttributeScanSSids.
// Helper function to provide a vector of SSIDs for attributes that contain
// them (such as NL80211_ATTR_SCAN_SSIDS).
bool GetScanSsidsAttribute(int id, std::vector<std::string> *value) const;
// TODO(wdg): This needs to be moved to AttributeMac.
// Stringizes the MAC address found in 'arg'. If there are problems (such
// as a NULL |arg|), |value| is set to a bogus MAC address.
static std::string StringFromMacAddress(const uint8_t *arg);
// Returns a string representing the passed-in |status| or |reason|, the
// value of which has been acquired from libnl (for example, from the
// NL80211_ATTR_STATUS_CODE or NL80211_ATTR_REASON_CODE attribute).
static std::string StringFromReason(uint16_t reason);
static std::string StringFromStatus(uint16_t status);
private:
static std::map<uint16_t, std::string> *reason_code_string_;
static std::map<uint16_t, std::string> *status_code_string_;
static uint16_t nl80211_message_type_;
DISALLOW_COPY_AND_ASSIGN(Nl80211Message);
};
class Nl80211Frame {
public:
enum Type {
kAssocResponseFrameType = 0x10,
kReassocResponseFrameType = 0x30,
kAssocRequestFrameType = 0x00,
kReassocRequestFrameType = 0x20,
kAuthFrameType = 0xb0,
kDisassocFrameType = 0xa0,
kDeauthFrameType = 0xc0,
kIllegalFrameType = 0xff
};
explicit Nl80211Frame(const ByteString &init);
bool ToString(std::string *output) const;
bool IsEqual(const Nl80211Frame &other) const;
uint16_t reason() const { return reason_; }
uint16_t status() const { return status_; }
private:
static const uint8_t kMinimumFrameByteCount;
static const uint8_t kFrameTypeMask;
std::string mac_from_;
std::string mac_to_;
uint8_t frame_type_;
uint16_t reason_;
uint16_t status_;
ByteString frame_;
DISALLOW_COPY_AND_ASSIGN(Nl80211Frame);
};
//
// Specific Nl80211Message types.
//
class AssociateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
AssociateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(AssociateMessage);
};
class AuthenticateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
AuthenticateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(AuthenticateMessage);
};
class CancelRemainOnChannelMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
CancelRemainOnChannelMessage()
: Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(CancelRemainOnChannelMessage);
};
class ConnectMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
ConnectMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(ConnectMessage);
};
class DeauthenticateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
DeauthenticateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(DeauthenticateMessage);
};
class DeleteStationMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
DeleteStationMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(DeleteStationMessage);
};
class DisassociateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
DisassociateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(DisassociateMessage);
};
class DisconnectMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
DisconnectMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(DisconnectMessage);
};
class FrameTxStatusMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
FrameTxStatusMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(FrameTxStatusMessage);
};
class GetRegMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
GetRegMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(GetRegMessage);
};
class JoinIbssMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
JoinIbssMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(JoinIbssMessage);
};
class MichaelMicFailureMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
MichaelMicFailureMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(MichaelMicFailureMessage);
};
class NewScanResultsMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
NewScanResultsMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(NewScanResultsMessage);
};
class NewStationMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
NewStationMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(NewStationMessage);
};
class NewWifiMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
NewWifiMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(NewWifiMessage);
};
class NotifyCqmMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
NotifyCqmMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(NotifyCqmMessage);
};
class PmksaCandidateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
PmksaCandidateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(PmksaCandidateMessage);
};
class RegBeaconHintMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
RegBeaconHintMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(RegBeaconHintMessage);
};
class RegChangeMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
RegChangeMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(RegChangeMessage);
};
class RemainOnChannelMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
RemainOnChannelMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(RemainOnChannelMessage);
};
class RoamMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
RoamMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(RoamMessage);
};
class ScanAbortedMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
ScanAbortedMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(ScanAbortedMessage);
};
class GetScanMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
GetScanMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(GetScanMessage);
};
class TriggerScanMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
TriggerScanMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(TriggerScanMessage);
};
class UnknownMessage : public Nl80211Message {
public:
explicit UnknownMessage(uint8_t command)
: Nl80211Message(command, kCommandString),
command_(command) {}
static const uint8_t kCommand;
static const char kCommandString[];
private:
uint8_t command_;
DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
};
class UnprotDeauthenticateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
UnprotDeauthenticateMessage()
: Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(UnprotDeauthenticateMessage);
};
class UnprotDisassociateMessage : public Nl80211Message {
public:
static const uint8_t kCommand;
static const char kCommandString[];
UnprotDisassociateMessage() : Nl80211Message(kCommand, kCommandString) {}
private:
DISALLOW_COPY_AND_ASSIGN(UnprotDisassociateMessage);
};
//
// Factory class.
//
class NetlinkMessageFactory {
public:
// Ownership of the message is passed to the caller and, as such, he should
// delete it.
static NetlinkMessage *CreateMessage(nlmsghdr *msg);
// TODO(wdg): Need a way for a class to register a callback and a message
// type. That way, CreateMessage can call the appropriate factory based on
// the incoming message type.
private:
DISALLOW_COPY_AND_ASSIGN(NetlinkMessageFactory);
};
// Nl80211MessageDataCollector - this class is used to collect data to be
// used for unit tests. It is only invoked in this case.
class Nl80211MessageDataCollector {
public:
static Nl80211MessageDataCollector *GetInstance();
void CollectDebugData(const Nl80211Message &message, nlmsghdr *msg);
protected:
friend struct
base::DefaultLazyInstanceTraits<Nl80211MessageDataCollector>;
explicit Nl80211MessageDataCollector();
private:
// In order to limit the output from this class, I keep track of types I
// haven't yet printed.
std::map<uint8_t, bool> need_to_print;
DISALLOW_COPY_AND_ASSIGN(Nl80211MessageDataCollector);
};
} // namespace shill
#endif // SHILL_USER_BOUND_NLMESSAGE_H_