blob: 1e8608cc0a300d76f921598330846120be6b094b [file] [log] [blame]
Paul Stewart0af98bf2011-05-10 17:38:08 -07001// Copyright (c) 2011 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 <time.h>
6
7#include <unistd.h>
8#include <string.h>
9#include <sys/socket.h>
10#include <arpa/inet.h>
11#include <netinet/ether.h>
12#include <net/if.h>
13#include <net/if_arp.h>
14#include <linux/netlink.h>
15#include <linux/rtnetlink.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016#include <fcntl.h>
Paul Stewart0af98bf2011-05-10 17:38:08 -070017#include <string>
18
Chris Masone487b8bf2011-05-13 16:27:57 -070019#include <base/callback_old.h>
Chris Masone0e1d1042011-05-09 18:07:03 -070020#include <base/hash_tables.h>
Chris Masone487b8bf2011-05-13 16:27:57 -070021#include <base/logging.h>
22#include <base/memory/scoped_ptr.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070023#include <base/stringprintf.h>
Paul Stewart0af98bf2011-05-10 17:38:08 -070024
25#include "shill/control_interface.h"
Chris Masone9be4a9d2011-05-16 15:44:09 -070026#include "shill/device.h"
Paul Stewart0af98bf2011-05-10 17:38:08 -070027#include "shill/device_info.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070028#include "shill/ethernet.h"
29#include "shill/manager.h"
Chris Masone487b8bf2011-05-13 16:27:57 -070030#include "shill/service.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070031#include "shill/wifi.h"
Paul Stewart0af98bf2011-05-10 17:38:08 -070032
33using std::string;
34
35namespace shill {
Paul Stewartb50f0b92011-05-16 16:31:42 -070036
37// static
38const char *DeviceInfo::kInterfaceUevent= "/sys/class/net/%s/uevent";
39// static
40const char *DeviceInfo::kInterfaceDriver= "/sys/class/net/%s/device/driver";
41// static
42const char *DeviceInfo::kModemDrivers[] = {
43 "gobi",
44 "QCUSBNet2k",
45 "GobiNet",
46 NULL
47};
48
49
50DeviceInfo::DeviceInfo(ControlInterface *control_interface,
51 EventDispatcher *dispatcher,
52 Manager *manager)
Paul Stewart0af98bf2011-05-10 17:38:08 -070053 : running_(false),
Paul Stewartb50f0b92011-05-16 16:31:42 -070054 control_interface_(control_interface),
Paul Stewart0af98bf2011-05-10 17:38:08 -070055 dispatcher_(dispatcher),
Paul Stewartb50f0b92011-05-16 16:31:42 -070056 manager_(manager),
Chris Masone0e1d1042011-05-09 18:07:03 -070057 rtnl_callback_(NewCallback(this, &DeviceInfo::ParseRTNL)),
Paul Stewart0af98bf2011-05-10 17:38:08 -070058 rtnl_socket_(-1),
59 request_flags_(0),
Paul Stewartb50f0b92011-05-16 16:31:42 -070060 request_sequence_(0) {}
Paul Stewart0af98bf2011-05-10 17:38:08 -070061
62DeviceInfo::~DeviceInfo() {
63 Stop();
64}
65
66void DeviceInfo::Start()
67{
68 struct sockaddr_nl addr;
69
70 if (running_)
71 return;
72
73 rtnl_socket_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
74 if (rtnl_socket_ < 0) {
75 LOG(ERROR) << "Failed to open rtnl socket";
76 return;
77 }
78
79 memset(&addr, 0, sizeof(addr));
80 addr.nl_family = AF_NETLINK;
81 addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
82
Paul Stewartb50f0b92011-05-16 16:31:42 -070083 if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
84 sizeof(addr)) < 0) {
Paul Stewart0af98bf2011-05-10 17:38:08 -070085 close(rtnl_socket_);
86 rtnl_socket_ = -1;
87 LOG(ERROR) << "RTNL socket bind failed";
88 return;
89 }
90
91 rtnl_handler_.reset(dispatcher_->CreateInputHandler(rtnl_socket_,
Chris Masone0e1d1042011-05-09 18:07:03 -070092 rtnl_callback_.get()));
Paul Stewart0af98bf2011-05-10 17:38:08 -070093 running_ = true;
94
95 request_flags_ = REQUEST_LINK | REQUEST_ADDR | REQUEST_ROUTE;
96 NextRequest(request_sequence_);
97
Paul Stewartb50f0b92011-05-16 16:31:42 -070098 VLOG(2) << "DeviceInfo started";
Paul Stewart0af98bf2011-05-10 17:38:08 -070099}
100
101void DeviceInfo::Stop()
102{
103 if (!running_)
104 return;
105
106 rtnl_handler_.reset(NULL);
107 close(rtnl_socket_);
108 running_ = false;
109}
110
111void DeviceInfo::NextRequest(uint32_t seq) {
112 struct rtnl_request {
113 struct nlmsghdr hdr;
114 struct rtgenmsg msg;
115 } req;
116 struct sockaddr_nl addr;
117 int flag = 0;
118
Paul Stewart0af98bf2011-05-10 17:38:08 -0700119 if (seq != request_sequence_)
120 return;
121
122 request_sequence_++;
123 memset(&req, 0, sizeof(req));
124
125 req.hdr.nlmsg_len = sizeof(req);
126 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
127 req.hdr.nlmsg_pid = 0;
128 req.hdr.nlmsg_seq = request_sequence_;
129 req.msg.rtgen_family = AF_INET;
130
131 if ((request_flags_ & REQUEST_LINK) != 0) {
132 req.hdr.nlmsg_type = RTM_GETLINK;
133 flag = REQUEST_LINK;
134 } else if ((request_flags_ & REQUEST_ADDR) != 0) {
135 req.hdr.nlmsg_type = RTM_GETADDR;
136 flag = REQUEST_ADDR;
137 } else if ((request_flags_ & REQUEST_ROUTE) != 0) {
138 req.hdr.nlmsg_type = RTM_GETROUTE;
139 flag = REQUEST_ROUTE;
140 } else
141 return;
142
143 memset(&addr, 0, sizeof(addr));
144 addr.nl_family = AF_NETLINK;
145
146 if (sendto(rtnl_socket_, &req, sizeof(req), 0,
Paul Stewartb50f0b92011-05-16 16:31:42 -0700147 reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
Paul Stewart0af98bf2011-05-10 17:38:08 -0700148 LOG(ERROR) << "RTNL sendto failed";
149 return;
150 }
151 request_flags_ &= ~flag;
152}
153
Paul Stewartb50f0b92011-05-16 16:31:42 -0700154Device::Technology DeviceInfo::GetDeviceTechnology(const char *interface_name) {
155 char contents[1024];
156 int length;
157 int fd;
158 string uevent_file = StringPrintf(kInterfaceUevent, interface_name);
159 string driver_file = StringPrintf(kInterfaceDriver, interface_name);
160 const char *wifi_type;
161 const char *driver_name;
162 int modem_idx;
Paul Stewart0af98bf2011-05-10 17:38:08 -0700163
Paul Stewartb50f0b92011-05-16 16:31:42 -0700164 fd = open(uevent_file.c_str(), O_RDONLY);
165 if (fd < 0)
166 return Device::kUnknown;
167
168 length = read(fd, contents, sizeof(contents) - 1);
169 if (length < 0)
170 return Device::kUnknown;
171
172 /*
173 * If the "uevent" file contains the string "DEVTYPE=wlan\n" at the
174 * start of the file or after a newline, we can safely assume this
175 * is a wifi device.
176 */
177 contents[length] = '\0';
178 wifi_type = strstr(contents, "DEVTYPE=wlan\n");
179 if (wifi_type != NULL && (wifi_type == contents || wifi_type[-1] == '\n'))
180 return Device::kWifi;
181
182 length = readlink(driver_file.c_str(), contents, sizeof(contents)-1);
183 if (length < 0)
184 return Device::kUnknown;
185
186 contents[length] = '\0';
187 driver_name = strrchr(contents, '/');
188 if (driver_name != NULL) {
189 driver_name++;
190 // See if driver for this interface is in a list of known modem driver names
191 for (modem_idx = 0; kModemDrivers[modem_idx] != NULL; modem_idx++)
192 if (strcmp(driver_name, kModemDrivers[modem_idx]) == 0)
193 return Device::kCellular;
194 }
195
196 return Device::kEthernet;
197}
198
199void DeviceInfo::AddLinkMsg(struct nlmsghdr *hdr) {
200 struct ifinfomsg *msg = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(hdr));
201 base::hash_map<int, scoped_refptr<Device> >::iterator ndev =
202 devices_.find(msg->ifi_index);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700203 int bytes = IFLA_PAYLOAD(hdr);
Paul Stewartb50f0b92011-05-16 16:31:42 -0700204 int dev_index = msg->ifi_index;
205 struct rtattr *rta;
206 int rta_bytes;
207 char *link_name = NULL;
208 Device *device;
209 Device::Technology technology;
210 bool is_stub = false;
Paul Stewart0af98bf2011-05-10 17:38:08 -0700211
Paul Stewartb50f0b92011-05-16 16:31:42 -0700212 VLOG(2) << "add link index " << dev_index << " flags " <<
213 msg->ifi_flags;
214
215 if (ndev == devices_.end()) {
216 rta_bytes = IFLA_PAYLOAD(hdr);
217 for (rta = IFLA_RTA(msg); RTA_OK(rta, rta_bytes);
218 rta = RTA_NEXT(rta, rta_bytes)) {
219 if (rta->rta_type == IFLA_IFNAME) {
220 link_name = reinterpret_cast<char *>(RTA_DATA(rta));
221 break;
222 } else {
223 VLOG(2) << " RTA type " << rta->rta_type;
224 }
225 }
226
227 if (link_name != NULL)
228 technology = GetDeviceTechnology(link_name);
229
230 switch (technology) {
231 case Device::kEthernet:
232 device = new Ethernet(control_interface_, dispatcher_,
233 link_name, dev_index);
234 break;
235 case Device::kWifi:
236 device = new WiFi(control_interface_, dispatcher_, link_name, dev_index);
237 break;
238 default:
239 device = new StubDevice(control_interface_, dispatcher_,
240 link_name, dev_index, technology);
241 is_stub = true;
242 }
243
244 devices_[dev_index] = device;
245
246 if (!is_stub)
247 manager_->RegisterDevice(device);
248 } else {
249 device = ndev->second;
250 }
251
252 // TODO(pstew): Send the the flags change upwards to the device
Paul Stewart0af98bf2011-05-10 17:38:08 -0700253}
254
Paul Stewartb50f0b92011-05-16 16:31:42 -0700255void DeviceInfo::DelLinkMsg(struct nlmsghdr *hdr) {
256 struct ifinfomsg *msg = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(hdr));
257 base::hash_map<int, scoped_refptr<Device> >::iterator ndev =
258 devices_.find(msg->ifi_index);
259 int dev_index = msg->ifi_index;
260 Device *device;
261
262 VLOG(2) << "del link index " << dev_index << " flags " <<
263 msg->ifi_flags;
264
265 if (ndev != devices_.end()) {
266 device = ndev->second;
267 devices_.erase(ndev);
268 manager_->DeregisterDevice(device);
269 }
Paul Stewart0af98bf2011-05-10 17:38:08 -0700270}
271
Paul Stewartb50f0b92011-05-16 16:31:42 -0700272void DeviceInfo::AddAddrMsg(struct nlmsghdr *hdr) {
273 struct ifaddrmsg *msg = reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
274 VLOG(2) << "add addrmsg family " << (int) msg->ifa_family << " index " <<
275 msg->ifa_index;
276}
277
278void DeviceInfo::DelAddrMsg(struct nlmsghdr *hdr) {
279 struct ifaddrmsg *msg = reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
280 VLOG(2) << "del addrmsg family " << (int) msg->ifa_family << " index " <<
281 msg->ifa_index;
282}
283
284void DeviceInfo::AddRouteMsg(struct nlmsghdr *hdr) {
285 struct rtmsg *msg = reinterpret_cast<struct rtmsg *>(NLMSG_DATA(hdr));
286 VLOG(2) << "add routemsg family " << (int) msg->rtm_family << " table " <<
287 (int) msg->rtm_table << " proto " << (int) msg->rtm_protocol;
288}
289
290void DeviceInfo::DelRouteMsg(struct nlmsghdr *hdr) {
291 struct rtmsg *msg = reinterpret_cast<struct rtmsg *>(NLMSG_DATA(hdr));
292 VLOG(2) << "del routemsg family " << (int) msg->rtm_family << " table " <<
293 (int) msg->rtm_table << " proto " << (int) msg->rtm_protocol;
Paul Stewart0af98bf2011-05-10 17:38:08 -0700294}
295
296void DeviceInfo::ParseRTNL(InputData *data) {
297 unsigned char *buf = data->buf;
298 unsigned char *end = buf + data->len;
299
300 while (buf < end) {
Paul Stewartb50f0b92011-05-16 16:31:42 -0700301 struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700302 struct nlmsgerr *err;
303
304 if (!NLMSG_OK(hdr, end - buf))
305 break;
306
307 switch (hdr->nlmsg_type) {
308 case NLMSG_NOOP:
309 case NLMSG_OVERRUN:
310 return;
311 case NLMSG_DONE:
312 NextRequest(hdr->nlmsg_seq);
313 return;
314 case NLMSG_ERROR:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700315 err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
Paul Stewart0af98bf2011-05-10 17:38:08 -0700316 LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
317 ")";
318 return;
319 case RTM_NEWLINK:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700320 AddLinkMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700321 break;
322 case RTM_DELLINK:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700323 DelLinkMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700324 break;
325 case RTM_NEWADDR:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700326 AddAddrMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700327 break;
328 case RTM_DELADDR:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700329 DelAddrMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700330 break;
331 case RTM_NEWROUTE:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700332 AddRouteMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700333 break;
334 case RTM_DELROUTE:
Paul Stewartb50f0b92011-05-16 16:31:42 -0700335 DelRouteMsg(hdr);
Paul Stewart0af98bf2011-05-10 17:38:08 -0700336 break;
337 }
338
339 buf += hdr->nlmsg_len;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700340 }
Paul Stewart0af98bf2011-05-10 17:38:08 -0700341}
342
343} // namespace shill