Split off rtnl facility from device_info
Create an "rtnl_handler" singleton for conversing with rtnl socket.
Create "listener" objects that hold callbacks to interested parties.
BUG=chromium-os:15804
TEST=Rerun unit tests
Change-Id: Ica845b39ce6a0885a4e6d2560146ff8f5f45b900
Reviewed-on: http://gerrit.chromium.org/gerrit/1632
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
new file mode 100644
index 0000000..addc827
--- /dev/null
+++ b/rtnl_handler.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <fcntl.h>
+#include <string>
+
+#include <base/callback_old.h>
+#include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/memory/singleton.h>
+
+#include "shill/io_handler.h"
+#include "shill/rtnl_handler.h"
+#include "shill/rtnl_listener.h"
+#include "shill/shill_event.h"
+
+using std::string;
+
+namespace shill {
+
+RTNLHandler::RTNLHandler()
+ : running_(false),
+ in_request_(false),
+ rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)),
+ rtnl_socket_(-1),
+ request_flags_(0),
+ request_sequence_(0) {
+ VLOG(2) << "RTNLHandler created";
+}
+
+RTNLHandler::~RTNLHandler() {
+ VLOG(2) << "RTNLHandler removed";
+ Stop();
+}
+
+RTNLHandler* RTNLHandler::GetInstance() {
+ return Singleton<RTNLHandler>::get();
+}
+
+void RTNLHandler::Start(EventDispatcher *dispatcher) {
+ struct sockaddr_nl addr;
+
+ if (running_)
+ return;
+
+ rtnl_socket_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (rtnl_socket_ < 0) {
+ LOG(ERROR) << "Failed to open rtnl socket";
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
+
+ if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
+ sizeof(addr)) < 0) {
+ close(rtnl_socket_);
+ rtnl_socket_ = -1;
+ LOG(ERROR) << "RTNL socket bind failed";
+ return;
+ }
+
+ rtnl_handler_.reset(dispatcher->CreateInputHandler(rtnl_socket_,
+ rtnl_callback_.get()));
+ running_ = true;
+
+ NextRequest(request_sequence_);
+
+ VLOG(2) << "RTNLHandler started";
+ VLOG(2) << "RTNLHandler started";
+}
+
+void RTNLHandler::Stop() {
+ if (!running_)
+ return;
+
+ rtnl_handler_.reset(NULL);
+ close(rtnl_socket_);
+ running_ = false;
+ in_request_ = false;
+ VLOG(2) << "RTNLHandler stopped";
+}
+
+void RTNLHandler::AddListener(RTNLListener *to_add) {
+ std::vector<RTNLListener *>::iterator it;
+ for (it = listeners_.begin(); it != listeners_.end(); ++it) {
+ if (to_add == *it)
+ return;
+ }
+ listeners_.push_back(to_add);
+ VLOG(2) << "RTNLHandler added listener";
+}
+
+void RTNLHandler::RemoveListener(RTNLListener *to_remove) {
+ std::vector<RTNLListener *>::iterator it;
+ for (it = listeners_.begin(); it != listeners_.end(); ++it) {
+ if (to_remove == *it) {
+ listeners_.erase(it);
+ return;
+ }
+ }
+ VLOG(2) << "RTNLHandler removed listener";
+}
+
+void RTNLHandler::SetInterfaceFlags(int interface_index, unsigned int flags,
+ unsigned int change) {
+ struct rtnl_request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg msg;
+ } req;
+
+ request_sequence_++;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_pid = 0;
+ req.hdr.nlmsg_seq = request_sequence_;
+ req.hdr.nlmsg_type = RTM_NEWLINK;
+ req.msg.ifi_index = interface_index;
+ req.msg.ifi_flags = flags;
+ req.msg.ifi_change = change;
+
+ if (send(rtnl_socket_, &req, sizeof(req), 0) < 0) {
+ LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
+ }
+}
+
+void RTNLHandler::RequestDump(int request_flags) {
+ request_flags_ |= request_flags;
+
+ VLOG(2) << "RTNLHandler got request to dump " << request_flags;
+
+ if (!in_request_ && running_)
+ NextRequest(request_sequence_);
+}
+
+void RTNLHandler::DispatchEvent(int type, struct nlmsghdr *hdr) {
+ std::vector<RTNLListener *>::iterator it;
+ for (it = listeners_.begin(); it != listeners_.end(); ++it) {
+ (*it)->NotifyEvent(type, hdr);
+ }
+}
+
+void RTNLHandler::NextRequest(uint32_t seq) {
+ struct rtnl_request {
+ struct nlmsghdr hdr;
+ struct rtgenmsg msg;
+ } req;
+ struct sockaddr_nl addr;
+ int flag = 0;
+
+ VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_ <<
+ " " << request_flags_;
+
+ if (seq != request_sequence_)
+ return;
+
+ request_sequence_++;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ req.hdr.nlmsg_pid = 0;
+ req.hdr.nlmsg_seq = request_sequence_;
+ req.msg.rtgen_family = AF_INET;
+
+ if ((request_flags_ & kRequestLink) != 0) {
+ req.hdr.nlmsg_type = RTM_GETLINK;
+ flag = kRequestLink;
+ } else if ((request_flags_ & kRequestAddr) != 0) {
+ req.hdr.nlmsg_type = RTM_GETADDR;
+ flag = kRequestAddr;
+ } else if ((request_flags_ & kRequestRoute) != 0) {
+ req.hdr.nlmsg_type = RTM_GETROUTE;
+ flag = kRequestRoute;
+ } else {
+ in_request_ = false;
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+
+ if (sendto(rtnl_socket_, &req, sizeof(req), 0,
+ reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
+ LOG(ERROR) << "RTNL sendto failed";
+ return;
+ }
+ request_flags_ &= ~flag;
+ in_request_ = true;
+}
+
+void RTNLHandler::ParseRTNL(InputData *data) {
+ unsigned char *buf = data->buf;
+ unsigned char *end = buf + data->len;
+
+ while (buf < end) {
+ struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
+ struct nlmsgerr *err;
+
+ if (!NLMSG_OK(hdr, end - buf))
+ break;
+
+ switch (hdr->nlmsg_type) {
+ case NLMSG_NOOP:
+ case NLMSG_OVERRUN:
+ return;
+ case NLMSG_DONE:
+ NextRequest(hdr->nlmsg_seq);
+ return;
+ case NLMSG_ERROR:
+ err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
+ LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
+ ")";
+ return;
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ DispatchEvent(kRequestLink, hdr);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ DispatchEvent(kRequestAddr, hdr);
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ DispatchEvent(kRequestRoute, hdr);
+ break;
+ }
+
+ buf += hdr->nlmsg_len;
+ }
+}
+
+} // namespace shill