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.