shill: Splits the netlink message factory between message types.
Provides a mechanism to register message factories with
|NetlinkMessageFactory|. Splits |NetlinkMessageFactory| into a generic
factory and one for |ControlNetlinkMessage| and |Nl80211Message|. Uses
the registration mechanism to install the control and nl80211 message
factories.
This CL also moves |UnknownMessage| from being a subtype of
|Nl80211Message| to being a subtype of |NetlinkMessage|.
Lucy has some 'splaining to do: At startup time, the message type isn't
known for some types of netlink message (notably, the nl80211 class of
messages). This message type is discovered, in the code, through the
|Config80211::AddFamilyByString| call. This CL adds a message factory
closure to that call so that Config80211 can install the factory when
the code knows the message type against which to register the factory.
BUG=chromium:220372
TEST=unittest and manual (restarted shill with log=-10,wifi and observed
messages of all types being created for netlink messages coming from the
kernel).
Change-Id: I496b405c20ef98b6e23e96e8088fc542ff29264c
Reviewed-on: https://gerrit.chromium.org/gerrit/45314
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/nl80211_message.cc b/nl80211_message.cc
index d990a76..c0c2400 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -36,6 +36,7 @@
#include <base/bind.h>
#include <base/format_macros.h>
+#include <base/stl_util.h>
#include <base/stringprintf.h>
#include "shill/attribute_list.h"
@@ -186,6 +187,8 @@
}
}
+// ErrorAckMessage.
+
const uint16_t ErrorAckMessage::kMessageType = NLMSG_ERROR;
bool ErrorAckMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
@@ -224,6 +227,8 @@
SLOG(WiFi, log_level) << ToString();
}
+// NoopMessage.
+
const uint16_t NoopMessage::kMessageType = NLMSG_NOOP;
ByteString NoopMessage::Encode(uint32_t sequence_number) {
@@ -235,6 +240,8 @@
SLOG(WiFi, log_level) << ToString();
}
+// DoneMessage.
+
const uint16_t DoneMessage::kMessageType = NLMSG_DONE;
ByteString DoneMessage::Encode(uint32_t sequence_number) {
@@ -247,6 +254,8 @@
SLOG(WiFi, log_level) << ToString();
}
+// OverrunMessage.
+
const uint16_t OverrunMessage::kMessageType = NLMSG_OVERRUN;
ByteString OverrunMessage::Encode(uint32_t sequence_number) {
@@ -258,6 +267,24 @@
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) {
@@ -731,6 +758,31 @@
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)
@@ -831,7 +883,6 @@
return frame_.Equals(other.frame_);
}
-
//
// Specific Nl80211Message types.
//
@@ -918,9 +969,6 @@
const uint8_t TriggerScanMessage::kCommand = NL80211_CMD_TRIGGER_SCAN;
const char TriggerScanMessage::kCommandString[] = "NL80211_CMD_TRIGGER_SCAN";
-const uint8_t UnknownMessage::kCommand = 0xff;
-const char UnknownMessage::kCommandString[] = "<Unknown Message Type>";
-
const uint8_t UnprotDeauthenticateMessage::kCommand =
NL80211_CMD_UNPROT_DEAUTHENTICATE;
const char UnprotDeauthenticateMessage::kCommandString[] =
@@ -931,102 +979,131 @@
const char UnprotDisassociateMessage::kCommandString[] =
"NL80211_CMD_UNPROT_DISASSOCIATE";
+// static
+NetlinkMessage *Nl80211Message::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);
+ scoped_ptr<NetlinkMessage> message;
+
+ switch (gnlh->cmd) {
+ case AssociateMessage::kCommand:
+ return new AssociateMessage();
+ case AuthenticateMessage::kCommand:
+ return new AuthenticateMessage();
+ case CancelRemainOnChannelMessage::kCommand:
+ return new CancelRemainOnChannelMessage();
+ case ConnectMessage::kCommand:
+ return new ConnectMessage();
+ case DeauthenticateMessage::kCommand:
+ return new DeauthenticateMessage();
+ case DeleteStationMessage::kCommand:
+ return new DeleteStationMessage();
+ case DisassociateMessage::kCommand:
+ return new DisassociateMessage();
+ case DisconnectMessage::kCommand:
+ return new DisconnectMessage();
+ case FrameTxStatusMessage::kCommand:
+ return new FrameTxStatusMessage();
+ case GetRegMessage::kCommand:
+ return new GetRegMessage();
+ case JoinIbssMessage::kCommand:
+ return new JoinIbssMessage();
+ case MichaelMicFailureMessage::kCommand:
+ return new MichaelMicFailureMessage();
+ case NewScanResultsMessage::kCommand:
+ return new NewScanResultsMessage();
+ case NewStationMessage::kCommand:
+ return new NewStationMessage();
+ case NewWifiMessage::kCommand:
+ return new NewWifiMessage();
+ case NotifyCqmMessage::kCommand:
+ return new NotifyCqmMessage();
+ case PmksaCandidateMessage::kCommand:
+ return new PmksaCandidateMessage();
+ case RegBeaconHintMessage::kCommand:
+ return new RegBeaconHintMessage();
+ case RegChangeMessage::kCommand:
+ return new RegChangeMessage();
+ case RemainOnChannelMessage::kCommand:
+ return new RemainOnChannelMessage();
+ case RoamMessage::kCommand:
+ return new RoamMessage();
+ case ScanAbortedMessage::kCommand:
+ return new ScanAbortedMessage();
+ case TriggerScanMessage::kCommand:
+ return new TriggerScanMessage();
+ case UnprotDeauthenticateMessage::kCommand:
+ return new UnprotDeauthenticateMessage();
+ case UnprotDisassociateMessage::kCommand:
+ return new UnprotDisassociateMessage();
+ default:
+ LOG(WARNING) << "Unknown/unhandled netlink nl80211 message " << gnlh->cmd;
+ break;
+ }
+ return NULL;
+}
+
//
// Factory class.
//
-// TODO(wdg): later, each message_type should register its own callback for
-// creating messages. For now, however, this is much easier.
-NetlinkMessage *NetlinkMessageFactory::CreateMessage(nlmsghdr *msg) {
- if (!msg) {
- LOG(ERROR) << "NULL |msg| parameter";
+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;
- void *payload = nlmsg_data(msg);
- if (msg->nlmsg_type == NoopMessage::kMessageType) {
+ if (const_msg->nlmsg_type == NoopMessage::kMessageType) {
message.reset(new NoopMessage());
- } else if (msg->nlmsg_type == DoneMessage::kMessageType) {
+ } else if (const_msg->nlmsg_type == DoneMessage::kMessageType) {
message.reset(new DoneMessage());
- } else if (msg->nlmsg_type == OverrunMessage::kMessageType) {
+ } else if (const_msg->nlmsg_type == OverrunMessage::kMessageType) {
message.reset(new OverrunMessage());
- } else if (msg->nlmsg_type == ErrorAckMessage::kMessageType) {
+ } else if (const_msg->nlmsg_type == ErrorAckMessage::kMessageType) {
message.reset(new ErrorAckMessage());
- } else if (msg->nlmsg_type == ControlNetlinkMessage::kMessageType) {
- genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
-
- switch (gnlh->cmd) {
- case NewFamilyMessage::kCommand:
- message.reset(new NewFamilyMessage()); break;
- case GetFamilyMessage::kCommand:
- message.reset(new GetFamilyMessage()); break;
-
- default:
- message.reset(new UnknownMessage(gnlh->cmd)); break;
- }
- } else {
- genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
-
- switch (gnlh->cmd) {
- case AssociateMessage::kCommand:
- message.reset(new AssociateMessage()); break;
- case AuthenticateMessage::kCommand:
- message.reset(new AuthenticateMessage()); break;
- case CancelRemainOnChannelMessage::kCommand:
- message.reset(new CancelRemainOnChannelMessage()); break;
- case ConnectMessage::kCommand:
- message.reset(new ConnectMessage()); break;
- case DeauthenticateMessage::kCommand:
- message.reset(new DeauthenticateMessage()); break;
- case DeleteStationMessage::kCommand:
- message.reset(new DeleteStationMessage()); break;
- case DisassociateMessage::kCommand:
- message.reset(new DisassociateMessage()); break;
- case DisconnectMessage::kCommand:
- message.reset(new DisconnectMessage()); break;
- case FrameTxStatusMessage::kCommand:
- message.reset(new FrameTxStatusMessage()); break;
- case GetRegMessage::kCommand:
- message.reset(new GetRegMessage()); break;
- case JoinIbssMessage::kCommand:
- message.reset(new JoinIbssMessage()); break;
- case MichaelMicFailureMessage::kCommand:
- message.reset(new MichaelMicFailureMessage()); break;
- case NewScanResultsMessage::kCommand:
- message.reset(new NewScanResultsMessage()); break;
- case NewStationMessage::kCommand:
- message.reset(new NewStationMessage()); break;
- case NewWifiMessage::kCommand:
- message.reset(new NewWifiMessage()); break;
- case NotifyCqmMessage::kCommand:
- message.reset(new NotifyCqmMessage()); break;
- case PmksaCandidateMessage::kCommand:
- message.reset(new PmksaCandidateMessage()); break;
- case RegBeaconHintMessage::kCommand:
- message.reset(new RegBeaconHintMessage()); break;
- case RegChangeMessage::kCommand:
- message.reset(new RegChangeMessage()); break;
- case RemainOnChannelMessage::kCommand:
- message.reset(new RemainOnChannelMessage()); break;
- case RoamMessage::kCommand:
- message.reset(new RoamMessage()); break;
- case ScanAbortedMessage::kCommand:
- message.reset(new ScanAbortedMessage()); break;
- case TriggerScanMessage::kCommand:
- message.reset(new TriggerScanMessage()); break;
- case UnprotDeauthenticateMessage::kCommand:
- message.reset(new UnprotDeauthenticateMessage()); break;
- case UnprotDisassociateMessage::kCommand:
- message.reset(new UnprotDisassociateMessage()); break;
-
- default:
- message.reset(new UnknownMessage(gnlh->cmd)); break;
- }
+ } 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 (!message->InitFromNlmsg(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;
}
@@ -1034,6 +1111,10 @@
return message.release();
}
+//
+// Data Collector
+//
+
Nl80211MessageDataCollector *
Nl80211MessageDataCollector::GetInstance() {
return g_datacollector.Pointer();