blob: fa476425ecc1c3606e1650199f4c0df817ae9c2b [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"
19#include "shill/nl80211_socket.h"
20#include "shill/scope_logger.h"
21#include "shill/user_bound_nlmessage.h"
22
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 }
73 }
74
75 if (!event_types_) {
Wade Guthried6153612012-08-23 11:36:14 -070076 event_types_ = new EventTypeStrings;
Wade Guthrie0d438532012-05-18 14:18:50 -070077 (*event_types_)[Config80211::kEventTypeConfig] = "config";
78 (*event_types_)[Config80211::kEventTypeScan] = "scan";
79 (*event_types_)[Config80211::kEventTypeRegulatory] = "regulatory";
80 (*event_types_)[Config80211::kEventTypeMlme] = "mlme";
81 }
82
83 // Install ourselves in the shill mainloop so we receive messages on the
84 // nl80211 socket.
85 dispatcher_ = dispatcher;
86 if (dispatcher_) {
87 dispatcher_handler_.reset(
88 dispatcher_->CreateReadyHandler(GetFd(),
89 IOHandler::kModeInput,
90 dispatcher_callback_));
91 }
92 return true;
93}
94
Wade Guthrieb1ec8602012-10-18 17:26:14 -070095bool Config80211::AddBroadcastCallback(const Callback &callback) {
96 list<Callback>::iterator i;
97 if (FindBroadcastCallback(callback)) {
98 LOG(WARNING) << "Trying to re-add a callback";
99 return false; // Should only be one copy in the list.
100 }
101 if (callback.is_null()) {
102 LOG(WARNING) << "Trying to add a NULL callback";
103 return false;
104 }
105 // And add the callback to the list.
106 SLOG(WiFi, 3) << "Config80211::" << __func__ << " - adding callback";
107 broadcast_callbacks_.push_back(callback);
108 return true;
109}
110
111bool Config80211::RemoveBroadcastCallback(const Callback &callback) {
112 list<Callback>::iterator i;
113 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
114 if ((*i).Equals(callback)) {
115 broadcast_callbacks_.erase(i);
116 // Should only be one copy in the list so we don't have to continue
117 // looking for another one.
118 return true;
119 }
120 }
121 LOG(WARNING) << "Callback not found.";
122 return false;
123}
124
125bool Config80211::FindBroadcastCallback(const Callback &callback) const {
126 list<Callback>::const_iterator i;
127 for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
128 if ((*i).Equals(callback)) {
129 return true;
130 }
131 }
132 return false;
133}
134
135void Config80211::ClearBroadcastCallbacks() {
136 broadcast_callbacks_.clear();
137}
138
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700139bool Config80211::SetMessageCallback(const KernelBoundNlMessage &message,
140 const Callback &callback) {
141 LOG(INFO) << "Setting callback for message " << message.GetId();
142 uint32_t message_id = message.GetId();
143 if (message_id == 0) {
144 LOG(ERROR) << "Message ID 0 is reserved for broadcast callbacks.";
145 return false;
146 }
147
148 if (ContainsKey(message_callbacks_, message_id)) {
149 LOG(ERROR) << "Already a callback assigned for id " << message_id;
150 return false;
151 }
152
153 if (callback.is_null()) {
154 LOG(ERROR) << "Trying to add a NULL callback for id " << message_id;
155 return false;
156 }
157
158 message_callbacks_[message_id] = callback;
159 return true;
160}
161
162bool Config80211::UnsetMessageCallbackById(uint32_t message_id) {
163 if (!ContainsKey(message_callbacks_, message_id)) {
164 LOG(WARNING) << "No callback assigned for id " << message_id;
165 return false;
166 }
167 message_callbacks_.erase(message_id);
168 return true;
169}
170
Wade Guthrie0d438532012-05-18 14:18:50 -0700171// static
172bool Config80211::GetEventTypeString(EventType type, string *value) {
173 if (!value) {
174 LOG(ERROR) << "NULL |value|";
175 return false;
176 }
177 if (!event_types_) {
178 LOG(ERROR) << "NULL |event_types_|";
179 return false;
180 }
181
Wade Guthried6153612012-08-23 11:36:14 -0700182 EventTypeStrings::iterator match = (*event_types_).find(type);
Wade Guthrie0d438532012-05-18 14:18:50 -0700183 if (match == (*event_types_).end()) {
184 LOG(ERROR) << "Event type " << type << " not found";
185 return false;
186 }
187 *value = match->second;
188 return true;
189}
190
Wade Guthried6153612012-08-23 11:36:14 -0700191void Config80211::SetWifiState(WifiState new_state) {
192 if (wifi_state_ == new_state) {
193 return;
194 }
195
196 // If we're newly-up, subscribe to all the event types that have been
197 // requested.
198 if (new_state == kWifiUp) {
199 SubscribedEvents::const_iterator i;
200 for (i = subscribed_events_.begin(); i != subscribed_events_.end(); ++i) {
Wade Guthried6153612012-08-23 11:36:14 -0700201 ActuallySubscribeToEvents(*i);
202 }
203 }
204 wifi_state_ = new_state;
205}
206
Wade Guthrie0d438532012-05-18 14:18:50 -0700207bool Config80211::SubscribeToEvents(EventType type) {
Wade Guthried6153612012-08-23 11:36:14 -0700208 bool it_worked = true;
209 if (!ContainsKey(subscribed_events_, type)) {
210 if (wifi_state_ == kWifiUp) {
211 it_worked = ActuallySubscribeToEvents(type);
212 }
213 // |subscribed_events_| is a list of events to which we want to subscribe
214 // when wifi comes up (including when it comes _back_ up after it goes
215 // down sometime in the future).
216 subscribed_events_.insert(type);
217 }
218 return it_worked;
219}
220
221bool Config80211::ActuallySubscribeToEvents(EventType type) {
Wade Guthrie0d438532012-05-18 14:18:50 -0700222 string group_name;
223
224 if (!GetEventTypeString(type, &group_name)) {
225 return false;
226 }
227 if (!sock_->AddGroupMembership(group_name)) {
228 return false;
229 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700230 // No sequence checking for multicast messages.
231 if (!sock_->DisableSequenceChecking()) {
232 return false;
233 }
234
235 // Install the global NetLink Callback for messages along with a parameter.
236 // The Netlink Callback's parameter is passed to 'C' as a 'void *'.
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700237 if (!sock_->SetNetlinkCallback(OnRawNlMessageReceived,
238 static_cast<void *>(this))) {
Wade Guthrie0d438532012-05-18 14:18:50 -0700239 return false;
240 }
241 return true;
242}
243
244void Config80211::HandleIncomingEvents(int unused_fd) {
245 sock_->GetMessages();
246}
247
248// NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
249// (but one consistent with the nl_socket_modify_cb call to which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700250// |OnRawNlMessageReceived| is a parameter). |raw_message| is actually a
Wade Guthrie0d438532012-05-18 14:18:50 -0700251// "struct sk_buff" but that data type is only visible in the kernel. We'll
252// scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700253// extracts an nlmsghdr pointer from |raw_message|. We'll, then, pass this to
254// a separate method, |OnNlMessageReceived|, to make testing easier.
Wade Guthrie0d438532012-05-18 14:18:50 -0700255
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700256// static
257int Config80211::OnRawNlMessageReceived(struct nl_msg *raw_message,
258 void *void_config80211) {
259 if (!void_config80211) {
260 LOG(WARNING) << "NULL config80211 parameter";
261 return NL_SKIP; // Skip current message, continue parsing buffer.
262 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700263
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700264 Config80211 *config80211 = static_cast<Config80211 *>(void_config80211);
265 SLOG(WiFi, 3) << " " << __func__ << " calling OnNlMessageReceived";
266 return config80211->OnNlMessageReceived(nlmsg_hdr(raw_message));
267}
Wade Guthrie0d438532012-05-18 14:18:50 -0700268
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700269int Config80211::OnNlMessageReceived(nlmsghdr *msg) {
270 SLOG(WiFi, 3) << "\t Entering " << __func__
271 << "( msg:" << msg->nlmsg_seq << ")";
Wade Guthrie0d438532012-05-18 14:18:50 -0700272 scoped_ptr<UserBoundNlMessage> message(
273 UserBoundNlMessageFactory::CreateMessage(msg));
274 if (message == NULL) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700275 SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
Wade Guthrie0d438532012-05-18 14:18:50 -0700276 } else {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700277 SLOG(WiFi, 3) << __func__ << "(msg:" << msg->nlmsg_seq << ")";
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700278 // Call (then erase) any message-specific callback.
279 if (ContainsKey(message_callbacks_, message->GetId())) {
280 SLOG(WiFi, 3) << "found message-specific callback";
281 if (message_callbacks_[message->GetId()].is_null()) {
282 LOG(ERROR) << "Callback exists but is NULL for ID " << message->GetId();
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700283 } else {
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700284 message_callbacks_[message->GetId()].Run(*message);
285 }
286 UnsetMessageCallbackById(message->GetId());
287 } else {
288 list<Callback>::iterator i = broadcast_callbacks_.begin();
289 while (i != broadcast_callbacks_.end()) {
290 SLOG(WiFi, 3) << "found a broadcast callback";
291 if (i->is_null()) {
292 list<Callback>::iterator temp = i;
293 ++temp;
294 broadcast_callbacks_.erase(i);
295 i = temp;
296 } else {
297 SLOG(WiFi, 3) << " " << __func__ << " - calling callback";
298 i->Run(*message);
299 ++i;
300 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700301 }
302 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700303 }
304
Wade Guthrie0d438532012-05-18 14:18:50 -0700305 return NL_SKIP; // Skip current message, continue parsing buffer.
306}
307
Wade Guthrie0d438532012-05-18 14:18:50 -0700308} // namespace shill.