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/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.