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