shill: Split Nl80211Message into class hierarchy.
Gotta apologize for this CL: it's a bit more gnarly than I'd like.
There're not that many modifications but, with all the moving of code,
it's harder to review than I'd like. I found that, easier than gerrit,
cherry-pick the change, do a git show HEAD^:nl80211_message.[ch] > old.[ch],
and edit the old and new files, together.
With the exception of the following changes, the Nl80211Message code was
just split into the NetlinkMessage <|-- GenericNetlinkMessage <|--
Nl80211Message class hierarchy.
1. In |NetlinkMessage::InitAndStripHeader|, a length check was
added to verify that enough bytes for the |nlmsghdr| exists.
2. In |GenericNetlinkMessage::Encode|, changed name of |attributes|
variable to |attribute_string|.
3. Split |EncodeHeader| and |InitAndStripHeader|, each, into
|nlmsghdr| and |genlmsghdr| pieces.
4. In |NetlinkMessage::EncodeHeader|, added encoding of |flags_| (that
had, somehow, gotten missed in the previous CL -- all other
|flags_| processing had been included.)
5. Removed the following unused methods from |Nl80211Message|:
- GetHeaderString
- StringFromFrame
- StringFromKeyType
- StringFromRegInitiator
- StringFromSsid
BUG=chromium-os:38221
TEST=unittest.
Change-Id: I208ede49fa8003d49eb487365f886a8f0d53da3f
Reviewed-on: https://gerrit.chromium.org/gerrit/44640
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/nl80211_message.h b/nl80211_message.h
index 10986fa..3ecc022 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -28,38 +28,195 @@
class Config80211;
-// Class for messages received from libnl.
-class Nl80211Message {
+// 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;
- static const unsigned int kEthernetAddressBytes;
- static const char kBogusMacAddress[];
- Nl80211Message(uint8 command, const char *command_string)
- : attributes_(new AttributeList),
+ 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);
+};
+
+// 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),
- flags_(0),
- message_type_(nl80211_message_type_),
- sequence_number_(kBroadcastSequenceNumber) {}
- virtual ~Nl80211Message() {}
+ 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_; }
- // Initializes the message with bytes from the kernel.
- virtual bool InitFromNlmsg(const nlmsghdr *msg);
- static void PrintBytes(int log_level, const unsigned char *buf,
- size_t num_bytes);
- uint32_t sequence_number() const { return sequence_number_; }
- virtual void Print(int log_level) const;
-
- void set_sequence_number(uint32_t seq) { sequence_number_ = seq; }
-
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);
+};
+
+// 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
@@ -87,54 +244,10 @@
static std::string StringFromReason(uint16_t reason);
static std::string StringFromStatus(uint16_t status);
- // Returns a netlink message suitable for Sockets::Send. Return value is
- // empty on failure. |nlmsg_type| needs to be the family id returned by
- // |genl_ctrl_resolve|.
- ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
-
- protected:
- // Reads the values from the |nlmsghdr| and |genlmsghdr| portions of the
- // netlink message and removes those headers (and any padding that happens to
- // be there) from |input| leaving, hopefully, the attributes.
- bool InitAndStripHeader(ByteString *input);
-
- // Returns a string of bytes representing an |nlmsghdr| with the members
- // filled-in with values representing the current message, and any required
- // padding.
- ByteString EncodeHeader(uint32_t sequence_number, uint16_t nlmsg_type);
-
- // Returns a string that should precede all user-bound message strings.
- virtual std::string GetHeaderString() const;
-
- // Returns a string that describes the contents of the frame pointed to by
- // 'attr'.
- std::string StringFromFrame(int attr_name) const;
-
- // Converts key_type to a string.
- static std::string StringFromKeyType(nl80211_key_type key_type);
-
- // Returns a string representation of the REG initiator described by the
- // method's parameter.
- static std::string StringFromRegInitiator(__u8 initiator);
-
- // Returns a string based on the SSID found in 'data'. Non-printable
- // characters are string-ized.
- static std::string StringFromSsid(const uint8_t len, const uint8_t *data);
-
private:
- friend class Config80211Test;
- FRIEND_TEST(Config80211Test, NL80211_CMD_NOTIFY_CQM);
-
- static uint16_t nl80211_message_type_;
static std::map<uint16_t, std::string> *reason_code_string_;
static std::map<uint16_t, std::string> *status_code_string_;
-
- AttributeListRefPtr attributes_;
- const uint8 command_;
- const char *command_string_;
- uint16_t flags_;
- uint16_t message_type_;
- uint32_t sequence_number_;
+ static uint16_t nl80211_message_type_;
DISALLOW_COPY_AND_ASSIGN(Nl80211Message);
};
@@ -535,14 +648,17 @@
// Factory class.
//
-class Nl80211MessageFactory {
+class NetlinkMessageFactory {
public:
// Ownership of the message is passed to the caller and, as such, he should
// delete it.
- static Nl80211Message *CreateMessage(nlmsghdr *msg);
+ 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(Nl80211MessageFactory);
+ DISALLOW_COPY_AND_ASSIGN(NetlinkMessageFactory);
};