shill: Splits netlink_message and generic_... out of nl80211_message.*
This CL just splits netlink_message.h/.cc and
generic_netlink_message.h/.cc out of nl80211_message.h/.cc. The name
"generic" is as regrettable as it is accurate -- messages in those files
use the |genlmsghdr| which is the "generic netlink message header". This
change necessitated modifiying header file includes. Other than those
minor (required) changes, this CL represents a straight cut (from
nl80211_message.*) and paste.
BUG=None
TEST=unittest
Change-Id: I79d1f96d8e942c1ca602015f48ce74099899fe63
Reviewed-on: https://gerrit.chromium.org/gerrit/47859
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/Makefile b/Makefile
index aff65c5..7c718da 100644
--- a/Makefile
+++ b/Makefile
@@ -233,6 +233,7 @@
event_dispatcher.o \
file_io.o \
file_reader.o \
+ generic_netlink_message.o \
geolocation_info.o \
glib.o \
glib_io_ready_handler.o \
@@ -253,8 +254,9 @@
metrics.o \
minijail.o \
modem_info.o \
- netlink_socket.o \
netlink_attribute.o \
+ netlink_message.o \
+ netlink_socket.o \
nl80211_attribute.o \
nl80211_message.o \
nss.o \
diff --git a/config80211.cc b/config80211.cc
index abea104..7d09fd1 100644
--- a/config80211.cc
+++ b/config80211.cc
@@ -15,10 +15,11 @@
#include "shill/attribute_list.h"
#include "shill/error.h"
#include "shill/event_dispatcher.h"
+#include "shill/generic_netlink_message.h"
#include "shill/io_handler.h"
#include "shill/logging.h"
#include "shill/netlink_socket.h"
-#include "shill/nl80211_message.h"
+#include "shill/netlink_message.h"
#include "shill/scope_logger.h"
#include "shill/shill_time.h"
diff --git a/config80211.h b/config80211.h
index 9582f11..cfd92b5 100644
--- a/config80211.h
+++ b/config80211.h
@@ -70,7 +70,7 @@
#include <base/bind.h>
#include <base/lazy_instance.h>
-#include "shill/nl80211_message.h"
+#include "shill/netlink_message.h"
struct nlmsghdr;
@@ -80,7 +80,6 @@
class EventDispatcher;
struct InputData;
class IOHandler;
-class NetlinkMessage;
class NetlinkSocket;
// Config80211 is a singleton that coordinates sending netlink messages to,
@@ -100,8 +99,7 @@
typedef base::Callback<void(const NetlinkMessage &)> NetlinkMessageHandler;
// Encapsulates all the different things we know about a specific message
- // type like its name, its id, and, eventually, a factory for creating
- // messages of the designated type.
+ // type like its name, and its id.
struct MessageType {
MessageType();
diff --git a/generic_netlink_message.cc b/generic_netlink_message.cc
new file mode 100644
index 0000000..c9fa543
--- /dev/null
+++ b/generic_netlink_message.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2013 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.
+
+#include "shill/generic_netlink_message.h"
+
+#include <netlink/msg.h>
+#include <netlink/netlink.h>
+
+#include <base/bind.h>
+#include <base/stringprintf.h>
+
+#include "shill/logging.h"
+#include "shill/netlink_attribute.h"
+
+using base::Bind;
+using base::StringPrintf;
+
+namespace shill {
+
+ByteString GenericNetlinkMessage::EncodeHeader(uint32_t sequence_number) {
+ // Build nlmsghdr.
+ ByteString result(NetlinkMessage::EncodeHeader(sequence_number));
+ if (result.GetLength() == 0) {
+ LOG(ERROR) << "Couldn't encode message header.";
+ return result;
+ }
+
+ // Build and append the genl message header.
+ genlmsghdr genl_header;
+ genl_header.cmd = command();
+ genl_header.version = 1;
+ genl_header.reserved = 0;
+
+ ByteString genl_header_string(
+ reinterpret_cast<unsigned char *>(&genl_header), sizeof(genl_header));
+ size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
+ genl_header_string.Resize(genlmsghdr_with_pad); // Zero-fill.
+
+ nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
+ pheader->nlmsg_len += genlmsghdr_with_pad;
+ result.Append(genl_header_string);
+ return result;
+}
+
+ByteString GenericNetlinkMessage::Encode(uint32_t sequence_number) {
+ ByteString result(EncodeHeader(sequence_number));
+ if (result.GetLength() == 0) {
+ LOG(ERROR) << "Couldn't encode message header.";
+ return result;
+ }
+
+ // Build and append attributes (padding is included by
+ // AttributeList::Encode).
+ ByteString attribute_string = attributes_->Encode();
+
+ // Need to re-calculate |header| since |Append|, above, moves the data.
+ nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
+ pheader->nlmsg_len += attribute_string.GetLength();
+ result.Append(attribute_string);
+
+ return result;
+}
+
+bool GenericNetlinkMessage::InitAndStripHeader(ByteString *input) {
+ if (!input) {
+ LOG(ERROR) << "NULL input";
+ return false;
+ }
+ if (!NetlinkMessage::InitAndStripHeader(input)) {
+ return false;
+ }
+
+ // Read the genlmsghdr.
+ genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(input->GetData());
+ if (command_ != gnlh->cmd) {
+ LOG(WARNING) << "This object thinks it's a " << command_
+ << " but the message thinks it's a " << gnlh->cmd;
+ }
+
+ // Strip the genlmsghdr.
+ input->RemovePrefix(NLMSG_ALIGN(sizeof(struct genlmsghdr)));
+ return true;
+}
+
+void GenericNetlinkMessage::Print(int log_level) const {
+ SLOG(WiFi, log_level) << StringPrintf("Message %s (%d)",
+ command_string(),
+ command());
+ attributes_->Print(log_level, 1);
+}
+
+// Control Message
+
+const uint16_t ControlNetlinkMessage::kMessageType = GENL_ID_CTRL;
+
+bool ControlNetlinkMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
+ if (!const_msg) {
+ LOG(ERROR) << "Null |msg| parameter";
+ return false;
+ }
+ ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
+ const_msg->nlmsg_len);
+
+ if (!InitAndStripHeader(&message)) {
+ return false;
+ }
+
+ // Attributes.
+ // Parse the attributes from the nl message payload into the 'tb' array.
+ nlattr *tb[CTRL_ATTR_MAX + 1];
+ nla_parse(tb, CTRL_ATTR_MAX,
+ reinterpret_cast<nlattr *>(message.GetData()), message.GetLength(),
+ NULL);
+
+ for (int i = 0; i < CTRL_ATTR_MAX + 1; ++i) {
+ if (tb[i]) {
+ // TODO(wdg): When Nl80211Messages instantiate their own attributes,
+ // this call should, instead, call |SetAttributeFromNlAttr|.
+ attributes_->CreateAndInitAttribute(
+ i, tb[i], Bind(&NetlinkAttribute::NewControlAttributeFromId));
+ }
+ }
+ return true;
+}
+
+// Specific Control types.
+
+const uint8_t NewFamilyMessage::kCommand = CTRL_CMD_NEWFAMILY;
+const char NewFamilyMessage::kCommandString[] = "CTRL_CMD_NEWFAMILY";
+
+const uint8_t GetFamilyMessage::kCommand = CTRL_CMD_GETFAMILY;
+const char GetFamilyMessage::kCommandString[] = "CTRL_CMD_GETFAMILY";
+
+// static
+NetlinkMessage *ControlNetlinkMessage::CreateMessage(
+ const nlmsghdr *const_msg) {
+ if (!const_msg) {
+ LOG(ERROR) << "NULL |const_msg| parameter";
+ return NULL;
+ }
+ // Casting away constness since, while nlmsg_data doesn't change its
+ // parameter, it also doesn't declare its paramenter as const.
+ nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
+ void *payload = nlmsg_data(msg);
+ genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
+
+ switch (gnlh->cmd) {
+ case NewFamilyMessage::kCommand:
+ return new NewFamilyMessage();
+ case GetFamilyMessage::kCommand:
+ return new GetFamilyMessage();
+ default:
+ LOG(WARNING) << "Unknown/unhandled netlink control message " << gnlh->cmd;
+ break;
+ }
+ return NULL;
+}
+
+} // namespace shill.
diff --git a/generic_netlink_message.h b/generic_netlink_message.h
new file mode 100644
index 0000000..23bd067
--- /dev/null
+++ b/generic_netlink_message.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2013 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_GENERIC_NETLINK_MESSAGE_H_
+#define SHILL_GENERIC_NETLINK_MESSAGE_H_
+
+#include "shill/attribute_list.h"
+#include "shill/byte_string.h"
+#include "shill/netlink_message.h"
+#include "shill/refptr_types.h"
+
+struct nlmsghdr;
+
+namespace shill {
+
+// 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);
+
+ 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);
+ // 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);
+
+ // Message factory for all types of Control netlink message.
+ static NetlinkMessage *CreateMessage(const nlmsghdr *const_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);
+};
+
+} // namespace shill
+
+#endif // SHILL_GENERIC_NETLINK_MESSAGE_H_
diff --git a/netlink_message.cc b/netlink_message.cc
new file mode 100644
index 0000000..9da1b16
--- /dev/null
+++ b/netlink_message.cc
@@ -0,0 +1,303 @@
+// 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.
+
+#include "shill/netlink_message.h"
+
+#include <limits.h>
+#include <netlink/msg.h>
+#include <netlink/netlink.h>
+
+#include <map>
+#include <string>
+
+#include <base/format_macros.h>
+#include <base/stl_util.h>
+#include <base/stringprintf.h>
+
+#include "shill/logging.h"
+
+using base::StringAppendF;
+using base::StringPrintf;
+using std::map;
+using std::min;
+using std::string;
+
+namespace shill {
+
+const uint32_t NetlinkMessage::kBroadcastSequenceNumber = 0;
+const uint16_t NetlinkMessage::kIllegalMessageType = UINT16_MAX;
+
+// NetlinkMessage
+
+ByteString NetlinkMessage::EncodeHeader(uint32_t sequence_number) {
+ ByteString result;
+ if (message_type_ == kIllegalMessageType) {
+ LOG(ERROR) << "Message type not set";
+ return result;
+ }
+ sequence_number_ = sequence_number;
+ if (sequence_number_ == kBroadcastSequenceNumber) {
+ LOG(ERROR) << "Couldn't get a legal sequence number";
+ return result;
+ }
+
+ // Build netlink header.
+ nlmsghdr header;
+ size_t nlmsghdr_with_pad = NLMSG_ALIGN(sizeof(header));
+ header.nlmsg_len = nlmsghdr_with_pad;
+ header.nlmsg_type = message_type_;
+ header.nlmsg_flags = NLM_F_REQUEST | flags_;
+ header.nlmsg_seq = sequence_number_;
+ header.nlmsg_pid = getpid();
+
+ // Netlink header + pad.
+ result.Append(ByteString(reinterpret_cast<unsigned char *>(&header),
+ sizeof(header)));
+ result.Resize(nlmsghdr_with_pad); // Zero-fill pad space (if any).
+ return result;
+}
+
+bool NetlinkMessage::InitAndStripHeader(ByteString *input) {
+ if (!input) {
+ LOG(ERROR) << "NULL input";
+ return false;
+ }
+ if (input->GetLength() < sizeof(nlmsghdr)) {
+ LOG(ERROR) << "Insufficient input to extract nlmsghdr";
+ return false;
+ }
+
+ // Read the nlmsghdr.
+ nlmsghdr *header = reinterpret_cast<nlmsghdr *>(input->GetData());
+ message_type_ = header->nlmsg_type;
+ flags_ = header->nlmsg_flags;
+ sequence_number_ = header->nlmsg_seq;
+
+ // Strip the nlmsghdr.
+ input->RemovePrefix(NLMSG_ALIGN(sizeof(struct nlmsghdr)));
+ return true;
+}
+
+bool NetlinkMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
+ if (!const_msg) {
+ LOG(ERROR) << "Null |const_msg| parameter";
+ return false;
+ }
+ ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
+ const_msg->nlmsg_len);
+ if (!InitAndStripHeader(&message)) {
+ return false;
+ }
+ return true;
+}
+
+// static
+void NetlinkMessage::PrintBytes(int log_level, const unsigned char *buf,
+ size_t num_bytes) {
+ SLOG(WiFi, log_level) << "Netlink Message -- Examining Bytes";
+ if (!buf) {
+ SLOG(WiFi, log_level) << "<NULL Buffer>";
+ return;
+ }
+
+ if (num_bytes >= sizeof(nlmsghdr)) {
+ const nlmsghdr *header = reinterpret_cast<const nlmsghdr *>(buf);
+ SLOG(WiFi, log_level) << StringPrintf(
+ "len: %02x %02x %02x %02x = %u bytes",
+ buf[0], buf[1], buf[2], buf[3], header->nlmsg_len);
+
+ SLOG(WiFi, log_level) << StringPrintf(
+ "type | flags: %02x %02x %02x %02x - type:%u flags:%s%s%s%s%s",
+ buf[4], buf[5], buf[6], buf[7], header->nlmsg_type,
+ ((header->nlmsg_flags & NLM_F_REQUEST) ? " REQUEST" : ""),
+ ((header->nlmsg_flags & NLM_F_MULTI) ? " MULTI" : ""),
+ ((header->nlmsg_flags & NLM_F_ACK) ? " ACK" : ""),
+ ((header->nlmsg_flags & NLM_F_ECHO) ? " ECHO" : ""),
+ ((header->nlmsg_flags & NLM_F_DUMP_INTR) ? " BAD-SEQ" : ""));
+
+ SLOG(WiFi, log_level) << StringPrintf(
+ "sequence: %02x %02x %02x %02x = %u",
+ buf[8], buf[9], buf[10], buf[11], header->nlmsg_seq);
+ SLOG(WiFi, log_level) << StringPrintf(
+ "pid: %02x %02x %02x %02x = %u",
+ buf[12], buf[13], buf[14], buf[15], header->nlmsg_pid);
+ buf += sizeof(nlmsghdr);
+ num_bytes -= sizeof(nlmsghdr);
+ } else {
+ SLOG(WiFi, log_level) << "Not enough bytes (" << num_bytes
+ << ") for a complete nlmsghdr (requires "
+ << sizeof(nlmsghdr) << ").";
+ }
+
+ while (num_bytes) {
+ string output;
+ size_t bytes_this_row = min(num_bytes, static_cast<size_t>(32));
+ for (size_t i = 0; i < bytes_this_row; ++i) {
+ StringAppendF(&output, " %02x", *buf++);
+ }
+ SLOG(WiFi, log_level) << output;
+ num_bytes -= bytes_this_row;
+ }
+}
+
+// ErrorAckMessage.
+
+const uint16_t ErrorAckMessage::kMessageType = NLMSG_ERROR;
+
+bool ErrorAckMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
+ if (!const_msg) {
+ LOG(ERROR) << "Null |const_msg| parameter";
+ return false;
+ }
+ ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
+ const_msg->nlmsg_len);
+ if (!InitAndStripHeader(&message)) {
+ return false;
+ }
+
+ // Get the error code from the payload.
+ error_ = *(reinterpret_cast<const uint32_t *>(message.GetConstData()));
+ return true;
+}
+
+ByteString ErrorAckMessage::Encode(uint32_t sequence_number) {
+ LOG(ERROR) << "We're not supposed to send errors or Acks to the kernel";
+ return ByteString();
+}
+
+string ErrorAckMessage::ToString() const {
+ string output;
+ if (error()) {
+ StringAppendF(&output, "NL80211_ERROR 0x%" PRIx32 ": %s",
+ -error_, strerror(-error_));
+ } else {
+ StringAppendF(&output, "ACK");
+ }
+ return output;
+}
+
+void ErrorAckMessage::Print(int log_level) const {
+ SLOG(WiFi, log_level) << ToString();
+}
+
+// NoopMessage.
+
+const uint16_t NoopMessage::kMessageType = NLMSG_NOOP;
+
+ByteString NoopMessage::Encode(uint32_t sequence_number) {
+ LOG(ERROR) << "We're not supposed to send NOOP to the kernel";
+ return ByteString();
+}
+
+void NoopMessage::Print(int log_level) const {
+ SLOG(WiFi, log_level) << ToString();
+}
+
+// DoneMessage.
+
+const uint16_t DoneMessage::kMessageType = NLMSG_DONE;
+
+ByteString DoneMessage::Encode(uint32_t sequence_number) {
+ LOG(ERROR)
+ << "We're not supposed to send Done messages (are we?) to the kernel";
+ return ByteString();
+}
+
+void DoneMessage::Print(int log_level) const {
+ SLOG(WiFi, log_level) << ToString();
+}
+
+// OverrunMessage.
+
+const uint16_t OverrunMessage::kMessageType = NLMSG_OVERRUN;
+
+ByteString OverrunMessage::Encode(uint32_t sequence_number) {
+ LOG(ERROR) << "We're not supposed to send Overruns to the kernel";
+ return ByteString();
+}
+
+void OverrunMessage::Print(int log_level) const {
+ SLOG(WiFi, log_level) << ToString();
+}
+
+// UnknownMessage.
+
+ByteString UnknownMessage::Encode(uint32_t sequence_number) {
+ LOG(ERROR) << "We're not supposed to send UNKNOWN messages to the kernel";
+ return ByteString();
+}
+
+void UnknownMessage::Print(int log_level) const {
+ int total_bytes = message_body_.GetLength();
+ const uint8_t *const_data = message_body_.GetConstData();
+
+ string output = StringPrintf("%d bytes:", total_bytes);
+ for (int i = 0; i < total_bytes; ++i) {
+ StringAppendF(&output, " 0x%02x", const_data[i]);
+ }
+ SLOG(WiFi, log_level) << output;
+}
+
+//
+// Factory class.
+//
+
+bool NetlinkMessageFactory::AddFactoryMethod(uint16_t message_type,
+ FactoryMethod factory) {
+ if (ContainsKey(factories_, message_type)) {
+ LOG(WARNING) << "Message type " << message_type << " already exists.";
+ return false;
+ }
+ if (message_type == NetlinkMessage::kIllegalMessageType) {
+ LOG(ERROR) << "Not installing factory for illegal message type.";
+ return false;
+ }
+ factories_[message_type] = factory;
+ return true;
+}
+
+NetlinkMessage *NetlinkMessageFactory::CreateMessage(
+ const nlmsghdr *const_msg) const {
+ if (!const_msg) {
+ LOG(ERROR) << "NULL |const_msg| parameter";
+ return NULL;
+ }
+
+ scoped_ptr<NetlinkMessage> message;
+
+ if (const_msg->nlmsg_type == NoopMessage::kMessageType) {
+ message.reset(new NoopMessage());
+ } else if (const_msg->nlmsg_type == DoneMessage::kMessageType) {
+ message.reset(new DoneMessage());
+ } else if (const_msg->nlmsg_type == OverrunMessage::kMessageType) {
+ message.reset(new OverrunMessage());
+ } else if (const_msg->nlmsg_type == ErrorAckMessage::kMessageType) {
+ message.reset(new ErrorAckMessage());
+ } else if (ContainsKey(factories_, const_msg->nlmsg_type)) {
+ map<uint16_t, FactoryMethod>::const_iterator factory;
+ factory = factories_.find(const_msg->nlmsg_type);
+ message.reset(factory->second.Run(const_msg));
+ }
+
+ // If no factory exists for this message _or_ if a factory exists but it
+ // failed, there'll be no message. Handle either of those cases, by
+ // creating an |UnknownMessage|.
+ if (!message) {
+ // Casting away constness since, while nlmsg_data doesn't change its
+ // parameter, it also doesn't declare its paramenter as const.
+ nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
+ ByteString payload(reinterpret_cast<char *>(nlmsg_data(msg)),
+ nlmsg_datalen(msg));
+ message.reset(new UnknownMessage(msg->nlmsg_type, payload));
+ }
+
+ if (!message->InitFromNlmsg(const_msg)) {
+ LOG(ERROR) << "Message did not initialize properly";
+ return NULL;
+ }
+
+ return message.release();
+}
+
+} // namespace shill.
diff --git a/netlink_message.h b/netlink_message.h
new file mode 100644
index 0000000..e19e6b0
--- /dev/null
+++ b/netlink_message.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2013 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_NETLINK_MESSAGE_H_
+#define SHILL_NETLINK_MESSAGE_H_
+
+#include <map>
+#include <string>
+
+#include <base/bind.h>
+
+#include <gtest/gtest.h> // for FRIEND_TEST.
+
+#include "shill/byte_string.h"
+
+struct nlmsghdr;
+
+namespace shill {
+
+// 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::GetFamily:
+//
+// nl80211_type = config80211_->GetFamily(Nl80211Message::kMessageType);
+//
+// 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) = 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);
+ // 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);
+ 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);
+ 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);
+ 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);
+ virtual void Print(int log_level) const;
+ std::string ToString() const { return "<OVERRUN - data lost>"; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OverrunMessage);
+};
+
+
+class UnknownMessage : public NetlinkMessage {
+ public:
+ UnknownMessage(uint16_t message_type, ByteString message_body) :
+ NetlinkMessage(message_type), message_body_(message_body) {}
+ virtual ByteString Encode(uint32_t sequence_number);
+ virtual void Print(int log_level) const;
+
+ private:
+ ByteString message_body_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
+};
+
+
+//
+// Factory class.
+//
+
+class NetlinkMessageFactory {
+ public:
+ typedef base::Callback<NetlinkMessage *(const nlmsghdr *msg)> FactoryMethod;
+
+ NetlinkMessageFactory() {}
+
+ // Adds a message factory for a specific message_type. Intended to be used
+ // at initialization.
+ bool AddFactoryMethod(uint16_t message_type, FactoryMethod factory);
+
+ // Ownership of the message is passed to the caller and, as such, he should
+ // delete it.
+ NetlinkMessage *CreateMessage(const nlmsghdr *msg) const;
+
+ private:
+ std::map<uint16_t, FactoryMethod> factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetlinkMessageFactory);
+};
+
+} // namespace shill
+
+#endif // SHILL_NETLINK_MESSAGE_H_
diff --git a/netlink_socket.cc b/netlink_socket.cc
index 50790f6..d791702 100644
--- a/netlink_socket.cc
+++ b/netlink_socket.cc
@@ -5,10 +5,11 @@
#include "shill/netlink_socket.h"
#include <linux/if_packet.h>
+#include <linux/netlink.h>
#include <sys/socket.h>
#include "shill/logging.h"
-#include "shill/nl80211_message.h"
+#include "shill/netlink_message.h"
#include "shill/sockets.h"
// This is from a version of linux/socket.h that we don't have.
diff --git a/nl80211_message.cc b/nl80211_message.cc
index c0c2400..9b7e982 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -25,18 +25,14 @@
#include "shill/nl80211_message.h"
#include <limits.h>
-#include <netlink/attr.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
-#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <base/bind.h>
-#include <base/format_macros.h>
-#include <base/stl_util.h>
#include <base/stringprintf.h>
#include "shill/attribute_list.h"
@@ -48,9 +44,7 @@
using base::Bind;
using base::LazyInstance;
using base::StringAppendF;
-using base::StringPrintf;
using std::map;
-using std::min;
using std::string;
using std::vector;
@@ -64,9 +58,6 @@
const uint8_t Nl80211Frame::kMinimumFrameByteCount = 26;
const uint8_t Nl80211Frame::kFrameTypeMask = 0xfc;
-const uint32_t NetlinkMessage::kBroadcastSequenceNumber = 0;
-const uint16_t NetlinkMessage::kIllegalMessageType = UINT16_MAX;
-
const char Nl80211Message::kBogusMacAddress[] = "XX:XX:XX:XX:XX:XX";
const unsigned int Nl80211Message::kEthernetAddressBytes = 6;
const char Nl80211Message::kMessageTypeString[] = "nl80211";
@@ -74,293 +65,6 @@
map<uint16_t, string> *Nl80211Message::status_code_string_ = NULL;
uint16_t Nl80211Message::nl80211_message_type_ = kIllegalMessageType;
-// NetlinkMessage
-
-ByteString NetlinkMessage::EncodeHeader(uint32_t sequence_number) {
- ByteString result;
- if (message_type_ == kIllegalMessageType) {
- LOG(ERROR) << "Message type not set";
- return result;
- }
- sequence_number_ = sequence_number;
- if (sequence_number_ == kBroadcastSequenceNumber) {
- LOG(ERROR) << "Couldn't get a legal sequence number";
- return result;
- }
-
- // Build netlink header.
- nlmsghdr header;
- size_t nlmsghdr_with_pad = NLMSG_ALIGN(sizeof(header));
- header.nlmsg_len = nlmsghdr_with_pad;
- header.nlmsg_type = message_type_;
- header.nlmsg_flags = NLM_F_REQUEST | flags_;
- header.nlmsg_seq = sequence_number_;
- header.nlmsg_pid = getpid();
-
- // Netlink header + pad.
- result.Append(ByteString(reinterpret_cast<unsigned char *>(&header),
- sizeof(header)));
- result.Resize(nlmsghdr_with_pad); // Zero-fill pad space (if any).
- return result;
-}
-
-bool NetlinkMessage::InitAndStripHeader(ByteString *input) {
- if (!input) {
- LOG(ERROR) << "NULL input";
- return false;
- }
- if (input->GetLength() < sizeof(nlmsghdr)) {
- LOG(ERROR) << "Insufficient input to extract nlmsghdr";
- return false;
- }
-
- // Read the nlmsghdr.
- nlmsghdr *header = reinterpret_cast<nlmsghdr *>(input->GetData());
- message_type_ = header->nlmsg_type;
- flags_ = header->nlmsg_flags;
- sequence_number_ = header->nlmsg_seq;
-
- // Strip the nlmsghdr.
- input->RemovePrefix(NLMSG_ALIGN(sizeof(struct nlmsghdr)));
- return true;
-}
-
-bool NetlinkMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
- if (!const_msg) {
- LOG(ERROR) << "Null |const_msg| parameter";
- return false;
- }
- ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
- const_msg->nlmsg_len);
- if (!InitAndStripHeader(&message)) {
- return false;
- }
- return true;
-}
-
-// static
-void NetlinkMessage::PrintBytes(int log_level, const unsigned char *buf,
- size_t num_bytes) {
- SLOG(WiFi, log_level) << "Netlink Message -- Examining Bytes";
- if (!buf) {
- SLOG(WiFi, log_level) << "<NULL Buffer>";
- return;
- }
-
- if (num_bytes >= sizeof(nlmsghdr)) {
- const nlmsghdr *header = reinterpret_cast<const nlmsghdr *>(buf);
- SLOG(WiFi, log_level) << StringPrintf(
- "len: %02x %02x %02x %02x = %u bytes",
- buf[0], buf[1], buf[2], buf[3], header->nlmsg_len);
-
- SLOG(WiFi, log_level) << StringPrintf(
- "type | flags: %02x %02x %02x %02x - type:%u flags:%s%s%s%s%s",
- buf[4], buf[5], buf[6], buf[7], header->nlmsg_type,
- ((header->nlmsg_flags & NLM_F_REQUEST) ? " REQUEST" : ""),
- ((header->nlmsg_flags & NLM_F_MULTI) ? " MULTI" : ""),
- ((header->nlmsg_flags & NLM_F_ACK) ? " ACK" : ""),
- ((header->nlmsg_flags & NLM_F_ECHO) ? " ECHO" : ""),
- ((header->nlmsg_flags & NLM_F_DUMP_INTR) ? " BAD-SEQ" : ""));
-
- SLOG(WiFi, log_level) << StringPrintf(
- "sequence: %02x %02x %02x %02x = %u",
- buf[8], buf[9], buf[10], buf[11], header->nlmsg_seq);
- SLOG(WiFi, log_level) << StringPrintf(
- "pid: %02x %02x %02x %02x = %u",
- buf[12], buf[13], buf[14], buf[15], header->nlmsg_pid);
- buf += sizeof(nlmsghdr);
- num_bytes -= sizeof(nlmsghdr);
- } else {
- SLOG(WiFi, log_level) << "Not enough bytes (" << num_bytes
- << ") for a complete nlmsghdr (requires "
- << sizeof(nlmsghdr) << ").";
- }
-
- while (num_bytes) {
- string output;
- size_t bytes_this_row = min(num_bytes, static_cast<size_t>(32));
- for (size_t i = 0; i < bytes_this_row; ++i) {
- StringAppendF(&output, " %02x", *buf++);
- }
- SLOG(WiFi, log_level) << output;
- num_bytes -= bytes_this_row;
- }
-}
-
-// ErrorAckMessage.
-
-const uint16_t ErrorAckMessage::kMessageType = NLMSG_ERROR;
-
-bool ErrorAckMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
- if (!const_msg) {
- LOG(ERROR) << "Null |const_msg| parameter";
- return false;
- }
- ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
- const_msg->nlmsg_len);
- if (!InitAndStripHeader(&message)) {
- return false;
- }
-
- // Get the error code from the payload.
- error_ = *(reinterpret_cast<const uint32_t *>(message.GetConstData()));
- return true;
-}
-
-ByteString ErrorAckMessage::Encode(uint32_t sequence_number) {
- LOG(ERROR) << "We're not supposed to send errors or Acks to the kernel";
- return ByteString();
-}
-
-string ErrorAckMessage::ToString() const {
- string output;
- if (error()) {
- StringAppendF(&output, "NL80211_ERROR 0x%" PRIx32 ": %s",
- -error_, strerror(-error_));
- } else {
- StringAppendF(&output, "ACK");
- }
- return output;
-}
-
-void ErrorAckMessage::Print(int log_level) const {
- SLOG(WiFi, log_level) << ToString();
-}
-
-// NoopMessage.
-
-const uint16_t NoopMessage::kMessageType = NLMSG_NOOP;
-
-ByteString NoopMessage::Encode(uint32_t sequence_number) {
- LOG(ERROR) << "We're not supposed to send NOOP to the kernel";
- return ByteString();
-}
-
-void NoopMessage::Print(int log_level) const {
- SLOG(WiFi, log_level) << ToString();
-}
-
-// DoneMessage.
-
-const uint16_t DoneMessage::kMessageType = NLMSG_DONE;
-
-ByteString DoneMessage::Encode(uint32_t sequence_number) {
- LOG(ERROR)
- << "We're not supposed to send Done messages (are we?) to the kernel";
- return ByteString();
-}
-
-void DoneMessage::Print(int log_level) const {
- SLOG(WiFi, log_level) << ToString();
-}
-
-// OverrunMessage.
-
-const uint16_t OverrunMessage::kMessageType = NLMSG_OVERRUN;
-
-ByteString OverrunMessage::Encode(uint32_t sequence_number) {
- LOG(ERROR) << "We're not supposed to send Overruns to the kernel";
- return ByteString();
-}
-
-void OverrunMessage::Print(int log_level) const {
- SLOG(WiFi, log_level) << ToString();
-}
-
-// UnknownMessage.
-
-ByteString UnknownMessage::Encode(uint32_t sequence_number) {
- LOG(ERROR) << "We're not supposed to send UNKNOWN messages to the kernel";
- return ByteString();
-}
-
-void UnknownMessage::Print(int log_level) const {
- int total_bytes = message_body_.GetLength();
- const uint8_t *const_data = message_body_.GetConstData();
-
- string output = StringPrintf("%d bytes:", total_bytes);
- for (int i = 0; i < total_bytes; ++i) {
- StringAppendF(&output, " 0x%02x", const_data[i]);
- }
- SLOG(WiFi, log_level) << output;
-}
-
-// GenericNetlinkMessage
-
-ByteString GenericNetlinkMessage::EncodeHeader(uint32_t sequence_number) {
- // Build nlmsghdr.
- ByteString result(NetlinkMessage::EncodeHeader(sequence_number));
- if (result.GetLength() == 0) {
- LOG(ERROR) << "Couldn't encode message header.";
- return result;
- }
-
- // Build and append the genl message header.
- genlmsghdr genl_header;
- genl_header.cmd = command();
- genl_header.version = 1;
- genl_header.reserved = 0;
-
- ByteString genl_header_string(
- reinterpret_cast<unsigned char *>(&genl_header), sizeof(genl_header));
- size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
- genl_header_string.Resize(genlmsghdr_with_pad); // Zero-fill.
-
- nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
- pheader->nlmsg_len += genlmsghdr_with_pad;
- result.Append(genl_header_string);
- return result;
-}
-
-ByteString GenericNetlinkMessage::Encode(uint32_t sequence_number) {
- ByteString result(EncodeHeader(sequence_number));
- if (result.GetLength() == 0) {
- LOG(ERROR) << "Couldn't encode message header.";
- return result;
- }
-
- // Build and append attributes (padding is included by
- // AttributeList::Encode).
- ByteString attribute_string = attributes_->Encode();
-
- // Need to re-calculate |header| since |Append|, above, moves the data.
- nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
- pheader->nlmsg_len += attribute_string.GetLength();
- result.Append(attribute_string);
-
- return result;
-}
-
-bool GenericNetlinkMessage::InitAndStripHeader(ByteString *input) {
- if (!input) {
- LOG(ERROR) << "NULL input";
- return false;
- }
- if (!NetlinkMessage::InitAndStripHeader(input)) {
- return false;
- }
-
- // Read the genlmsghdr.
- genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(input->GetData());
- if (command_ != gnlh->cmd) {
- LOG(WARNING) << "This object thinks it's a " << command_
- << " but the message thinks it's a " << gnlh->cmd;
- }
-
- // Strip the genlmsghdr.
- input->RemovePrefix(NLMSG_ALIGN(sizeof(struct genlmsghdr)));
- return true;
-}
-
-void GenericNetlinkMessage::Print(int log_level) const {
- SLOG(WiFi, log_level) << StringPrintf("Message %s (%d)",
- command_string(),
- command());
- attributes_->Print(log_level, 1);
-}
-
-// Nl80211Message
-
// static
void Nl80211Message::SetMessageType(uint16_t message_type) {
if (message_type == NetlinkMessage::kIllegalMessageType) {
@@ -715,74 +419,6 @@
return match->second;
}
-
-// Control Message
-
-const uint16_t ControlNetlinkMessage::kMessageType = GENL_ID_CTRL;
-
-bool ControlNetlinkMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
- if (!const_msg) {
- LOG(ERROR) << "Null |msg| parameter";
- return false;
- }
- ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
- const_msg->nlmsg_len);
-
- if (!InitAndStripHeader(&message)) {
- return false;
- }
-
- // Attributes.
- // Parse the attributes from the nl message payload into the 'tb' array.
- nlattr *tb[CTRL_ATTR_MAX + 1];
- nla_parse(tb, CTRL_ATTR_MAX,
- reinterpret_cast<nlattr *>(message.GetData()), message.GetLength(),
- NULL);
-
- for (int i = 0; i < CTRL_ATTR_MAX + 1; ++i) {
- if (tb[i]) {
- // TODO(wdg): When Nl80211Messages instantiate their own attributes,
- // this call should, instead, call |SetAttributeFromNlAttr|.
- attributes_->CreateAndInitAttribute(
- i, tb[i], Bind(&NetlinkAttribute::NewControlAttributeFromId));
- }
- }
- return true;
-}
-
-// Specific Control types.
-
-const uint8_t NewFamilyMessage::kCommand = CTRL_CMD_NEWFAMILY;
-const char NewFamilyMessage::kCommandString[] = "CTRL_CMD_NEWFAMILY";
-
-const uint8_t GetFamilyMessage::kCommand = CTRL_CMD_GETFAMILY;
-const char GetFamilyMessage::kCommandString[] = "CTRL_CMD_GETFAMILY";
-
-// static
-NetlinkMessage *ControlNetlinkMessage::CreateMessage(
- const nlmsghdr *const_msg) {
- if (!const_msg) {
- LOG(ERROR) << "NULL |const_msg| parameter";
- return NULL;
- }
- // Casting away constness since, while nlmsg_data doesn't change its
- // parameter, it also doesn't declare its paramenter as const.
- nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
- void *payload = nlmsg_data(msg);
- genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
-
- switch (gnlh->cmd) {
- case NewFamilyMessage::kCommand:
- return new NewFamilyMessage();
- case GetFamilyMessage::kCommand:
- return new GetFamilyMessage();
- default:
- LOG(WARNING) << "Unknown/unhandled netlink control message " << gnlh->cmd;
- break;
- }
- return NULL;
-}
-
// Nl80211Frame
Nl80211Frame::Nl80211Frame(const ByteString &raw_frame)
@@ -1051,67 +687,6 @@
}
//
-// Factory class.
-//
-
-bool NetlinkMessageFactory::AddFactoryMethod(uint16_t message_type,
- FactoryMethod factory) {
- if (ContainsKey(factories_, message_type)) {
- LOG(WARNING) << "Message type " << message_type << " already exists.";
- return false;
- }
- if (message_type == NetlinkMessage::kIllegalMessageType) {
- LOG(ERROR) << "Not installing factory for illegal message type.";
- return false;
- }
- factories_[message_type] = factory;
- return true;
-}
-
-NetlinkMessage *NetlinkMessageFactory::CreateMessage(
- const nlmsghdr *const_msg) const {
- if (!const_msg) {
- LOG(ERROR) << "NULL |const_msg| parameter";
- return NULL;
- }
-
- scoped_ptr<NetlinkMessage> message;
-
- if (const_msg->nlmsg_type == NoopMessage::kMessageType) {
- message.reset(new NoopMessage());
- } else if (const_msg->nlmsg_type == DoneMessage::kMessageType) {
- message.reset(new DoneMessage());
- } else if (const_msg->nlmsg_type == OverrunMessage::kMessageType) {
- message.reset(new OverrunMessage());
- } else if (const_msg->nlmsg_type == ErrorAckMessage::kMessageType) {
- message.reset(new ErrorAckMessage());
- } else if (ContainsKey(factories_, const_msg->nlmsg_type)) {
- map<uint16_t, FactoryMethod>::const_iterator factory;
- factory = factories_.find(const_msg->nlmsg_type);
- message.reset(factory->second.Run(const_msg));
- }
-
- // If no factory exists for this message _or_ if a factory exists but it
- // failed, there'll be no message. Handle either of those cases, by
- // creating an |UnknownMessage|.
- if (!message) {
- // Casting away constness since, while nlmsg_data doesn't change its
- // parameter, it also doesn't declare its paramenter as const.
- nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
- ByteString payload(reinterpret_cast<char *>(nlmsg_data(msg)),
- nlmsg_datalen(msg));
- message.reset(new UnknownMessage(msg->nlmsg_type, payload));
- }
-
- if (!message->InitFromNlmsg(const_msg)) {
- LOG(ERROR) << "Message did not initialize properly";
- return NULL;
- }
-
- return message.release();
-}
-
-//
// Data Collector
//
diff --git a/nl80211_message.h b/nl80211_message.h
index 33b3dbc..6d5c8ac 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -9,304 +9,15 @@
#include <string>
#include <vector>
-#include <base/basictypes.h>
#include <base/lazy_instance.h>
-#include <gtest/gtest.h> // for FRIEND_TEST.
-
-#include "shill/attribute_list.h"
#include "shill/byte_string.h"
-#include "shill/refptr_types.h"
+#include "shill/generic_netlink_message.h"
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::GetFamily:
-//
-// nl80211_type = config80211_->GetFamily(Nl80211Message::kMessageType);
-//
-// 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) = 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);
- // 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);
- 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);
- 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);
- 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);
- virtual void Print(int log_level) const;
- std::string ToString() const { return "<OVERRUN - data lost>"; }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OverrunMessage);
-};
-
-
-class UnknownMessage : public NetlinkMessage {
- public:
- UnknownMessage(uint16_t message_type, ByteString message_body) :
- NetlinkMessage(message_type), message_body_(message_body) {}
- virtual ByteString Encode(uint32_t sequence_number);
- virtual void Print(int log_level) const;
-
- private:
- ByteString message_body_;
-
- DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
-};
-
-// 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);
-
- 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);
- // 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);
-
- // Message factory for all types of Control netlink message.
- static NetlinkMessage *CreateMessage(const nlmsghdr *const_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 {
@@ -717,31 +428,6 @@
DISALLOW_COPY_AND_ASSIGN(UnprotDisassociateMessage);
};
-
-//
-// Factory class.
-//
-
-class NetlinkMessageFactory {
- public:
- typedef base::Callback<NetlinkMessage *(const nlmsghdr *msg)> FactoryMethod;
-
- NetlinkMessageFactory() {}
-
- // Adds a message factory for a specific message_type. Intended to be used
- // at initialization.
- bool AddFactoryMethod(uint16_t message_type, FactoryMethod factory);
-
- // Ownership of the message is passed to the caller and, as such, he should
- // delete it.
- NetlinkMessage *CreateMessage(const nlmsghdr *msg) const;
-
- private:
- std::map<uint16_t, FactoryMethod> factories_;
-
- 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.