device_info.cc revision fb39ba4b2ef92f0c9d19f6910a350cd8a14405a0
1// Copyright 2014 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 "apmanager/device_info.h"
6
7#include <linux/rtnetlink.h>
8
9#include <string>
10
11#include <base/bind.h>
12#include <base/files/file_util.h>
13#include <base/logging.h>
14#include <shill/net/netlink_attribute.h>
15#include <shill/net/netlink_manager.h>
16#include <shill/net/nl80211_message.h>
17#include <shill/net/rtnl_handler.h>
18#include <shill/net/rtnl_listener.h>
19#include <shill/net/rtnl_message.h>
20#include <shill/net/sockets.h>
21
22#include "apmanager/manager.h"
23
24using base::Bind;
25using shill::ByteString;
26using shill::NetlinkManager;
27using shill::NetlinkMessage;
28using shill::Nl80211Message;
29using shill::RTNLHandler;
30using shill::RTNLMessage;
31using shill::RTNLListener;
32using std::map;
33using std::string;
34
35namespace apmanager {
36
37const char DeviceInfo::kDeviceInfoRoot[] = "/sys/class/net";
38const char DeviceInfo::kInterfaceUevent[] = "uevent";
39const char DeviceInfo::kInterfaceUeventWifiSignature[] = "DEVTYPE=wlan\n";
40
41DeviceInfo::DeviceInfo(Manager* manager)
42    : link_callback_(Bind(&DeviceInfo::LinkMsgHandler, Unretained(this))),
43      device_info_root_(kDeviceInfoRoot),
44      manager_(manager),
45      netlink_manager_(NetlinkManager::GetInstance()),
46      rtnl_handler_(RTNLHandler::GetInstance()),
47      sockets_(new shill::Sockets()) {
48}
49
50DeviceInfo::~DeviceInfo() {}
51
52void DeviceInfo::Start() {
53  // Start netlink manager.
54  netlink_manager_->Init();
55  uint16_t nl80211_family_id = netlink_manager_->GetFamily(
56      Nl80211Message::kMessageTypeString,
57      Bind(&Nl80211Message::CreateMessage));
58  if (nl80211_family_id == NetlinkMessage::kIllegalMessageType) {
59    LOG(FATAL) << "Didn't get a legal message type for 'nl80211' messages.";
60  }
61  Nl80211Message::SetMessageType(nl80211_family_id);
62  netlink_manager_->Start();
63
64  // Start enumerating WiFi devices (PHYs).
65  EnumerateDevices();
66
67  // Start RTNL for monitoring network interfaces.
68  rtnl_handler_->Start(sockets_.get());
69  link_listener_.reset(
70      new RTNLListener(RTNLHandler::kRequestLink, link_callback_));
71}
72
73void DeviceInfo::Stop() {
74  link_listener_.reset();
75}
76
77void DeviceInfo::EnumerateDevices() {
78  shill::GetWiphyMessage get_wiphy;
79  get_wiphy.attributes()->SetFlagAttributeValue(NL80211_ATTR_SPLIT_WIPHY_DUMP,
80                                                true);
81  get_wiphy.AddFlag(NLM_F_DUMP);
82  netlink_manager_->SendNl80211Message(
83      &get_wiphy,
84      Bind(&DeviceInfo::OnWiFiPhyInfoReceived, AsWeakPtr()),
85      Bind(&NetlinkManager::OnAckDoNothing),
86      Bind(&NetlinkManager::OnNetlinkMessageError));
87}
88
89void DeviceInfo::OnWiFiPhyInfoReceived(const shill::Nl80211Message& msg) {
90  // Verify NL80211_CMD_NEW_WIPHY.
91  if (msg.command() != shill::NewWiphyMessage::kCommand) {
92    LOG(ERROR) << "Received unexpected command:"
93               << msg.command();
94    return;
95  }
96
97  scoped_refptr<Device> device = new Device();
98  device->ParseWiFiPhyInfo(msg);
99
100  // Register device
101  RegisterDevice(device);
102}
103
104void DeviceInfo::LinkMsgHandler(const RTNLMessage& msg) {
105  DCHECK(msg.type() == RTNLMessage::kTypeLink);
106
107  // Get interface name.
108  if (!msg.HasAttribute(IFLA_IFNAME)) {
109    LOG(ERROR) << "Link event message does not have IFLA_IFNAME!";
110    return;
111  }
112  ByteString b(msg.GetAttribute(IFLA_IFNAME));
113  string iface_name(reinterpret_cast<const char*>(b.GetConstData()));
114
115  int dev_index = msg.interface_index();
116  if (msg.mode() == RTNLMessage::kModeAdd) {
117    AddLinkMsgHandler(iface_name, dev_index);
118  } else if (msg.mode() == RTNLMessage::kModeDelete) {
119    DelLinkMsgHandler(iface_name, dev_index);
120  } else {
121    NOTREACHED();
122  }
123}
124
125void DeviceInfo::AddLinkMsgHandler(const string& iface_name, int iface_index) {
126  // Ignore non-wifi interfaces.
127  if (!IsWifiInterface(iface_name)) {
128    LOG(INFO) << "Ignore link event for non-wifi interface: " << iface_name;
129    return;
130  }
131
132  // Return if interface already existed. Could receive multiple add link event
133  // for a single interface.
134  if (interface_infos_.find(iface_index) != interface_infos_.end()) {
135    LOG(INFO) << "AddLinkMsgHandler: interface " << iface_name
136              << " is already added";
137    return;
138  }
139
140  // Add interface.
141  Device::WiFiInterface wifi_interface;
142  wifi_interface.iface_name = iface_name;
143  wifi_interface.iface_index = iface_index;
144  interface_infos_[iface_index] = wifi_interface;
145
146  // Get interface info.
147  GetWiFiInterfaceInfo(iface_index);
148}
149
150void DeviceInfo::DelLinkMsgHandler(const string& iface_name, int iface_index) {
151  LOG(INFO) << "DelLinkMsgHandler iface_name: " << iface_name
152            << "iface_index: " << iface_index;
153  map<uint32_t, Device::WiFiInterface>::iterator iter =
154      interface_infos_.find(iface_index);
155  if (iter != interface_infos_.end()) {
156    // Deregister interface from the Device.
157    scoped_refptr<Device> device = GetDevice(iter->second.device_name);
158    if (device) {
159      device->DeregisterInterface(iter->second);
160    }
161    interface_infos_.erase(iter);
162  }
163}
164
165bool DeviceInfo::IsWifiInterface(const string& iface_name) {
166  string contents;
167  if (!GetDeviceInfoContents(iface_name, kInterfaceUevent, &contents)) {
168    LOG(INFO) << "Interface " << iface_name << " has no uevent file";
169    return false;
170  }
171
172  if (contents.find(kInterfaceUeventWifiSignature) == string::npos) {
173    LOG(INFO) << "Interface " << iface_name << " is not a WiFi interface";
174    return false;
175  }
176
177  return true;
178}
179
180bool DeviceInfo::GetDeviceInfoContents(const string& iface_name,
181                                       const string& path_name,
182                                       string* contents_out) {
183  return base::ReadFileToString(
184      device_info_root_.Append(iface_name).Append(path_name),
185      contents_out);
186}
187
188void DeviceInfo::GetWiFiInterfaceInfo(int interface_index) {
189  shill::GetInterfaceMessage msg;
190  if (!msg.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
191                                              interface_index)) {
192    LOG(ERROR) << "Unable to set interface index attribute for "
193                  "GetInterface message.  Interface type cannot be "
194                  "determined!";
195    return;
196  }
197
198  netlink_manager_->SendNl80211Message(
199      &msg,
200      Bind(&DeviceInfo::OnWiFiInterfaceInfoReceived, AsWeakPtr()),
201      Bind(&NetlinkManager::OnAckDoNothing),
202      Bind(&NetlinkManager::OnNetlinkMessageError));
203}
204
205void DeviceInfo::OnWiFiInterfaceInfoReceived(const shill::Nl80211Message& msg) {
206  if (msg.command() != NL80211_CMD_NEW_INTERFACE) {
207    LOG(ERROR) << "Message is not a new interface response";
208    return;
209  }
210
211  uint32_t interface_index;
212  if (!msg.const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX,
213                                                    &interface_index)) {
214    LOG(ERROR) << "Message contains no interface index";
215    return;
216  }
217  uint32_t interface_type;
218  if (!msg.const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFTYPE,
219                                                    &interface_type)) {
220    LOG(ERROR) << "Message contains no interface type";
221    return;
222  }
223
224  map<uint32_t, Device::WiFiInterface>::iterator iter =
225      interface_infos_.find(interface_index);
226  if (iter == interface_infos_.end()) {
227    LOG(ERROR) << "Receive WiFi interface info for non-exist interface: "
228               << interface_index;
229    return;
230  }
231  iter->second.iface_type = interface_type;
232
233  // Request PHY info, to know which Device to register this interface to.
234  GetWiFiInterfacePhyInfo(interface_index);
235}
236
237void DeviceInfo::GetWiFiInterfacePhyInfo(uint32_t iface_index) {
238  shill::GetWiphyMessage get_wiphy;
239  get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
240                                               iface_index);
241  netlink_manager_->SendNl80211Message(
242      &get_wiphy,
243      Bind(&DeviceInfo::OnWiFiInterfacePhyInfoReceived,
244           AsWeakPtr(),
245           iface_index),
246      Bind(&NetlinkManager::OnAckDoNothing),
247      Bind(&NetlinkManager::OnNetlinkMessageError));
248}
249
250void DeviceInfo::OnWiFiInterfacePhyInfoReceived(
251    uint32_t iface_index, const shill::Nl80211Message& msg) {
252  // Verify NL80211_CMD_NEW_WIPHY.
253  if (msg.command() != shill::NewWiphyMessage::kCommand) {
254    LOG(ERROR) << "Received unexpected command:"
255               << msg.command();
256    return;
257  }
258
259  map<uint32_t, Device::WiFiInterface>::iterator iter =
260      interface_infos_.find(iface_index);
261  if (iter == interface_infos_.end()) {
262    // Interface is gone by the time we received its PHY info.
263    LOG(ERROR) << "Interface [" << iface_index
264               << "] is deleted when PHY info is received";
265    return;
266  }
267
268  string device_name;
269  if (!msg.const_attributes()->GetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
270                                                       &device_name)) {
271    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_NAME";
272    return;
273  }
274
275  scoped_refptr<Device> device = GetDevice(device_name);
276  if (!device) {
277    LOG(ERROR) << "Trying to register an interface [" << iter->second.iface_name
278               << "] on a PHY [" << device_name << "] that's not enumerated";
279    // This should never happen, the PHY should already be enumerated by
280    // the time WiFi interface is detected, since we don't expected WiFi device
281    // to be inserted/removed during run time.
282    // In the case if we detect an interface before PHY enumeration is
283    // completed, remove the interface info now so it will retry the interface
284    // discovery process (GetWiFiInterfaceInfo and GetWiFiInterfacePhyInfo)
285    // again when the interface is detected next time. And hopefully by then the
286    // PHY enumeration is completed.
287    interface_infos_.erase(iface_index);
288    return;
289  }
290  iter->second.device_name = device_name;
291
292  device->RegisterInterface(iter->second);
293}
294
295void DeviceInfo::RegisterDevice(scoped_refptr<Device> device) {
296  if (!device) {
297    return;
298  }
299  devices_[device->GetDeviceName()] = device;
300  // Register device with manager.
301  manager_->RegisterDevice(device);
302}
303
304scoped_refptr<Device> DeviceInfo::GetDevice(const string& device_name) {
305  map<string, scoped_refptr<Device>>::iterator iter =
306      devices_.find(device_name);
307  if (iter == devices_.end()) {
308    return nullptr;
309  }
310  return iter->second;
311}
312
313}  // namespace apmanager
314