blob: 21d794a8626dbc60e7cb66ce6222d31a6bb08b24 [file] [log] [blame]
Wade Guthrie0d438532012-05-18 14:18:50 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/config80211.h"
6
7#include <ctype.h>
Wade Guthrie0d438532012-05-18 14:18:50 -07008#include <netlink/msg.h>
9
10#include <map>
11#include <sstream>
12#include <string>
13
14#include <base/memory/weak_ptr.h>
Wade Guthried6153612012-08-23 11:36:14 -070015#include <base/stl_util.h>
Wade Guthrie0d438532012-05-18 14:18:50 -070016
17#include "shill/io_handler.h"
18#include "shill/logging.h"
repo syncdc085c82012-12-28 08:54:41 -080019#include "shill/nl80211_message.h"
Wade Guthrie0d438532012-05-18 14:18:50 -070020#include "shill/nl80211_socket.h"
21#include "shill/scope_logger.h"
Wade Guthrie0d438532012-05-18 14:18:50 -070022
23using base::Bind;
24using base::LazyInstance;
Wade Guthrieb1ec8602012-10-18 17:26:14 -070025using std::list;
Wade Guthrie0d438532012-05-18 14:18:50 -070026using std::string;
27
28namespace shill {
29
30namespace {
31LazyInstance<Config80211> g_config80211 = LAZY_INSTANCE_INITIALIZER;
Wade Guthrie0d438532012-05-18 14:18:50 -070032} // namespace
33
Wade Guthried6153612012-08-23 11:36:14 -070034Config80211::EventTypeStrings *Config80211::event_types_ = NULL;
Wade Guthrie0d438532012-05-18 14:18:50 -070035
36Config80211::Config80211()
Wade Guthried6153612012-08-23 11:36:14 -070037 : wifi_state_(kWifiDown),
38 dispatcher_(NULL),
Wade Guthrie0d438532012-05-18 14:18:50 -070039 weak_ptr_factory_(this),
40 dispatcher_callback_(Bind(&Config80211::HandleIncomingEvents,
41 weak_ptr_factory_.GetWeakPtr())),
42 sock_(NULL) {
43}
44
45Config80211::~Config80211() {
46 // Since Config80211 is a singleton, it should be safe to delete a static
47 // member.
48 delete event_types_;
49 event_types_ = NULL;
50}
51
52Config80211 *Config80211::GetInstance() {
53 return g_config80211.Pointer();
54}
55
Wade Guthried6153612012-08-23 11:36:14 -070056void Config80211::Reset() {
57 wifi_state_ = kWifiDown;
58 subscribed_events_.clear();
Wade Guthrieb1ec8602012-10-18 17:26:14 -070059 ClearBroadcastCallbacks();
Wade Guthried6153612012-08-23 11:36:14 -070060}
61
Wade Guthrie0d438532012-05-18 14:18:50 -070062bool Config80211::Init(EventDispatcher *dispatcher) {
63 if (!sock_) {
64 sock_ = new Nl80211Socket;
65 if (!sock_) {
66 LOG(ERROR) << "No memory";
67 return false;
68 }
69
70 if (!sock_->Init()) {
71 return false;
72 }
Wade Guthrie5d53d492012-11-07 09:53:31 -080073
74 // Install the global NetLink Callback.
75 sock_->SetNetlinkCallback(OnRawNlMessageReceived,
76 static_cast<void *>(this));
77
78 // Don't make libnl do the sequence checking (because it'll not like
79 // the broadcast messages for which we'll eventually ask). We'll do all the
80 // sequence checking we need.
81 sock_->DisableSequenceChecking();
Wade Guthrie0d438532012-05-18 14:18:50 -070082 }
83
84 if (!event_types_) {
Wade Guthried6153612012-08-23 11:36:14 -070085 event_types_ = new EventTypeStrings;
Wade Guthrie0d438532012-05-18 14:18:50 -070086 (*event_types_)[Config80211::kEventTypeConfig] = "config";
87 (*event_types_)[Config80211::kEventTypeScan] = "scan";
88 (*event_types_)[Config80211::kEventTypeRegulatory] = "regulatory";
89 (*event_types_)[Config80211::kEventTypeMlme] = "mlme";
90 }
91
92 // Install ourselves in the shill mainloop so we receive messages on the
93 // nl80211 socket.
94 dispatcher_ = dispatcher;
95 if (dispatcher_) {
96 dispatcher_handler_.reset(
97 dispatcher_->CreateReadyHandler(GetFd(),
98 IOHandler::kModeInput,
99 dispatcher_callback_));
100 }
101 return true;
102}
103
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700104bool Config80211::AddBroadcastCallback(const Callback &callback) {
105 list<Callback>::iterator i;
106 if (FindBroadcastCallback(callback)) {
107 LOG(WARNING) << "Trying to re-add a callback";
108 return false; // Should only be one copy in the list.
109 }
110 if (callback.is_null()) {
111 LOG(WARNING) << "Trying to add a NULL callback";
112 return false;
113 }
114 // And add the callback to the list.
115 SLOG(WiFi, 3) << "Config80211::" << __func__ << " - adding callback";
116 broadcast_callbacks_.push_back(callback);
117 return true;
118}
119
120bool Config80211::RemoveBroadcastCallback(const Callback &callback) {
121 list<Callback>::iterator i;
122 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
123 if ((*i).Equals(callback)) {
124 broadcast_callbacks_.erase(i);
125 // Should only be one copy in the list so we don't have to continue
126 // looking for another one.
127 return true;
128 }
129 }
130 LOG(WARNING) << "Callback not found.";
131 return false;
132}
133
134bool Config80211::FindBroadcastCallback(const Callback &callback) const {
135 list<Callback>::const_iterator i;
136 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
137 if ((*i).Equals(callback)) {
138 return true;
139 }
140 }
141 return false;
142}
143
144void Config80211::ClearBroadcastCallbacks() {
145 broadcast_callbacks_.clear();
146}
147
repo syncdc085c82012-12-28 08:54:41 -0800148bool Config80211::SendMessage(Nl80211Message *message,
Wade Guthrie5d53d492012-11-07 09:53:31 -0800149 const Callback &callback) {
150 if (!message) {
151 LOG(ERROR) << "Message is NULL.";
152 return false;
153 }
repo syncdc085c82012-12-28 08:54:41 -0800154 uint32 sequence_number = message->sequence_number();
Christopher Wiley393b93f2012-11-08 17:30:58 -0800155 if (!sequence_number) {
repo syncdc085c82012-12-28 08:54:41 -0800156 sequence_number = sock_->GetSequenceNumber();
157 message->set_sequence_number(sequence_number);
158 }
159 if (!sock_->SendMessage(message)) {
Christopher Wiley393b93f2012-11-08 17:30:58 -0800160 LOG(ERROR) << "Failed to send nl80211 message.";
Wade Guthrie5d53d492012-11-07 09:53:31 -0800161 return false;
162 }
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700163 if (callback.is_null()) {
Christopher Wiley393b93f2012-11-08 17:30:58 -0800164 LOG(INFO) << "Callback for message was null.";
165 return true;
166 }
167 if (ContainsKey(message_callbacks_, sequence_number)) {
168 LOG(ERROR) << "Sent message, but already had a callback for this message?";
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700169 return false;
170 }
Christopher Wiley393b93f2012-11-08 17:30:58 -0800171 message_callbacks_[sequence_number] = callback;
172 LOG(INFO) << "Sent nl80211 message with sequence number: " << sequence_number;
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700173 return true;
174}
175
repo syncdc085c82012-12-28 08:54:41 -0800176bool Config80211::RemoveMessageCallback(const Nl80211Message &message) {
Christopher Wiley393b93f2012-11-08 17:30:58 -0800177 if (!ContainsKey(message_callbacks_, message.sequence_number())) {
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700178 return false;
179 }
Christopher Wiley393b93f2012-11-08 17:30:58 -0800180 message_callbacks_.erase(message.sequence_number());
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700181 return true;
182}
183
Wade Guthrie0d438532012-05-18 14:18:50 -0700184// static
185bool Config80211::GetEventTypeString(EventType type, string *value) {
186 if (!value) {
187 LOG(ERROR) << "NULL |value|";
188 return false;
189 }
190 if (!event_types_) {
191 LOG(ERROR) << "NULL |event_types_|";
192 return false;
193 }
194
Wade Guthried6153612012-08-23 11:36:14 -0700195 EventTypeStrings::iterator match = (*event_types_).find(type);
Wade Guthrie0d438532012-05-18 14:18:50 -0700196 if (match == (*event_types_).end()) {
197 LOG(ERROR) << "Event type " << type << " not found";
198 return false;
199 }
200 *value = match->second;
201 return true;
202}
203
Wade Guthried6153612012-08-23 11:36:14 -0700204void Config80211::SetWifiState(WifiState new_state) {
205 if (wifi_state_ == new_state) {
206 return;
207 }
208
Wade Guthrie5d53d492012-11-07 09:53:31 -0800209 if (!sock_) {
210 LOG(ERROR) << "Config80211::Init needs to be called before this";
211 return;
212 }
213
Wade Guthried6153612012-08-23 11:36:14 -0700214 // If we're newly-up, subscribe to all the event types that have been
215 // requested.
216 if (new_state == kWifiUp) {
Wade Guthrie5d53d492012-11-07 09:53:31 -0800217 // Install the global NetLink Callback.
218 sock_->SetNetlinkCallback(OnRawNlMessageReceived,
219 static_cast<void *>(this));
Wade Guthried6153612012-08-23 11:36:14 -0700220 SubscribedEvents::const_iterator i;
221 for (i = subscribed_events_.begin(); i != subscribed_events_.end(); ++i) {
Wade Guthried6153612012-08-23 11:36:14 -0700222 ActuallySubscribeToEvents(*i);
223 }
224 }
225 wifi_state_ = new_state;
226}
227
Wade Guthrie0d438532012-05-18 14:18:50 -0700228bool Config80211::SubscribeToEvents(EventType type) {
Wade Guthried6153612012-08-23 11:36:14 -0700229 bool it_worked = true;
230 if (!ContainsKey(subscribed_events_, type)) {
231 if (wifi_state_ == kWifiUp) {
232 it_worked = ActuallySubscribeToEvents(type);
233 }
234 // |subscribed_events_| is a list of events to which we want to subscribe
235 // when wifi comes up (including when it comes _back_ up after it goes
236 // down sometime in the future).
237 subscribed_events_.insert(type);
238 }
239 return it_worked;
240}
241
242bool Config80211::ActuallySubscribeToEvents(EventType type) {
Wade Guthrie0d438532012-05-18 14:18:50 -0700243 string group_name;
244
245 if (!GetEventTypeString(type, &group_name)) {
246 return false;
247 }
248 if (!sock_->AddGroupMembership(group_name)) {
249 return false;
250 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700251 return true;
252}
253
254void Config80211::HandleIncomingEvents(int unused_fd) {
255 sock_->GetMessages();
256}
257
258// NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
259// (but one consistent with the nl_socket_modify_cb call to which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700260// |OnRawNlMessageReceived| is a parameter). |raw_message| is actually a
Wade Guthrie0d438532012-05-18 14:18:50 -0700261// "struct sk_buff" but that data type is only visible in the kernel. We'll
262// scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700263// extracts an nlmsghdr pointer from |raw_message|. We'll, then, pass this to
264// a separate method, |OnNlMessageReceived|, to make testing easier.
Wade Guthrie0d438532012-05-18 14:18:50 -0700265
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700266// static
267int Config80211::OnRawNlMessageReceived(struct nl_msg *raw_message,
268 void *void_config80211) {
269 if (!void_config80211) {
270 LOG(WARNING) << "NULL config80211 parameter";
271 return NL_SKIP; // Skip current message, continue parsing buffer.
272 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700273
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700274 Config80211 *config80211 = static_cast<Config80211 *>(void_config80211);
275 SLOG(WiFi, 3) << " " << __func__ << " calling OnNlMessageReceived";
276 return config80211->OnNlMessageReceived(nlmsg_hdr(raw_message));
277}
Wade Guthrie0d438532012-05-18 14:18:50 -0700278
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700279int Config80211::OnNlMessageReceived(nlmsghdr *msg) {
Christopher Wiley393b93f2012-11-08 17:30:58 -0800280 if (!msg) {
281 LOG(ERROR) << __func__ << "() called with null header.";
282 return NL_SKIP;
283 }
284 const uint32 sequence_number = msg->nlmsg_seq;
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700285 SLOG(WiFi, 3) << "\t Entering " << __func__
Christopher Wiley393b93f2012-11-08 17:30:58 -0800286 << "( msg:" << sequence_number << ")";
repo syncdc085c82012-12-28 08:54:41 -0800287 scoped_ptr<Nl80211Message> message(Nl80211MessageFactory::CreateMessage(msg));
Wade Guthrie0d438532012-05-18 14:18:50 -0700288 if (message == NULL) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700289 SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
Christopher Wiley393b93f2012-11-08 17:30:58 -0800290 return NL_SKIP; // Skip current message, continue parsing buffer.
291 }
292 // Call (then erase) any message-specific callback.
293 if (ContainsKey(message_callbacks_, sequence_number)) {
294 SLOG(WiFi, 3) << "found message-specific callback";
295 if (message_callbacks_[sequence_number].is_null()) {
296 LOG(ERROR) << "Callback exists but is NULL for ID " << sequence_number;
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700297 } else {
Christopher Wiley393b93f2012-11-08 17:30:58 -0800298 message_callbacks_[sequence_number].Run(*message);
299 }
300 message_callbacks_.erase(sequence_number);
301 } else {
302 list<Callback>::iterator i = broadcast_callbacks_.begin();
303 while (i != broadcast_callbacks_.end()) {
304 SLOG(WiFi, 3) << "found a broadcast callback";
305 if (i->is_null()) {
306 i = broadcast_callbacks_.erase(i);
307 } else {
308 SLOG(WiFi, 3) << " " << __func__ << " - calling callback";
309 i->Run(*message);
310 ++i;
Wade Guthrie0d438532012-05-18 14:18:50 -0700311 }
312 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700313 }
314
Wade Guthrie0d438532012-05-18 14:18:50 -0700315 return NL_SKIP; // Skip current message, continue parsing buffer.
316}
317
Wade Guthrie0d438532012-05-18 14:18:50 -0700318} // namespace shill.