1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "apmanager/hostapd_monitor.h"
18 
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/un.h>
22 
23 #include <base/bind.h>
24 #include <base/logging.h>
25 #include <base/strings/stringprintf.h>
26 #include <shill/net/io_handler_factory_container.h>
27 #include <shill/net/sockets.h>
28 
29 using base::Bind;
30 using base::Unretained;
31 using shill::IOHandlerFactoryContainer;
32 using std::string;
33 
34 namespace apmanager {
35 
36 // static.
37 #if !defined(__ANDROID__)
38 const char HostapdMonitor::kLocalPathFormat[] =
39     "/var/run/apmanager/hostapd/hostapd_ctrl_%s";
40 #else
41 const char HostapdMonitor::kLocalPathFormat[] =
42     "/data/misc/apmanager/hostapd/hostapd_ctrl_%s";
43 #endif  // __ANDROID__
44 
45 const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH";
46 const char HostapdMonitor::kHostapdRespOk[] = "OK\n";
47 const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED";
48 const char HostapdMonitor::kHostapdEventStationDisconnected[] =
49     "AP-STA-DISCONNECTED";
50 const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500;
51 const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5;
52 const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000;
53 const int HostapdMonitor::kInvalidSocket = -1;
54 
HostapdMonitor(const EventCallback & callback,const string & control_interface_path,const string & network_interface_name)55 HostapdMonitor::HostapdMonitor(const EventCallback& callback,
56                                const string& control_interface_path,
57                                const string& network_interface_name)
58     : sockets_(new shill::Sockets()),
59       event_callback_(callback),
60       dest_path_(base::StringPrintf("%s/%s",
61                                     control_interface_path.c_str(),
62                                     network_interface_name.c_str())),
63       local_path_(base::StringPrintf(kLocalPathFormat,
64                                      network_interface_name.c_str())),
65       hostapd_socket_(kInvalidSocket),
66       io_handler_factory_(
67           IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
68       event_dispatcher_(EventDispatcher::GetInstance()),
69       weak_ptr_factory_(this),
70       started_(false) {}
71 
~HostapdMonitor()72 HostapdMonitor::~HostapdMonitor() {
73   if (hostapd_socket_ != kInvalidSocket) {
74     unlink(local_path_.c_str());
75     sockets_->Close(hostapd_socket_);
76   }
77 }
78 
Start()79 void HostapdMonitor::Start() {
80   if (started_) {
81     LOG(ERROR) << "HostapdMonitor already started";
82     return;
83   }
84 
85   hostapd_ctrl_iface_check_count_ = 0;
86   // Start off by checking the control interface file for the hostapd process.
87   event_dispatcher_->PostTask(
88       Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
89            weak_ptr_factory_.GetWeakPtr()));
90   started_ = true;
91 }
92 
HostapdCtrlIfaceCheckTask()93 void HostapdMonitor::HostapdCtrlIfaceCheckTask() {
94   struct stat buf;
95   if (stat(dest_path_.c_str(), &buf) != 0) {
96     if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) {
97       // This indicates the hostapd failed to start. Invoke callback indicating
98       // hostapd start failed.
99       LOG(ERROR) << "Timeout waiting for hostapd control interface";
100       event_callback_.Run(kHostapdFailed, "");
101     } else {
102       hostapd_ctrl_iface_check_count_++;
103       event_dispatcher_->PostDelayedTask(
104           base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
105                      weak_ptr_factory_.GetWeakPtr()),
106           kHostapdCtrlIfaceCheckIntervalMs);
107     }
108     return;
109   }
110 
111   // Control interface is up, meaning hostapd started successfully.
112   event_callback_.Run(kHostapdStarted, "");
113 
114   // Attach to the control interface to receive unsolicited event notifications.
115   AttachToHostapd();
116 }
117 
AttachToHostapd()118 void HostapdMonitor::AttachToHostapd() {
119   if (hostapd_socket_ != kInvalidSocket) {
120     LOG(ERROR) << "Socket already initialized";
121     return;
122   }
123 
124   // Setup socket address for local file and remote file.
125   struct sockaddr_un local;
126   local.sun_family = AF_UNIX;
127   snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str());
128   struct sockaddr_un dest;
129   dest.sun_family = AF_UNIX;
130   snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str());
131 
132   // Setup socket for interprocess communication.
133   hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0);
134   if (hostapd_socket_ < 0) {
135     LOG(ERROR) << "Failed to open hostapd socket";
136     return;
137   }
138   if (sockets_->Bind(hostapd_socket_,
139                      reinterpret_cast<struct sockaddr*>(&local),
140                      sizeof(local)) < 0) {
141     PLOG(ERROR) << "Failed to bind to local socket";
142     return;
143   }
144   if (sockets_->Connect(hostapd_socket_,
145                         reinterpret_cast<struct sockaddr*>(&dest),
146                         sizeof(dest)) < 0) {
147     PLOG(ERROR) << "Failed to connect";
148     return;
149   }
150 
151   // Setup IO Input handler.
152   hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
153       hostapd_socket_,
154       Bind(&HostapdMonitor::ParseMessage, Unretained(this)),
155       Bind(&HostapdMonitor::OnReadError, Unretained(this))));
156 
157   if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) {
158     LOG(ERROR) << "Failed to attach to hostapd";
159     return;
160   }
161 
162   // Start a timer for ATTACH response.
163   attach_timeout_callback_.Reset(
164       Bind(&HostapdMonitor::AttachTimeoutHandler,
165            weak_ptr_factory_.GetWeakPtr()));
166   event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(),
167                                      kHostapdAttachTimeoutMs);
168   return;
169 }
170 
AttachTimeoutHandler()171 void HostapdMonitor::AttachTimeoutHandler() {
172   LOG(ERROR) << "Timeout waiting for attach response";
173 }
174 
175 // Method for sending message to hostapd control interface.
SendMessage(const char * message,size_t length)176 bool HostapdMonitor::SendMessage(const char* message, size_t length) {
177   if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) {
178     PLOG(ERROR) << "Send to hostapd failed";
179     return false;
180   }
181 
182   return true;
183 }
184 
ParseMessage(shill::InputData * data)185 void HostapdMonitor::ParseMessage(shill::InputData* data) {
186   string str(reinterpret_cast<const char*>(data->buf), data->len);
187   // "OK" response for the "ATTACH" command.
188   if (str == kHostapdRespOk) {
189     attach_timeout_callback_.Cancel();
190     return;
191   }
192 
193   // Event messages are in format of <[Level]>[Event] [Detail message].
194   // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55
195   // Refer to wpa_ctrl.h for complete list of possible events.
196   if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) {
197     // Remove the log level.
198     string msg = str.substr(3);
199     string event;
200     string data;
201     size_t pos = msg.find_first_of(' ', 0);
202     if (pos == string::npos) {
203       event = msg;
204     } else {
205       event = msg.substr(0, pos);
206       data = msg.substr(pos + 1);
207     }
208 
209     Event event_code;
210     if (event == kHostapdEventStationConnected) {
211       event_code = kStationConnected;
212     } else if (event == kHostapdEventStationDisconnected) {
213       event_code = kStationDisconnected;
214     } else {
215       LOG(INFO) << "Received unknown event: " << event;
216       return;
217     }
218     event_callback_.Run(event_code, data);
219     return;
220   }
221 
222   LOG(INFO) << "Received unknown message: " << str;
223 }
224 
OnReadError(const string & error_msg)225 void HostapdMonitor::OnReadError(const string& error_msg) {
226   LOG(FATAL) << "Hostapd Socket read returns error: "
227              << error_msg;
228 }
229 
230 }  // namespace apmanager
231