blob: 089180eb1a44cc6b26c6de8a21e1373ed36b72f1 [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>
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>
16#include <fcntl.h>
17#include <string>
18
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"
26#include "shill/shill_event.h"
Darin Petkov633ac6f2011-07-08 13:56:13 -070027#include "shill/sockets.h"
Paul Stewarta3c56f92011-05-26 07:08:52 -070028
29using std::string;
30
31namespace shill {
32
33RTNLHandler::RTNLHandler()
Darin Petkov633ac6f2011-07-08 13:56:13 -070034 : sockets_(NULL),
Paul Stewarta3c56f92011-05-26 07:08:52 -070035 in_request_(false),
Paul Stewarta3c56f92011-05-26 07:08:52 -070036 rtnl_socket_(-1),
37 request_flags_(0),
mukesh agrawalf60e4062011-05-27 13:13:41 -070038 request_sequence_(0),
39 rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070040 VLOG(2) << "RTNLHandler created";
41}
42
43RTNLHandler::~RTNLHandler() {
44 VLOG(2) << "RTNLHandler removed";
45 Stop();
46}
47
48RTNLHandler* RTNLHandler::GetInstance() {
49 return Singleton<RTNLHandler>::get();
50}
51
Darin Petkov633ac6f2011-07-08 13:56:13 -070052void RTNLHandler::Start(EventDispatcher *dispatcher, Sockets *sockets) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070053 struct sockaddr_nl addr;
54
Darin Petkov633ac6f2011-07-08 13:56:13 -070055 if (sockets_) {
Paul Stewarta3c56f92011-05-26 07:08:52 -070056 return;
Darin Petkov633ac6f2011-07-08 13:56:13 -070057 }
Paul Stewarta3c56f92011-05-26 07:08:52 -070058
Darin Petkov633ac6f2011-07-08 13:56:13 -070059 rtnl_socket_ = sockets->Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
Paul Stewarta3c56f92011-05-26 07:08:52 -070060 if (rtnl_socket_ < 0) {
61 LOG(ERROR) << "Failed to open rtnl socket";
62 return;
63 }
64
65 memset(&addr, 0, sizeof(addr));
66 addr.nl_family = AF_NETLINK;
67 addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
68
Darin Petkov633ac6f2011-07-08 13:56:13 -070069 if (sockets->Bind(rtnl_socket_,
70 reinterpret_cast<struct sockaddr *>(&addr),
71 sizeof(addr)) < 0) {
72 sockets->Close(rtnl_socket_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070073 rtnl_socket_ = -1;
74 LOG(ERROR) << "RTNL socket bind failed";
75 return;
76 }
77
78 rtnl_handler_.reset(dispatcher->CreateInputHandler(rtnl_socket_,
79 rtnl_callback_.get()));
Darin Petkov633ac6f2011-07-08 13:56:13 -070080 sockets_ = sockets;
Paul Stewarta3c56f92011-05-26 07:08:52 -070081
82 NextRequest(request_sequence_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070083 VLOG(2) << "RTNLHandler started";
84}
85
86void RTNLHandler::Stop() {
Darin Petkov633ac6f2011-07-08 13:56:13 -070087 if (!sockets_)
Paul Stewarta3c56f92011-05-26 07:08:52 -070088 return;
89
90 rtnl_handler_.reset(NULL);
Darin Petkov633ac6f2011-07-08 13:56:13 -070091 sockets_->Close(rtnl_socket_);
Paul Stewarta3c56f92011-05-26 07:08:52 -070092 in_request_ = false;
Darin Petkov633ac6f2011-07-08 13:56:13 -070093 sockets_ = NULL;
Paul Stewarta3c56f92011-05-26 07:08:52 -070094 VLOG(2) << "RTNLHandler stopped";
95}
96
97void RTNLHandler::AddListener(RTNLListener *to_add) {
98 std::vector<RTNLListener *>::iterator it;
99 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
100 if (to_add == *it)
101 return;
102 }
103 listeners_.push_back(to_add);
104 VLOG(2) << "RTNLHandler added listener";
105}
106
107void RTNLHandler::RemoveListener(RTNLListener *to_remove) {
108 std::vector<RTNLListener *>::iterator it;
109 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
110 if (to_remove == *it) {
111 listeners_.erase(it);
112 return;
113 }
114 }
115 VLOG(2) << "RTNLHandler removed listener";
116}
117
118void RTNLHandler::SetInterfaceFlags(int interface_index, unsigned int flags,
119 unsigned int change) {
120 struct rtnl_request {
121 struct nlmsghdr hdr;
122 struct ifinfomsg msg;
123 } req;
124
125 request_sequence_++;
126 memset(&req, 0, sizeof(req));
127
128 req.hdr.nlmsg_len = sizeof(req);
129 req.hdr.nlmsg_flags = NLM_F_REQUEST;
130 req.hdr.nlmsg_pid = 0;
131 req.hdr.nlmsg_seq = request_sequence_;
132 req.hdr.nlmsg_type = RTM_NEWLINK;
133 req.msg.ifi_index = interface_index;
134 req.msg.ifi_flags = flags;
135 req.msg.ifi_change = change;
136
Darin Petkov633ac6f2011-07-08 13:56:13 -0700137 if (sockets_->Send(rtnl_socket_, &req, sizeof(req), 0) < 0) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700138 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
139 }
140}
141
142void RTNLHandler::RequestDump(int request_flags) {
143 request_flags_ |= request_flags;
144
145 VLOG(2) << "RTNLHandler got request to dump " << request_flags;
146
Darin Petkov633ac6f2011-07-08 13:56:13 -0700147 if (!in_request_ && sockets_)
Paul Stewarta3c56f92011-05-26 07:08:52 -0700148 NextRequest(request_sequence_);
149}
150
151void RTNLHandler::DispatchEvent(int type, struct nlmsghdr *hdr) {
152 std::vector<RTNLListener *>::iterator it;
153 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
154 (*it)->NotifyEvent(type, hdr);
155 }
156}
157
158void RTNLHandler::NextRequest(uint32_t seq) {
159 struct rtnl_request {
160 struct nlmsghdr hdr;
161 struct rtgenmsg msg;
162 } req;
163 struct sockaddr_nl addr;
164 int flag = 0;
165
Darin Petkov633ac6f2011-07-08 13:56:13 -0700166 VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_
167 << " " << request_flags_;
Paul Stewarta3c56f92011-05-26 07:08:52 -0700168
169 if (seq != request_sequence_)
170 return;
171
172 request_sequence_++;
173 memset(&req, 0, sizeof(req));
174
175 req.hdr.nlmsg_len = sizeof(req);
176 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
177 req.hdr.nlmsg_pid = 0;
178 req.hdr.nlmsg_seq = request_sequence_;
179 req.msg.rtgen_family = AF_INET;
180
181 if ((request_flags_ & kRequestLink) != 0) {
182 req.hdr.nlmsg_type = RTM_GETLINK;
183 flag = kRequestLink;
184 } else if ((request_flags_ & kRequestAddr) != 0) {
185 req.hdr.nlmsg_type = RTM_GETADDR;
186 flag = kRequestAddr;
187 } else if ((request_flags_ & kRequestRoute) != 0) {
188 req.hdr.nlmsg_type = RTM_GETROUTE;
189 flag = kRequestRoute;
190 } else {
191 in_request_ = false;
192 return;
193 }
194
195 memset(&addr, 0, sizeof(addr));
196 addr.nl_family = AF_NETLINK;
197
Darin Petkov633ac6f2011-07-08 13:56:13 -0700198 if (sockets_->SendTo(rtnl_socket_,
199 &req,
200 sizeof(req),
201 0,
202 reinterpret_cast<struct sockaddr *>(&addr),
203 sizeof(addr)) < 0) {
Paul Stewarta3c56f92011-05-26 07:08:52 -0700204 LOG(ERROR) << "RTNL sendto failed";
205 return;
206 }
207 request_flags_ &= ~flag;
208 in_request_ = true;
209}
210
211void RTNLHandler::ParseRTNL(InputData *data) {
212 unsigned char *buf = data->buf;
213 unsigned char *end = buf + data->len;
214
215 while (buf < end) {
216 struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
217 struct nlmsgerr *err;
218
mukesh agrawalf60e4062011-05-27 13:13:41 -0700219 if (!NLMSG_OK(hdr, static_cast<unsigned int>(end - buf)))
Paul Stewarta3c56f92011-05-26 07:08:52 -0700220 break;
221
222 switch (hdr->nlmsg_type) {
223 case NLMSG_NOOP:
224 case NLMSG_OVERRUN:
225 return;
226 case NLMSG_DONE:
227 NextRequest(hdr->nlmsg_seq);
228 return;
229 case NLMSG_ERROR:
230 err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
231 LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
232 ")";
233 return;
234 case RTM_NEWLINK:
235 case RTM_DELLINK:
236 DispatchEvent(kRequestLink, hdr);
237 break;
238 case RTM_NEWADDR:
239 case RTM_DELADDR:
240 DispatchEvent(kRequestAddr, hdr);
241 break;
242 case RTM_NEWROUTE:
243 case RTM_DELROUTE:
244 DispatchEvent(kRequestRoute, hdr);
245 break;
246 }
247
248 buf += hdr->nlmsg_len;
249 }
250}
251
Paul Stewartc39f1132011-06-22 12:02:28 -0700252static bool AddAtribute(struct nlmsghdr *hdr, int max_msg_size, int attr_type,
253 const void *attr_data, int attr_len) {
254 int len = RTA_LENGTH(attr_len);
255 int new_msg_size = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(len);
256 struct rtattr *rt_attr;
257
258 if (new_msg_size > max_msg_size)
259 return false;
260
261 rt_attr = reinterpret_cast<struct rtattr *> (reinterpret_cast<unsigned char *>
262 (hdr) +
263 NLMSG_ALIGN(hdr->nlmsg_len));
264 rt_attr->rta_type = attr_type;
265 rt_attr->rta_len = len;
266 memcpy(RTA_DATA(rt_attr), attr_data, attr_len);
267 hdr->nlmsg_len = new_msg_size;
268 return true;
269}
270
271bool RTNLHandler::AddressRequest(int interface_index, int cmd, int flags,
272 const IPConfig &ipconfig) {
273 const IPConfig::Properties &properties = ipconfig.properties();
274 int address_family;
275 int address_size;
276 unsigned char *attrs, *attrs_end;
277 int max_msg_size;
278 struct {
279 struct nlmsghdr hdr;
280 struct ifaddrmsg ifa;
281 unsigned char attrs[256];
282 } req;
283 union {
284 in_addr ip4;
285 in6_addr in6;
286 } addr;
287
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700288 if (properties.address_family == IPAddress::kAddressFamilyIPv4) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700289 address_family = AF_INET;
290 address_size = sizeof(struct in_addr);
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700291 } else if (properties.address_family == IPAddress::kAddressFamilyIPv6) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700292 address_family = AF_INET6;
293 address_size = sizeof(struct in6_addr);
294 } else
295 return false;
296
297 request_sequence_++;
298 memset(&req, 0, sizeof(req));
299 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
300 req.hdr.nlmsg_flags = NLM_F_REQUEST | flags;
301 req.hdr.nlmsg_type = cmd;
302 req.ifa.ifa_family = address_family;
303 req.ifa.ifa_index = interface_index;
304
305 max_msg_size = req.hdr.nlmsg_len + sizeof(req.attrs);
306
307 // TODO(pstew): This code only works for Ethernet-like setups,
308 // not with devices that have a peer address like PPP.
309 if (inet_pton(address_family, properties.address.c_str(), &addr) <= 0 ||
310 !AddAtribute(&req.hdr, max_msg_size, IFA_LOCAL, &addr, address_size))
311 return false;
312
313 if (inet_pton(address_family, properties.broadcast_address.c_str(),
314 &addr) <= 0 ||
315 !AddAtribute(&req.hdr, max_msg_size, IFA_BROADCAST, &addr, address_size))
316 return false;
317
318 req.ifa.ifa_prefixlen = properties.subnet_cidr;
319
Darin Petkov633ac6f2011-07-08 13:56:13 -0700320 if (sockets_->Send(rtnl_socket_, &req, req.hdr.nlmsg_len, 0) < 0) {
Paul Stewartc39f1132011-06-22 12:02:28 -0700321 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
322 return false;
323 }
324
325 return true;
326}
327
328bool RTNLHandler::AddInterfaceAddress(int interface_index,
329 const IPConfig &ipconfig) {
330 return AddressRequest(interface_index, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
331 ipconfig);
332}
333
334bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
335 const IPConfig &ipconfig) {
336 return AddressRequest(interface_index, RTM_DELADDR, 0, ipconfig);
337}
338
Paul Stewarta3c56f92011-05-26 07:08:52 -0700339} // namespace shill