blob: 6faf42296747c55dabd495ba101a1820366a333c [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>
8
9#include <netlink/msg.h>
10
11#include <map>
12#include <sstream>
13#include <string>
14
15#include <base/memory/weak_ptr.h>
16#include <base/stringprintf.h>
17
18#include "shill/io_handler.h"
19#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;
26using base::StringAppendF;
27using base::StringPrintf;
28using std::map;
29using std::string;
30
31namespace shill {
32
33namespace {
34LazyInstance<Config80211> g_config80211 = LAZY_INSTANCE_INITIALIZER;
35LazyInstance<Callback80211Object> g_callback80211 = LAZY_INSTANCE_INITIALIZER;
36} // namespace
37
38map<Config80211::EventType, std::string> *Config80211::event_types_ = NULL;
39
40Config80211::Config80211()
41 : dispatcher_(NULL),
42 weak_ptr_factory_(this),
43 dispatcher_callback_(Bind(&Config80211::HandleIncomingEvents,
44 weak_ptr_factory_.GetWeakPtr())),
45 sock_(NULL) {
46}
47
48Config80211::~Config80211() {
49 // Since Config80211 is a singleton, it should be safe to delete a static
50 // member.
51 delete event_types_;
52 event_types_ = NULL;
53}
54
55Config80211 *Config80211::GetInstance() {
56 return g_config80211.Pointer();
57}
58
59bool Config80211::Init(EventDispatcher *dispatcher) {
60 if (!sock_) {
61 sock_ = new Nl80211Socket;
62 if (!sock_) {
63 LOG(ERROR) << "No memory";
64 return false;
65 }
66
67 if (!sock_->Init()) {
68 return false;
69 }
70 }
71
72 if (!event_types_) {
73 event_types_ = new std::map<EventType, std::string>;
74 (*event_types_)[Config80211::kEventTypeConfig] = "config";
75 (*event_types_)[Config80211::kEventTypeScan] = "scan";
76 (*event_types_)[Config80211::kEventTypeRegulatory] = "regulatory";
77 (*event_types_)[Config80211::kEventTypeMlme] = "mlme";
78 }
79
80 // Install ourselves in the shill mainloop so we receive messages on the
81 // nl80211 socket.
82 dispatcher_ = dispatcher;
83 if (dispatcher_) {
84 dispatcher_handler_.reset(
85 dispatcher_->CreateReadyHandler(GetFd(),
86 IOHandler::kModeInput,
87 dispatcher_callback_));
88 }
89 return true;
90}
91
92// static
93bool Config80211::GetEventTypeString(EventType type, string *value) {
94 if (!value) {
95 LOG(ERROR) << "NULL |value|";
96 return false;
97 }
98 if (!event_types_) {
99 LOG(ERROR) << "NULL |event_types_|";
100 return false;
101 }
102
103 map<EventType, string>::iterator match = (*event_types_).find(type);
104 if (match == (*event_types_).end()) {
105 LOG(ERROR) << "Event type " << type << " not found";
106 return false;
107 }
108 *value = match->second;
109 return true;
110}
111
112bool Config80211::SubscribeToEvents(EventType type) {
113 string group_name;
114
115 if (!GetEventTypeString(type, &group_name)) {
116 return false;
117 }
118 if (!sock_->AddGroupMembership(group_name)) {
119 return false;
120 }
121
122 // No sequence checking for multicast messages.
123 if (!sock_->DisableSequenceChecking()) {
124 return false;
125 }
126
127 // Install the global NetLink Callback for messages along with a parameter.
128 // The Netlink Callback's parameter is passed to 'C' as a 'void *'.
129 if (!sock_->SetNetlinkCallback(OnNlMessageReceived,
130 static_cast<void *>(
131 const_cast<Config80211::Callback *>(
132 &default_callback_)))) {
133 return false;
134 }
135 return true;
136}
137
138void Config80211::HandleIncomingEvents(int unused_fd) {
139 sock_->GetMessages();
140}
141
142// NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
143// (but one consistent with the nl_socket_modify_cb call to which
144// |OnNlMessageReceived| is a parameter). |raw_message| is actually a
145// "struct sk_buff" but that data type is only visible in the kernel. We'll
146// scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
147// extracts an nlmsghdr pointer from |raw_message|.
148int Config80211::OnNlMessageReceived(struct nl_msg *raw_message,
149 void *user_callback_object) {
150 SLOG(WiFi, 6) << "\t Entering " << __func__
151 << "( msg:" << raw_message
152 << ", cb:" << user_callback_object << ")";
153
154 string output("@");
155
156 nlmsghdr *msg = nlmsg_hdr(raw_message);
157
158 scoped_ptr<UserBoundNlMessage> message(
159 UserBoundNlMessageFactory::CreateMessage(msg));
160 if (message == NULL) {
161 output.append("unknown event");
162 } else {
163 if (user_callback_object) {
164 Config80211::Callback *bound_object =
165 static_cast<Config80211::Callback *> (user_callback_object);
166 if (!bound_object->is_null()) {
167 bound_object->Run(*message);
168 }
169 }
170 StringAppendF(&output, "%s", message->ToString().c_str());
171 }
172
173 SLOG(WiFi, 5) << output;
174
175 return NL_SKIP; // Skip current message, continue parsing buffer.
176}
177
178// Callback80211Object
179
180Callback80211Object::Callback80211Object() :
181 config80211_(NULL),
182 weak_ptr_factory_(this) {
183}
184
185Callback80211Object::~Callback80211Object() {
186 DeinstallAsCallback();
187}
188
189void Callback80211Object::Config80211MessageCallback(
190 const UserBoundNlMessage &msg) {
191 SLOG(WiFi, 2) << "Received " << msg.GetMessageTypeString()
192 << " (" << + msg.GetMessageType() << ")";
193 scoped_ptr<UserBoundNlMessage::AttributeNameIterator> i;
194
195 for (i.reset(msg.GetAttributeNameIterator()); !i->AtEnd(); i->Advance()) {
196 string value = "<unknown>";
197 msg.GetAttributeString(i->GetName(), &value);
198 SLOG(WiFi, 2) << " Attr:" << msg.StringFromAttributeName(i->GetName())
199 << "=" << value
200 << " Type:" << msg.GetAttributeTypeString(i->GetName());
201 }
202}
203
204bool Callback80211Object::InstallAsCallback() {
205 if (config80211_) {
206 Config80211::Callback callback =
207 Bind(&Callback80211Object::Config80211MessageCallback,
208 weak_ptr_factory_.GetWeakPtr());
209 config80211_->SetDefaultCallback(callback);
210 return true;
211 }
212 return false;
213}
214
215bool Callback80211Object::DeinstallAsCallback() {
216 if (config80211_) {
217 config80211_->UnsetDefaultCallback();
218 return true;
219 }
220 return false;
221}
222
223Callback80211Object *Callback80211Object::GetInstance() {
224 return g_callback80211.Pointer();
225}
226
227} // namespace shill.