blob: bcfd0202f6d9c4dcee643bbe73598a60ae3403c8 [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
19#include <base/callback_old.h>
20#include <base/logging.h>
21#include <base/memory/scoped_ptr.h>
22#include <base/memory/singleton.h>
23
24#include "shill/io_handler.h"
Paul Stewartc39f1132011-06-22 12:02:28 -070025#include "shill/ipconfig.h"
Paul Stewarta3c56f92011-05-26 07:08:52 -070026#include "shill/rtnl_handler.h"
27#include "shill/rtnl_listener.h"
28#include "shill/shill_event.h"
29
30using std::string;
31
32namespace shill {
33
34RTNLHandler::RTNLHandler()
35 : running_(false),
36 in_request_(false),
37 rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)),
38 rtnl_socket_(-1),
39 request_flags_(0),
40 request_sequence_(0) {
41 VLOG(2) << "RTNLHandler created";
42}
43
44RTNLHandler::~RTNLHandler() {
45 VLOG(2) << "RTNLHandler removed";
46 Stop();
47}
48
49RTNLHandler* RTNLHandler::GetInstance() {
50 return Singleton<RTNLHandler>::get();
51}
52
53void RTNLHandler::Start(EventDispatcher *dispatcher) {
54 struct sockaddr_nl addr;
55
56 if (running_)
57 return;
58
59 rtnl_socket_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
60 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
69 if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
70 sizeof(addr)) < 0) {
71 close(rtnl_socket_);
72 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()));
79 running_ = true;
80
81 NextRequest(request_sequence_);
82
83 VLOG(2) << "RTNLHandler started";
84 VLOG(2) << "RTNLHandler started";
85}
86
87void RTNLHandler::Stop() {
88 if (!running_)
89 return;
90
91 rtnl_handler_.reset(NULL);
92 close(rtnl_socket_);
93 running_ = false;
94 in_request_ = false;
95 VLOG(2) << "RTNLHandler stopped";
96}
97
98void RTNLHandler::AddListener(RTNLListener *to_add) {
99 std::vector<RTNLListener *>::iterator it;
100 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
101 if (to_add == *it)
102 return;
103 }
104 listeners_.push_back(to_add);
105 VLOG(2) << "RTNLHandler added listener";
106}
107
108void RTNLHandler::RemoveListener(RTNLListener *to_remove) {
109 std::vector<RTNLListener *>::iterator it;
110 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
111 if (to_remove == *it) {
112 listeners_.erase(it);
113 return;
114 }
115 }
116 VLOG(2) << "RTNLHandler removed listener";
117}
118
119void RTNLHandler::SetInterfaceFlags(int interface_index, unsigned int flags,
120 unsigned int change) {
121 struct rtnl_request {
122 struct nlmsghdr hdr;
123 struct ifinfomsg msg;
124 } req;
125
126 request_sequence_++;
127 memset(&req, 0, sizeof(req));
128
129 req.hdr.nlmsg_len = sizeof(req);
130 req.hdr.nlmsg_flags = NLM_F_REQUEST;
131 req.hdr.nlmsg_pid = 0;
132 req.hdr.nlmsg_seq = request_sequence_;
133 req.hdr.nlmsg_type = RTM_NEWLINK;
134 req.msg.ifi_index = interface_index;
135 req.msg.ifi_flags = flags;
136 req.msg.ifi_change = change;
137
138 if (send(rtnl_socket_, &req, sizeof(req), 0) < 0) {
139 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
140 }
141}
142
143void RTNLHandler::RequestDump(int request_flags) {
144 request_flags_ |= request_flags;
145
146 VLOG(2) << "RTNLHandler got request to dump " << request_flags;
147
148 if (!in_request_ && running_)
149 NextRequest(request_sequence_);
150}
151
152void RTNLHandler::DispatchEvent(int type, struct nlmsghdr *hdr) {
153 std::vector<RTNLListener *>::iterator it;
154 for (it = listeners_.begin(); it != listeners_.end(); ++it) {
155 (*it)->NotifyEvent(type, hdr);
156 }
157}
158
159void RTNLHandler::NextRequest(uint32_t seq) {
160 struct rtnl_request {
161 struct nlmsghdr hdr;
162 struct rtgenmsg msg;
163 } req;
164 struct sockaddr_nl addr;
165 int flag = 0;
166
167 VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_ <<
168 " " << request_flags_;
169
170 if (seq != request_sequence_)
171 return;
172
173 request_sequence_++;
174 memset(&req, 0, sizeof(req));
175
176 req.hdr.nlmsg_len = sizeof(req);
177 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
178 req.hdr.nlmsg_pid = 0;
179 req.hdr.nlmsg_seq = request_sequence_;
180 req.msg.rtgen_family = AF_INET;
181
182 if ((request_flags_ & kRequestLink) != 0) {
183 req.hdr.nlmsg_type = RTM_GETLINK;
184 flag = kRequestLink;
185 } else if ((request_flags_ & kRequestAddr) != 0) {
186 req.hdr.nlmsg_type = RTM_GETADDR;
187 flag = kRequestAddr;
188 } else if ((request_flags_ & kRequestRoute) != 0) {
189 req.hdr.nlmsg_type = RTM_GETROUTE;
190 flag = kRequestRoute;
191 } else {
192 in_request_ = false;
193 return;
194 }
195
196 memset(&addr, 0, sizeof(addr));
197 addr.nl_family = AF_NETLINK;
198
199 if (sendto(rtnl_socket_, &req, sizeof(req), 0,
200 reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
201 LOG(ERROR) << "RTNL sendto failed";
202 return;
203 }
204 request_flags_ &= ~flag;
205 in_request_ = true;
206}
207
208void RTNLHandler::ParseRTNL(InputData *data) {
209 unsigned char *buf = data->buf;
210 unsigned char *end = buf + data->len;
211
212 while (buf < end) {
213 struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
214 struct nlmsgerr *err;
215
216 if (!NLMSG_OK(hdr, end - buf))
217 break;
218
219 switch (hdr->nlmsg_type) {
220 case NLMSG_NOOP:
221 case NLMSG_OVERRUN:
222 return;
223 case NLMSG_DONE:
224 NextRequest(hdr->nlmsg_seq);
225 return;
226 case NLMSG_ERROR:
227 err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
228 LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
229 ")";
230 return;
231 case RTM_NEWLINK:
232 case RTM_DELLINK:
233 DispatchEvent(kRequestLink, hdr);
234 break;
235 case RTM_NEWADDR:
236 case RTM_DELADDR:
237 DispatchEvent(kRequestAddr, hdr);
238 break;
239 case RTM_NEWROUTE:
240 case RTM_DELROUTE:
241 DispatchEvent(kRequestRoute, hdr);
242 break;
243 }
244
245 buf += hdr->nlmsg_len;
246 }
247}
248
Paul Stewartc39f1132011-06-22 12:02:28 -0700249static bool AddAtribute(struct nlmsghdr *hdr, int max_msg_size, int attr_type,
250 const void *attr_data, int attr_len) {
251 int len = RTA_LENGTH(attr_len);
252 int new_msg_size = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(len);
253 struct rtattr *rt_attr;
254
255 if (new_msg_size > max_msg_size)
256 return false;
257
258 rt_attr = reinterpret_cast<struct rtattr *> (reinterpret_cast<unsigned char *>
259 (hdr) +
260 NLMSG_ALIGN(hdr->nlmsg_len));
261 rt_attr->rta_type = attr_type;
262 rt_attr->rta_len = len;
263 memcpy(RTA_DATA(rt_attr), attr_data, attr_len);
264 hdr->nlmsg_len = new_msg_size;
265 return true;
266}
267
268bool RTNLHandler::AddressRequest(int interface_index, int cmd, int flags,
269 const IPConfig &ipconfig) {
270 const IPConfig::Properties &properties = ipconfig.properties();
271 int address_family;
272 int address_size;
273 unsigned char *attrs, *attrs_end;
274 int max_msg_size;
275 struct {
276 struct nlmsghdr hdr;
277 struct ifaddrmsg ifa;
278 unsigned char attrs[256];
279 } req;
280 union {
281 in_addr ip4;
282 in6_addr in6;
283 } addr;
284
285 if (properties.address_family == IPConfig::kAddressFamilyIPv4) {
286 address_family = AF_INET;
287 address_size = sizeof(struct in_addr);
288 } else if (properties.address_family == IPConfig::kAddressFamilyIPv6) {
289 address_family = AF_INET6;
290 address_size = sizeof(struct in6_addr);
291 } else
292 return false;
293
294 request_sequence_++;
295 memset(&req, 0, sizeof(req));
296 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
297 req.hdr.nlmsg_flags = NLM_F_REQUEST | flags;
298 req.hdr.nlmsg_type = cmd;
299 req.ifa.ifa_family = address_family;
300 req.ifa.ifa_index = interface_index;
301
302 max_msg_size = req.hdr.nlmsg_len + sizeof(req.attrs);
303
304 // TODO(pstew): This code only works for Ethernet-like setups,
305 // not with devices that have a peer address like PPP.
306 if (inet_pton(address_family, properties.address.c_str(), &addr) <= 0 ||
307 !AddAtribute(&req.hdr, max_msg_size, IFA_LOCAL, &addr, address_size))
308 return false;
309
310 if (inet_pton(address_family, properties.broadcast_address.c_str(),
311 &addr) <= 0 ||
312 !AddAtribute(&req.hdr, max_msg_size, IFA_BROADCAST, &addr, address_size))
313 return false;
314
315 req.ifa.ifa_prefixlen = properties.subnet_cidr;
316
317 if (send(rtnl_socket_, &req, req.hdr.nlmsg_len, 0) < 0) {
318 LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
319 return false;
320 }
321
322 return true;
323}
324
325bool RTNLHandler::AddInterfaceAddress(int interface_index,
326 const IPConfig &ipconfig) {
327 return AddressRequest(interface_index, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
328 ipconfig);
329}
330
331bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
332 const IPConfig &ipconfig) {
333 return AddressRequest(interface_index, RTM_DELADDR, 0, ipconfig);
334}
335
Paul Stewarta3c56f92011-05-26 07:08:52 -0700336} // namespace shill