shill: Reading from the raw socket rather than through libnl.
This is one step closer to removing the libnl socket, altogether.
Removed libnl callbacks since they aren't used, anymore.
BUG=chromium-os:36099
TEST=unittests and manual tests (Added test code to send messages, saw
the response in logs. Saw broadcast messages, beacon hints and scan
results, as well.).
Change-Id: Iaad5dee3b59f77bd2358bef17d3885fed7f72171
Reviewed-on: https://gerrit.chromium.org/gerrit/40390
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/config80211.cc b/config80211.cc
index 21d794a..7bcaa4a 100644
--- a/config80211.cc
+++ b/config80211.cc
@@ -14,6 +14,7 @@
#include <base/memory/weak_ptr.h>
#include <base/stl_util.h>
+#include "shill/error.h"
#include "shill/io_handler.h"
#include "shill/logging.h"
#include "shill/nl80211_message.h"
@@ -37,8 +38,8 @@
: wifi_state_(kWifiDown),
dispatcher_(NULL),
weak_ptr_factory_(this),
- dispatcher_callback_(Bind(&Config80211::HandleIncomingEvents,
- weak_ptr_factory_.GetWeakPtr())),
+ dispatcher_callback_(Bind(&Config80211::OnRawNlMessageReceived,
+ weak_ptr_factory_.GetWeakPtr())),
sock_(NULL) {
}
@@ -70,11 +71,6 @@
if (!sock_->Init()) {
return false;
}
-
- // Install the global NetLink Callback.
- sock_->SetNetlinkCallback(OnRawNlMessageReceived,
- static_cast<void *>(this));
-
// Don't make libnl do the sequence checking (because it'll not like
// the broadcast messages for which we'll eventually ask). We'll do all the
// sequence checking we need.
@@ -89,15 +85,7 @@
(*event_types_)[Config80211::kEventTypeMlme] = "mlme";
}
- // Install ourselves in the shill mainloop so we receive messages on the
- // nl80211 socket.
dispatcher_ = dispatcher;
- if (dispatcher_) {
- dispatcher_handler_.reset(
- dispatcher_->CreateReadyHandler(GetFd(),
- IOHandler::kModeInput,
- dispatcher_callback_));
- }
return true;
}
@@ -211,12 +199,18 @@
return;
}
- // If we're newly-up, subscribe to all the event types that have been
- // requested.
if (new_state == kWifiUp) {
- // Install the global NetLink Callback.
- sock_->SetNetlinkCallback(OnRawNlMessageReceived,
- static_cast<void *>(this));
+ // Install ourselves in the shill mainloop so we receive messages on the
+ // nl80211 socket.
+ if (dispatcher_) {
+ dispatcher_handler_.reset(dispatcher_->CreateInputHandler(
+ GetFd(),
+ dispatcher_callback_,
+ Bind(&Config80211::OnReadError, weak_ptr_factory_.GetWeakPtr())));
+ }
+
+ // If we're newly-up, subscribe to all the event types that have been
+ // requested.
SubscribedEvents::const_iterator i;
for (i = subscribed_events_.begin(); i != subscribed_events_.end(); ++i) {
ActuallySubscribeToEvents(*i);
@@ -251,45 +245,48 @@
return true;
}
-void Config80211::HandleIncomingEvents(int unused_fd) {
- sock_->GetMessages();
-}
-
-// NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
-// (but one consistent with the nl_socket_modify_cb call to which
-// |OnRawNlMessageReceived| is a parameter). |raw_message| is actually a
-// "struct sk_buff" but that data type is only visible in the kernel. We'll
-// scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
-// extracts an nlmsghdr pointer from |raw_message|. We'll, then, pass this to
-// a separate method, |OnNlMessageReceived|, to make testing easier.
-
-// static
-int Config80211::OnRawNlMessageReceived(struct nl_msg *raw_message,
- void *void_config80211) {
- if (!void_config80211) {
- LOG(WARNING) << "NULL config80211 parameter";
- return NL_SKIP; // Skip current message, continue parsing buffer.
+void Config80211::OnRawNlMessageReceived(InputData *data) {
+ if (!data) {
+ LOG(ERROR) << __func__ << "() called with null header.";
+ return;
}
-
- Config80211 *config80211 = static_cast<Config80211 *>(void_config80211);
- SLOG(WiFi, 3) << " " << __func__ << " calling OnNlMessageReceived";
- return config80211->OnNlMessageReceived(nlmsg_hdr(raw_message));
+ unsigned char *buf = data->buf;
+ unsigned char *end = buf + data->len;
+ while (buf < end) {
+ nlmsghdr *msg = reinterpret_cast<nlmsghdr *>(buf);
+ // Discard the message if there're not enough bytes to a) tell the code how
+ // much space is in the message (i.e., to access nlmsg_len) or b) to hold
+ // the entire message. The odd calculation is there to keep the code from
+ // potentially calculating an illegal address (causes a segfault on some
+ // architectures).
+ size_t bytes_left = end - buf;
+ if (((bytes_left < (offsetof(nlmsghdr, nlmsg_len) +
+ sizeof(nlmsghdr::nlmsg_len))) ||
+ (bytes_left < msg->nlmsg_len))) {
+ LOG(ERROR) << "Discarding incomplete message.";
+ return;
+ }
+ OnNlMessageReceived(msg);
+ buf += msg->nlmsg_len;
+ }
}
-int Config80211::OnNlMessageReceived(nlmsghdr *msg) {
+void Config80211::OnNlMessageReceived(nlmsghdr *msg) {
if (!msg) {
LOG(ERROR) << __func__ << "() called with null header.";
- return NL_SKIP;
+ return;
}
const uint32 sequence_number = msg->nlmsg_seq;
SLOG(WiFi, 3) << "\t Entering " << __func__
- << "( msg:" << sequence_number << ")";
+ << " (msg:" << sequence_number << ")";
scoped_ptr<Nl80211Message> message(Nl80211MessageFactory::CreateMessage(msg));
if (message == NULL) {
SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
- return NL_SKIP; // Skip current message, continue parsing buffer.
+ return; // Skip current message, continue parsing buffer.
}
// Call (then erase) any message-specific callback.
+ // TODO(wdg): Support multi-part messages; don't delete callback until last
+ // part appears.
if (ContainsKey(message_callbacks_, sequence_number)) {
SLOG(WiFi, 3) << "found message-specific callback";
if (message_callbacks_[sequence_number].is_null()) {
@@ -299,20 +296,22 @@
}
message_callbacks_.erase(sequence_number);
} else {
- list<Callback>::iterator i = broadcast_callbacks_.begin();
+ list<Callback>::const_iterator i = broadcast_callbacks_.begin();
while (i != broadcast_callbacks_.end()) {
- SLOG(WiFi, 3) << "found a broadcast callback";
- if (i->is_null()) {
- i = broadcast_callbacks_.erase(i);
- } else {
- SLOG(WiFi, 3) << " " << __func__ << " - calling callback";
- i->Run(*message);
- ++i;
- }
+ SLOG(WiFi, 3) << " " << __func__ << " - calling callback";
+ i->Run(*message);
+ ++i;
}
}
-
- return NL_SKIP; // Skip current message, continue parsing buffer.
}
+void Config80211::OnReadError(const Error &error) {
+ // TODO(wdg): When config80211 is used for scan, et al., this should either be
+ // LOG(FATAL) or the code should properly deal with errors, e.g., dropped
+ // messages due to the socket buffer being full.
+ LOG(ERROR) << "Config80211's netlink Socket read returns error: "
+ << error.message();
+}
+
+
} // namespace shill.