blob: ce4447f16fafdb1dfdde8d0c06b8f15f13bc84a7 [file] [log] [blame]
Paul Stewarta3c56f92011-05-26 07:08:52 -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 <errno.h>
6#include <time.h>
7#include <unistd.h>
8#include <string.h>
Darin Petkove0a312e2011-07-20 13:45:28 -07009#include <sys/ioctl.h>
Paul Stewarta3c56f92011-05-26 07:08:52 -070010#include <sys/socket.h>
11#include <arpa/inet.h>
12#include <netinet/ether.h>
13#include <net/if.h>
14#include <net/if_arp.h>
15#include <linux/netlink.h>
16#include <linux/rtnetlink.h>
17#include <fcntl.h>
Paul Stewarta3c56f92011-05-26 07:08:52 -070018
Paul Stewarta3c56f92011-05-26 07:08:52 -070019#include <base/logging.h>
Paul Stewarta3c56f92011-05-26 07:08:52 -070020
21#include "shill/io_handler.h"
Paul Stewart1d18e8c2011-07-15 11:00:31 -070022#include "shill/ip_address.h"
Paul Stewartc39f1132011-06-22 12:02:28 -070023#include "shill/ipconfig.h"
Paul Stewarta3c56f92011-05-26 07:08:52 -070024#include "shill/rtnl_handler.h"
25#include "shill/rtnl_listener.h"
Paul Stewart75e89d22011-08-01 10:00:02 -070026#include "shill/rtnl_message.h"
Paul Stewarta3c56f92011-05-26 07:08:52 -070027#include "shill/shill_event.h"
Darin Petkov633ac6f2011-07-08 13:56:13 -070028#include "shill/sockets.h"
Paul Stewarta3c56f92011-05-26 07:08:52 -070029
30using std::string;
31
32namespace shill {
33
Paul Stewart0d2ada32011-08-09 17:01:57 -070034static base::LazyInstance<RTNLHandler> g_rtnl_handler(
35 base::LINKER_INITIALIZED);
36
Paul Stewarta3c56f92011-05-26 07:08:52 -070037RTNLHandler::RTNLHandler()
Darin Petkov633ac6f2011-07-08 13:56:13 -070038 : sockets_(NULL),
Paul Stewarta3c56f92011-05-26 07:08:52 -070039 in_request_(false),
Paul Stewarta3c56f92011-05-26 07:08:52 -070040 rtnl_socket_(-1),
41 request_flags_(0),
mukesh agrawalf60e4062011-05-27 13:13:41 -070042 request_sequence_(0),
43 rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070044 VLOG(2) << "RTNLHandler created";
45}
46
47RTNLHandler::~RTNLHandler() {
48 VLOG(2) << "RTNLHandler removed";
49 Stop();
50}
51
52RTNLHandler* RTNLHandler::GetInstance() {
Paul Stewart0d2ada32011-08-09 17:01:57 -070053 return g_rtnl_handler.Pointer();
Paul Stewarta3c56f92011-05-26 07:08:52 -070054}
55
Darin Petkov633ac6f2011-07-08 13:56:13 -070056void RTNLHandler::Start(EventDispatcher *dispatcher, Sockets *sockets) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070057 struct sockaddr_nl addr;
58
Darin Petkov633ac6f2011-07-08 13:56:13 -070059 if (sockets_) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070060 return;
Darin Petkov633ac6f2011-07-08 13:56:13 -070061 }
Paul Stewarta3c56f92011-05-26 07:08:52 -070062
Darin Petkov633ac6f2011-07-08 13:56:13 -070063 rtnl_socket_ = sockets->Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
Paul Stewarta3c56f92011-05-26 07:08:52 -070064 if (rtnl_socket_ < 0) {
65 LOG(ERROR) << "Failed to open rtnl socket";
66 return;
67 }
68
69 memset(&addr, 0, sizeof(addr));
70 addr.nl_family = AF_NETLINK;
71 addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
72
Darin Petkov633ac6f2011-07-08 13:56:13 -070073 if (sockets->Bind(rtnl_socket_,
74 reinterpret_cast<struct sockaddr *>(&addr),
75 sizeof(addr)) < 0) {
76 sockets->Close(rtnl_socket_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070077 rtnl_socket_ = -1;
78 LOG(ERROR) << "RTNL socket bind failed";
79 return;
80 }
81
82 rtnl_handler_.reset(dispatcher->CreateInputHandler(rtnl_socket_,
83 rtnl_callback_.get()));
Darin Petkov633ac6f2011-07-08 13:56:13 -070084 sockets_ = sockets;
Paul Stewarta3c56f92011-05-26 07:08:52 -070085
86 NextRequest(request_sequence_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070087 VLOG(2) << "RTNLHandler started";
88}
89
90void RTNLHandler::Stop() {
Darin Petkov633ac6f2011-07-08 13:56:13 -070091 if (!sockets_)
Paul Stewarta3c56f92011-05-26 07:08:52 -070092 return;
93
94 rtnl_handler_.reset(NULL);
Darin Petkov633ac6f2011-07-08 13:56:13 -070095 sockets_->Close(rtnl_socket_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070096 in_request_ = false;
Darin Petkov633ac6f2011-07-08 13:56:13 -070097 sockets_ = NULL;
Paul Stewart65c40f52011-08-08 07:27:46 -070098 request_flags_ = 0;
Paul Stewarta3c56f92011-05-26 07:08:52 -070099 VLOG(2) << "RTNLHandler stopped";
100}
101
102void RTNLHandler::AddListener(RTNLListener *to_add) {
103 std::vector<RTNLListener *>::iterator it;
104 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
105 if (to_add == *it)
106 return;
107 }
108 listeners_.push_back(to_add);
109 VLOG(2) << "RTNLHandler added listener";
110}
111
112void RTNLHandler::RemoveListener(RTNLListener *to_remove) {
113 std::vector<RTNLListener *>::iterator it;
114 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
115 if (to_remove == *it) {
116 listeners_.erase(it);
117 return;
118 }
119 }
120 VLOG(2) << "RTNLHandler removed listener";
121}
122
123void RTNLHandler::SetInterfaceFlags(int interface_index, unsigned int flags,
Darin Petkov0828f5f2011-08-11 10:18:52 -0700124 unsigned int change) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700125 struct rtnl_request {
126 struct nlmsghdr hdr;
127 struct ifinfomsg msg;
128 } req;
129
130 request_sequence_++;
131 memset(&req, 0, sizeof(req));
132
133 req.hdr.nlmsg_len = sizeof(req);
134 req.hdr.nlmsg_flags = NLM_F_REQUEST;
135 req.hdr.nlmsg_pid = 0;
136 req.hdr.nlmsg_seq = request_sequence_;
137 req.hdr.nlmsg_type = RTM_NEWLINK;
138 req.msg.ifi_index = interface_index;
139 req.msg.ifi_flags = flags;
140 req.msg.ifi_change = change;
141
Darin Petkov633ac6f2011-07-08 13:56:13 -0700142 if (sockets_->Send(rtnl_socket_, &req, sizeof(req), 0) < 0) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700143 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
144 }
145}
146
147void RTNLHandler::RequestDump(int request_flags) {
148 request_flags_ |= request_flags;
149
mukesh agrawal47009f82011-08-25 14:07:35 -0700150 VLOG(2) << "RTNLHandler got request to dump "
151 << std::showbase << std::hex
152 << request_flags
153 << std::dec << std::noshowbase;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700154
Darin Petkov633ac6f2011-07-08 13:56:13 -0700155 if (!in_request_ && sockets_)
Paul Stewarta3c56f92011-05-26 07:08:52 -0700156 NextRequest(request_sequence_);
157}
158
Chris Masone2aa97072011-08-09 17:35:08 -0700159void RTNLHandler::DispatchEvent(int type, const RTNLMessage &msg) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700160 std::vector<RTNLListener *>::iterator it;
161 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
Chris Masone2aa97072011-08-09 17:35:08 -0700162 (*it)->NotifyEvent(type, msg);
Paul Stewarta3c56f92011-05-26 07:08:52 -0700163 }
164}
165
166void RTNLHandler::NextRequest(uint32_t seq) {
167 struct rtnl_request {
168 struct nlmsghdr hdr;
169 struct rtgenmsg msg;
170 } req;
171 struct sockaddr_nl addr;
172 int flag = 0;
173
Darin Petkov633ac6f2011-07-08 13:56:13 -0700174 VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_
mukesh agrawal47009f82011-08-25 14:07:35 -0700175 << std::showbase << std::hex
176 << " " << request_flags_
177 << std::dec << std::noshowbase;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700178
179 if (seq != request_sequence_)
180 return;
181
182 request_sequence_++;
183 memset(&req, 0, sizeof(req));
184
185 req.hdr.nlmsg_len = sizeof(req);
186 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
187 req.hdr.nlmsg_pid = 0;
188 req.hdr.nlmsg_seq = request_sequence_;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700189
190 if ((request_flags_ & kRequestLink) != 0) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700191 req.msg.rtgen_family = AF_INET;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700192 req.hdr.nlmsg_type = RTM_GETLINK;
193 flag = kRequestLink;
194 } else if ((request_flags_ & kRequestAddr) != 0) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700195 req.msg.rtgen_family = AF_INET;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700196 req.hdr.nlmsg_type = RTM_GETADDR;
197 flag = kRequestAddr;
198 } else if ((request_flags_ & kRequestRoute) != 0) {
Paul Stewart75e89d22011-08-01 10:00:02 -0700199 req.msg.rtgen_family = AF_INET;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700200 req.hdr.nlmsg_type = RTM_GETROUTE;
201 flag = kRequestRoute;
Paul Stewart75e89d22011-08-01 10:00:02 -0700202 } else if ((request_flags_ & kRequestAddr6) != 0) {
203 req.msg.rtgen_family = AF_INET6;
204 req.hdr.nlmsg_type = RTM_GETADDR;
205 flag = kRequestAddr6;
206 } else if ((request_flags_ & kRequestRoute6) != 0) {
207 req.msg.rtgen_family = AF_INET6;
208 req.hdr.nlmsg_type = RTM_GETROUTE;
209 flag = kRequestRoute6;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700210 } else {
211 in_request_ = false;
212 return;
213 }
214
215 memset(&addr, 0, sizeof(addr));
216 addr.nl_family = AF_NETLINK;
217
Darin Petkov633ac6f2011-07-08 13:56:13 -0700218 if (sockets_->SendTo(rtnl_socket_,
219 &req,
220 sizeof(req),
221 0,
222 reinterpret_cast<struct sockaddr *>(&addr),
223 sizeof(addr)) < 0) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700224 LOG(ERROR) << "RTNL sendto failed";
225 return;
226 }
227 request_flags_ &= ~flag;
228 in_request_ = true;
229}
230
231void RTNLHandler::ParseRTNL(InputData *data) {
232 unsigned char *buf = data->buf;
233 unsigned char *end = buf + data->len;
234
235 while (buf < end) {
236 struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
mukesh agrawalf60e4062011-05-27 13:13:41 -0700237 if (!NLMSG_OK(hdr, static_cast<unsigned int>(end - buf)))
Paul Stewarta3c56f92011-05-26 07:08:52 -0700238 break;
239
Chris Masone2aa97072011-08-09 17:35:08 -0700240 RTNLMessage msg;
241 if (!msg.Decode(ByteString(reinterpret_cast<unsigned char *>(hdr),
242 hdr->nlmsg_len))) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700243
Chris Masone2aa97072011-08-09 17:35:08 -0700244 switch (hdr->nlmsg_type) {
245 case NLMSG_NOOP:
246 case NLMSG_OVERRUN:
247 break;
248 case NLMSG_DONE:
249 NextRequest(hdr->nlmsg_seq);
250 break;
251 case NLMSG_ERROR:
252 {
253 struct nlmsgerr *err =
254 reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
255 LOG(ERROR) << "error " << -err->error << " ("
256 << strerror(-err->error) << ")";
257 break;
258 }
259 default:
260 NOTIMPLEMENTED() << "Unknown NL message type.";
261 }
262 } else {
263 switch (msg.type()) {
264 case RTNLMessage::kMessageTypeLink:
265 DispatchEvent(kRequestLink, msg);
266 break;
267 case RTNLMessage::kMessageTypeAddress:
268 DispatchEvent(kRequestAddr, msg);
269 break;
270 case RTNLMessage::kMessageTypeRoute:
271 DispatchEvent(kRequestRoute, msg);
272 break;
273 default:
274 NOTIMPLEMENTED() << "Unknown RTNL message type.";
275 }
276 }
Paul Stewarta3c56f92011-05-26 07:08:52 -0700277 buf += hdr->nlmsg_len;
278 }
279}
280
Paul Stewartc39f1132011-06-22 12:02:28 -0700281static bool AddAtribute(struct nlmsghdr *hdr, int max_msg_size, int attr_type,
282 const void *attr_data, int attr_len) {
283 int len = RTA_LENGTH(attr_len);
284 int new_msg_size = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(len);
285 struct rtattr *rt_attr;
286
287 if (new_msg_size > max_msg_size)
288 return false;
289
290 rt_attr = reinterpret_cast<struct rtattr *> (reinterpret_cast<unsigned char *>
291 (hdr) +
292 NLMSG_ALIGN(hdr->nlmsg_len));
293 rt_attr->rta_type = attr_type;
294 rt_attr->rta_len = len;
295 memcpy(RTA_DATA(rt_attr), attr_data, attr_len);
296 hdr->nlmsg_len = new_msg_size;
297 return true;
298}
299
300bool RTNLHandler::AddressRequest(int interface_index, int cmd, int flags,
301 const IPConfig &ipconfig) {
302 const IPConfig::Properties &properties = ipconfig.properties();
303 int address_family;
304 int address_size;
305 unsigned char *attrs, *attrs_end;
306 int max_msg_size;
307 struct {
308 struct nlmsghdr hdr;
309 struct ifaddrmsg ifa;
310 unsigned char attrs[256];
311 } req;
312 union {
313 in_addr ip4;
314 in6_addr in6;
315 } addr;
316
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700317 if (properties.address_family == IPAddress::kAddressFamilyIPv4) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700318 address_family = AF_INET;
319 address_size = sizeof(struct in_addr);
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700320 } else if (properties.address_family == IPAddress::kAddressFamilyIPv6) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700321 address_family = AF_INET6;
322 address_size = sizeof(struct in6_addr);
Paul Stewart75e89d22011-08-01 10:00:02 -0700323 } else {
Paul Stewartc39f1132011-06-22 12:02:28 -0700324 return false;
Paul Stewart75e89d22011-08-01 10:00:02 -0700325 }
Paul Stewartc39f1132011-06-22 12:02:28 -0700326
327 request_sequence_++;
328 memset(&req, 0, sizeof(req));
329 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
330 req.hdr.nlmsg_flags = NLM_F_REQUEST | flags;
331 req.hdr.nlmsg_type = cmd;
332 req.ifa.ifa_family = address_family;
333 req.ifa.ifa_index = interface_index;
334
335 max_msg_size = req.hdr.nlmsg_len + sizeof(req.attrs);
336
337 // TODO(pstew): This code only works for Ethernet-like setups,
338 // not with devices that have a peer address like PPP.
339 if (inet_pton(address_family, properties.address.c_str(), &addr) <= 0 ||
340 !AddAtribute(&req.hdr, max_msg_size, IFA_LOCAL, &addr, address_size))
341 return false;
342
343 if (inet_pton(address_family, properties.broadcast_address.c_str(),
344 &addr) <= 0 ||
345 !AddAtribute(&req.hdr, max_msg_size, IFA_BROADCAST, &addr, address_size))
346 return false;
347
348 req.ifa.ifa_prefixlen = properties.subnet_cidr;
349
Darin Petkov633ac6f2011-07-08 13:56:13 -0700350 if (sockets_->Send(rtnl_socket_, &req, req.hdr.nlmsg_len, 0) < 0) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700351 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
352 return false;
353 }
354
355 return true;
356}
357
358bool RTNLHandler::AddInterfaceAddress(int interface_index,
359 const IPConfig &ipconfig) {
360 return AddressRequest(interface_index, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
361 ipconfig);
362}
363
364bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
365 const IPConfig &ipconfig) {
366 return AddressRequest(interface_index, RTM_DELADDR, 0, ipconfig);
367}
368
Darin Petkove0a312e2011-07-20 13:45:28 -0700369int RTNLHandler::GetInterfaceIndex(const string &interface_name) {
370 if (interface_name.empty()) {
371 LOG(ERROR) << "Empty interface name -- unable to obtain index.";
372 return -1;
373 }
374 struct ifreq ifr;
375 if (interface_name.size() >= sizeof(ifr.ifr_name)) {
376 LOG(ERROR) << "Interface name too long: " << interface_name.size() << " >= "
377 << sizeof(ifr.ifr_name);
378 return -1;
379 }
380 int socket = sockets_->Socket(PF_INET, SOCK_DGRAM, 0);
381 if (socket < 0) {
382 PLOG(ERROR) << "Unable to open INET socket";
383 return -1;
384 }
385 ScopedSocketCloser socket_closer(sockets_, socket);
386 memset(&ifr, 0, sizeof(ifr));
387 strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name));
388 if (sockets_->Ioctl(socket, SIOCGIFINDEX, &ifr) < 0) {
389 PLOG(ERROR) << "SIOCGIFINDEX error for " << interface_name;
390 return -1;
391 }
392 return ifr.ifr_ifindex;
393}
394
Paul Stewart75e89d22011-08-01 10:00:02 -0700395bool RTNLHandler::SendMessage(RTNLMessage *message) {
396 message->set_seq(request_sequence_++);
397 ByteString msgdata = message->Encode();
398
399 if (msgdata.GetLength() == 0) {
400 return false;
401 }
402
403 if (sockets_->Send(rtnl_socket_,
404 msgdata.GetData(),
405 msgdata.GetLength(),
406 0) < 0) {
407 PLOG(ERROR) << "RTNL send failed: " << strerror(errno);
408 return false;
409 }
410
411 return true;
412}
413
Paul Stewarta3c56f92011-05-26 07:08:52 -0700414} // namespace shill