blob: addc827699b3984e43d45d1230b0d9c1e86da234 [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"
25#include "shill/rtnl_handler.h"
26#include "shill/rtnl_listener.h"
27#include "shill/shill_event.h"
28
29using std::string;
30
31namespace shill {
32
33RTNLHandler::RTNLHandler()
34 : running_(false),
35 in_request_(false),
36 rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)),
37 rtnl_socket_(-1),
38 request_flags_(0),
39 request_sequence_(0) {
40 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
52void RTNLHandler::Start(EventDispatcher *dispatcher) {
53 struct sockaddr_nl addr;
54
55 if (running_)
56 return;
57
58 rtnl_socket_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
59 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
68 if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
69 sizeof(addr)) < 0) {
70 close(rtnl_socket_);
71 rtnl_socket_ = -1;
72 LOG(ERROR) << "RTNL socket bind failed";
73 return;
74 }
75
76 rtnl_handler_.reset(dispatcher->CreateInputHandler(rtnl_socket_,
77 rtnl_callback_.get()));
78 running_ = true;
79
80 NextRequest(request_sequence_);
81
82 VLOG(2) << "RTNLHandler started";
83 VLOG(2) << "RTNLHandler started";
84}
85
86void RTNLHandler::Stop() {
87 if (!running_)
88 return;
89
90 rtnl_handler_.reset(NULL);
91 close(rtnl_socket_);
92 running_ = false;
93 in_request_ = false;
94 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
137 if (send(rtnl_socket_, &req, sizeof(req), 0) < 0) {
138 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
147 if (!in_request_ && running_)
148 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
166 VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_ <<
167 " " << request_flags_;
168
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
198 if (sendto(rtnl_socket_, &req, sizeof(req), 0,
199 reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
200 LOG(ERROR) << "RTNL sendto failed";
201 return;
202 }
203 request_flags_ &= ~flag;
204 in_request_ = true;
205}
206
207void RTNLHandler::ParseRTNL(InputData *data) {
208 unsigned char *buf = data->buf;
209 unsigned char *end = buf + data->len;
210
211 while (buf < end) {
212 struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
213 struct nlmsgerr *err;
214
215 if (!NLMSG_OK(hdr, end - buf))
216 break;
217
218 switch (hdr->nlmsg_type) {
219 case NLMSG_NOOP:
220 case NLMSG_OVERRUN:
221 return;
222 case NLMSG_DONE:
223 NextRequest(hdr->nlmsg_seq);
224 return;
225 case NLMSG_ERROR:
226 err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
227 LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
228 ")";
229 return;
230 case RTM_NEWLINK:
231 case RTM_DELLINK:
232 DispatchEvent(kRequestLink, hdr);
233 break;
234 case RTM_NEWADDR:
235 case RTM_DELADDR:
236 DispatchEvent(kRequestAddr, hdr);
237 break;
238 case RTM_NEWROUTE:
239 case RTM_DELROUTE:
240 DispatchEvent(kRequestRoute, hdr);
241 break;
242 }
243
244 buf += hdr->nlmsg_len;
245 }
246}
247
248} // namespace shill