blob: 584e36d3143499b2113c60a66bd1477d2b28e41b [file] [log] [blame]
// 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 <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 <string>
#include <base/callback.h>
#include <base/logging.h>
#include <base/hash_tables.h>
#include <base/scoped_ptr.h>
#include "shill/control_interface.h"
#include "shill/service.h"
#include "shill/device_info.h"
using std::string;
namespace shill {
DeviceInfo::DeviceInfo(EventDispatcher *dispatcher)
: running_(false),
dispatcher_(dispatcher),
rtnl_callback_(NewCallback(this, &DeviceInfo::ParseRTNL)),
rtnl_socket_(-1),
request_flags_(0),
request_sequence_(0) {
LOG(INFO) << "DeviceInfo initialized";
}
DeviceInfo::~DeviceInfo() {
Stop();
}
void DeviceInfo::Start()
{
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_, (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;
request_flags_ = REQUEST_LINK | REQUEST_ADDR | REQUEST_ROUTE;
NextRequest(request_sequence_);
LOG(INFO) << "DeviceInfo started";
}
void DeviceInfo::Stop()
{
if (!running_)
return;
rtnl_handler_.reset(NULL);
close(rtnl_socket_);
running_ = false;
}
void DeviceInfo::NextRequest(uint32_t seq) {
struct rtnl_request {
struct nlmsghdr hdr;
struct rtgenmsg msg;
} req;
struct sockaddr_nl addr;
int flag = 0;
LOG(INFO) << __func__ << ": " << seq << " :: " << request_sequence_;
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_ & REQUEST_LINK) != 0) {
req.hdr.nlmsg_type = RTM_GETLINK;
flag = REQUEST_LINK;
} else if ((request_flags_ & REQUEST_ADDR) != 0) {
req.hdr.nlmsg_type = RTM_GETADDR;
flag = REQUEST_ADDR;
} else if ((request_flags_ & REQUEST_ROUTE) != 0) {
req.hdr.nlmsg_type = RTM_GETROUTE;
flag = REQUEST_ROUTE;
} else
return;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
if (sendto(rtnl_socket_, &req, sizeof(req), 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOG(ERROR) << "RTNL sendto failed";
return;
}
request_flags_ &= ~flag;
}
void DeviceInfo::LinkMsg(unsigned char *buf, bool del) {
struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
int bytes = IFLA_PAYLOAD(hdr);
LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
": del = " << del;
}
void DeviceInfo::AddrMsg(unsigned char *buf, bool del) {
struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
": del = " << del;
}
void DeviceInfo::RouteMsg(unsigned char *buf, bool del) {
struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
": del = " << del;
}
void DeviceInfo::ParseRTNL(InputData *data) {
unsigned char *buf = data->buf;
unsigned char *end = buf + data->len;
while (buf < end) {
struct nlmsghdr *hdr = (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 = (nlmsgerr *) NLMSG_DATA(hdr);
LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
")";
return;
case RTM_NEWLINK:
LinkMsg(buf, false);
break;
case RTM_DELLINK:
LinkMsg(buf, true);
break;
case RTM_NEWADDR:
AddrMsg(buf, false);
break;
case RTM_DELADDR:
AddrMsg(buf, true);
break;
case RTM_NEWROUTE:
RouteMsg(buf, false);
break;
case RTM_DELROUTE:
RouteMsg(buf, true);
break;
}
buf += hdr->nlmsg_len;
}
}
} // namespace shill