blob: 14217fb3d293046be524abd5a547942f8a8a1bb6 [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"
Wade Guthrie5d53d492012-11-07 09:53:31 -080018#include "shill/kernel_bound_nlmessage.h"
Wade Guthrie0d438532012-05-18 14:18:50 -070019#include "shill/logging.h"
20#include "shill/nl80211_socket.h"
21#include "shill/scope_logger.h"
22#include "shill/user_bound_nlmessage.h"
23
24using base::Bind;
25using base::LazyInstance;
Wade Guthrieb1ec8602012-10-18 17:26:14 -070026using std::list;
Wade Guthrie0d438532012-05-18 14:18:50 -070027using std::string;
28
29namespace shill {
30
31namespace {
32LazyInstance<Config80211> g_config80211 = LAZY_INSTANCE_INITIALIZER;
Wade Guthrie0d438532012-05-18 14:18:50 -070033} // namespace
34
Wade Guthried6153612012-08-23 11:36:14 -070035Config80211::EventTypeStrings *Config80211::event_types_ = NULL;
Wade Guthrie0d438532012-05-18 14:18:50 -070036
37Config80211::Config80211()
Wade Guthried6153612012-08-23 11:36:14 -070038 : wifi_state_(kWifiDown),
39 dispatcher_(NULL),
Wade Guthrie0d438532012-05-18 14:18:50 -070040 weak_ptr_factory_(this),
41 dispatcher_callback_(Bind(&Config80211::HandleIncomingEvents,
42 weak_ptr_factory_.GetWeakPtr())),
43 sock_(NULL) {
44}
45
46Config80211::~Config80211() {
47 // Since Config80211 is a singleton, it should be safe to delete a static
48 // member.
49 delete event_types_;
50 event_types_ = NULL;
51}
52
53Config80211 *Config80211::GetInstance() {
54 return g_config80211.Pointer();
55}
56
Wade Guthried6153612012-08-23 11:36:14 -070057void Config80211::Reset() {
58 wifi_state_ = kWifiDown;
59 subscribed_events_.clear();
Wade Guthrieb1ec8602012-10-18 17:26:14 -070060 ClearBroadcastCallbacks();
Wade Guthried6153612012-08-23 11:36:14 -070061}
62
Wade Guthrie0d438532012-05-18 14:18:50 -070063bool Config80211::Init(EventDispatcher *dispatcher) {
64 if (!sock_) {
65 sock_ = new Nl80211Socket;
66 if (!sock_) {
67 LOG(ERROR) << "No memory";
68 return false;
69 }
70
71 if (!sock_->Init()) {
72 return false;
73 }
Wade Guthrie5d53d492012-11-07 09:53:31 -080074
75 // Install the global NetLink Callback.
76 sock_->SetNetlinkCallback(OnRawNlMessageReceived,
77 static_cast<void *>(this));
78
79 // Don't make libnl do the sequence checking (because it'll not like
80 // the broadcast messages for which we'll eventually ask). We'll do all the
81 // sequence checking we need.
82 sock_->DisableSequenceChecking();
Wade Guthrie0d438532012-05-18 14:18:50 -070083 }
84
85 if (!event_types_) {
Wade Guthried6153612012-08-23 11:36:14 -070086 event_types_ = new EventTypeStrings;
Wade Guthrie0d438532012-05-18 14:18:50 -070087 (*event_types_)[Config80211::kEventTypeConfig] = "config";
88 (*event_types_)[Config80211::kEventTypeScan] = "scan";
89 (*event_types_)[Config80211::kEventTypeRegulatory] = "regulatory";
90 (*event_types_)[Config80211::kEventTypeMlme] = "mlme";
91 }
92
93 // Install ourselves in the shill mainloop so we receive messages on the
94 // nl80211 socket.
95 dispatcher_ = dispatcher;
96 if (dispatcher_) {
97 dispatcher_handler_.reset(
98 dispatcher_->CreateReadyHandler(GetFd(),
99 IOHandler::kModeInput,
100 dispatcher_callback_));
101 }
102 return true;
103}
104
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700105bool Config80211::AddBroadcastCallback(const Callback &callback) {
106 list<Callback>::iterator i;
107 if (FindBroadcastCallback(callback)) {
108 LOG(WARNING) << "Trying to re-add a callback";
109 return false; // Should only be one copy in the list.
110 }
111 if (callback.is_null()) {
112 LOG(WARNING) << "Trying to add a NULL callback";
113 return false;
114 }
115 // And add the callback to the list.
116 SLOG(WiFi, 3) << "Config80211::" << __func__ << " - adding callback";
117 broadcast_callbacks_.push_back(callback);
118 return true;
119}
120
121bool Config80211::RemoveBroadcastCallback(const Callback &callback) {
122 list<Callback>::iterator i;
123 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
124 if ((*i).Equals(callback)) {
125 broadcast_callbacks_.erase(i);
126 // Should only be one copy in the list so we don't have to continue
127 // looking for another one.
128 return true;
129 }
130 }
131 LOG(WARNING) << "Callback not found.";
132 return false;
133}
134
135bool Config80211::FindBroadcastCallback(const Callback &callback) const {
136 list<Callback>::const_iterator i;
137 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
138 if ((*i).Equals(callback)) {
139 return true;
140 }
141 }
142 return false;
143}
144
145void Config80211::ClearBroadcastCallbacks() {
146 broadcast_callbacks_.clear();
147}
148
Wade Guthrie5d53d492012-11-07 09:53:31 -0800149bool Config80211::SendMessage(KernelBoundNlMessage *message,
150 const Callback &callback) {
151 if (!message) {
152 LOG(ERROR) << "Message is NULL.";
153 return false;
154 }
155 if (!SetMessageCallback(*message, callback)) {
156 return false;
157 }
158
159 message->Send(sock_);
160 return true;
161}
162
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700163bool Config80211::SetMessageCallback(const KernelBoundNlMessage &message,
164 const Callback &callback) {
165 LOG(INFO) << "Setting callback for message " << message.GetId();
166 uint32_t message_id = message.GetId();
167 if (message_id == 0) {
168 LOG(ERROR) << "Message ID 0 is reserved for broadcast callbacks.";
169 return false;
170 }
171
172 if (ContainsKey(message_callbacks_, message_id)) {
173 LOG(ERROR) << "Already a callback assigned for id " << message_id;
174 return false;
175 }
176
177 if (callback.is_null()) {
178 LOG(ERROR) << "Trying to add a NULL callback for id " << message_id;
179 return false;
180 }
181
182 message_callbacks_[message_id] = callback;
183 return true;
184}
185
186bool Config80211::UnsetMessageCallbackById(uint32_t message_id) {
187 if (!ContainsKey(message_callbacks_, message_id)) {
188 LOG(WARNING) << "No callback assigned for id " << message_id;
189 return false;
190 }
191 message_callbacks_.erase(message_id);
192 return true;
193}
194
Wade Guthrie0d438532012-05-18 14:18:50 -0700195// static
196bool Config80211::GetEventTypeString(EventType type, string *value) {
197 if (!value) {
198 LOG(ERROR) << "NULL |value|";
199 return false;
200 }
201 if (!event_types_) {
202 LOG(ERROR) << "NULL |event_types_|";
203 return false;
204 }
205
Wade Guthried6153612012-08-23 11:36:14 -0700206 EventTypeStrings::iterator match = (*event_types_).find(type);
Wade Guthrie0d438532012-05-18 14:18:50 -0700207 if (match == (*event_types_).end()) {
208 LOG(ERROR) << "Event type " << type << " not found";
209 return false;
210 }
211 *value = match->second;
212 return true;
213}
214
Wade Guthried6153612012-08-23 11:36:14 -0700215void Config80211::SetWifiState(WifiState new_state) {
216 if (wifi_state_ == new_state) {
217 return;
218 }
219
Wade Guthrie5d53d492012-11-07 09:53:31 -0800220 if (!sock_) {
221 LOG(ERROR) << "Config80211::Init needs to be called before this";
222 return;
223 }
224
Wade Guthried6153612012-08-23 11:36:14 -0700225 // If we're newly-up, subscribe to all the event types that have been
226 // requested.
227 if (new_state == kWifiUp) {
Wade Guthrie5d53d492012-11-07 09:53:31 -0800228 // Install the global NetLink Callback.
229 sock_->SetNetlinkCallback(OnRawNlMessageReceived,
230 static_cast<void *>(this));
Wade Guthried6153612012-08-23 11:36:14 -0700231 SubscribedEvents::const_iterator i;
232 for (i = subscribed_events_.begin(); i != subscribed_events_.end(); ++i) {
Wade Guthried6153612012-08-23 11:36:14 -0700233 ActuallySubscribeToEvents(*i);
234 }
235 }
236 wifi_state_ = new_state;
237}
238
Wade Guthrie0d438532012-05-18 14:18:50 -0700239bool Config80211::SubscribeToEvents(EventType type) {
Wade Guthried6153612012-08-23 11:36:14 -0700240 bool it_worked = true;
241 if (!ContainsKey(subscribed_events_, type)) {
242 if (wifi_state_ == kWifiUp) {
243 it_worked = ActuallySubscribeToEvents(type);
244 }
245 // |subscribed_events_| is a list of events to which we want to subscribe
246 // when wifi comes up (including when it comes _back_ up after it goes
247 // down sometime in the future).
248 subscribed_events_.insert(type);
249 }
250 return it_worked;
251}
252
253bool Config80211::ActuallySubscribeToEvents(EventType type) {
Wade Guthrie0d438532012-05-18 14:18:50 -0700254 string group_name;
255
256 if (!GetEventTypeString(type, &group_name)) {
257 return false;
258 }
259 if (!sock_->AddGroupMembership(group_name)) {
260 return false;
261 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700262 return true;
263}
264
265void Config80211::HandleIncomingEvents(int unused_fd) {
266 sock_->GetMessages();
267}
268
269// NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
270// (but one consistent with the nl_socket_modify_cb call to which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700271// |OnRawNlMessageReceived| is a parameter). |raw_message| is actually a
Wade Guthrie0d438532012-05-18 14:18:50 -0700272// "struct sk_buff" but that data type is only visible in the kernel. We'll
273// scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700274// extracts an nlmsghdr pointer from |raw_message|. We'll, then, pass this to
275// a separate method, |OnNlMessageReceived|, to make testing easier.
Wade Guthrie0d438532012-05-18 14:18:50 -0700276
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700277// static
278int Config80211::OnRawNlMessageReceived(struct nl_msg *raw_message,
279 void *void_config80211) {
280 if (!void_config80211) {
281 LOG(WARNING) << "NULL config80211 parameter";
282 return NL_SKIP; // Skip current message, continue parsing buffer.
283 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700284
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700285 Config80211 *config80211 = static_cast<Config80211 *>(void_config80211);
286 SLOG(WiFi, 3) << " " << __func__ << " calling OnNlMessageReceived";
287 return config80211->OnNlMessageReceived(nlmsg_hdr(raw_message));
288}
Wade Guthrie0d438532012-05-18 14:18:50 -0700289
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700290int Config80211::OnNlMessageReceived(nlmsghdr *msg) {
291 SLOG(WiFi, 3) << "\t Entering " << __func__
292 << "( msg:" << msg->nlmsg_seq << ")";
Wade Guthrie0d438532012-05-18 14:18:50 -0700293 scoped_ptr<UserBoundNlMessage> message(
294 UserBoundNlMessageFactory::CreateMessage(msg));
295 if (message == NULL) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700296 SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
Wade Guthrie0d438532012-05-18 14:18:50 -0700297 } else {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700298 SLOG(WiFi, 3) << __func__ << "(msg:" << msg->nlmsg_seq << ")";
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700299 // Call (then erase) any message-specific callback.
300 if (ContainsKey(message_callbacks_, message->GetId())) {
301 SLOG(WiFi, 3) << "found message-specific callback";
302 if (message_callbacks_[message->GetId()].is_null()) {
303 LOG(ERROR) << "Callback exists but is NULL for ID " << message->GetId();
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700304 } else {
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700305 message_callbacks_[message->GetId()].Run(*message);
306 }
307 UnsetMessageCallbackById(message->GetId());
308 } else {
309 list<Callback>::iterator i = broadcast_callbacks_.begin();
310 while (i != broadcast_callbacks_.end()) {
311 SLOG(WiFi, 3) << "found a broadcast callback";
312 if (i->is_null()) {
313 list<Callback>::iterator temp = i;
314 ++temp;
315 broadcast_callbacks_.erase(i);
316 i = temp;
317 } else {
318 SLOG(WiFi, 3) << " " << __func__ << " - calling callback";
319 i->Run(*message);
320 ++i;
321 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700322 }
323 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700324 }
325
Wade Guthrie0d438532012-05-18 14:18:50 -0700326 return NL_SKIP; // Skip current message, continue parsing buffer.
327}
328
Wade Guthrie0d438532012-05-18 14:18:50 -0700329} // namespace shill.