shill: Add IP Address tracking to DeviceInfo
Subscribe to IP Address messages in DeviceInfo, and create a
per-device list of assigned IP Addresses. Provide a method
to flush all globally scoped addresses from a device.
As a result, we can now flush assigned IP addresses when a
Connection is terminated. There is also some incidental cleanup
in RTNLHandler to remove some vestiges of hand-baked RTNL
message encoding.
BUG=chromium-os:19744
TEST=Run new unit tests. Test using ethernet on a netbook to make sure
addresses are added and removed correctly.
Change-Id: I63fd09088e71c43cb1f11a89a8ef15e11074976c
Reviewed-on: http://gerrit.chromium.org/gerrit/7180
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device_info.cc b/device_info.cc
index 638f88f..b905d56 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -37,6 +37,7 @@
using std::map;
using std::string;
+using std::vector;
namespace shill {
@@ -63,7 +64,10 @@
dispatcher_(dispatcher),
manager_(manager),
link_callback_(NewCallback(this, &DeviceInfo::LinkMsgHandler)),
- link_listener_(NULL) {
+ address_callback_(NewCallback(this, &DeviceInfo::AddressMsgHandler)),
+ link_listener_(NULL),
+ address_listener_(NULL),
+ rtnl_handler_(RTNLHandler::GetInstance()) {
}
DeviceInfo::~DeviceInfo() {}
@@ -75,11 +79,15 @@
void DeviceInfo::Start() {
link_listener_.reset(
new RTNLListener(RTNLHandler::kRequestLink, link_callback_.get()));
- RTNLHandler::GetInstance()->RequestDump(RTNLHandler::kRequestLink);
+ address_listener_.reset(
+ new RTNLListener(RTNLHandler::kRequestAddr, address_callback_.get()));
+ rtnl_handler_->RequestDump(RTNLHandler::kRequestLink |
+ RTNLHandler::kRequestAddr);
}
void DeviceInfo::Stop() {
- link_listener_.reset(NULL);
+ link_listener_.reset();
+ address_listener_.reset();
}
void DeviceInfo::RegisterDevice(const DeviceRefPtr &device) {
@@ -140,8 +148,8 @@
}
void DeviceInfo::AddLinkMsgHandler(const RTNLMessage &msg) {
- DCHECK(msg.type() == RTNLMessage::kMessageTypeLink &&
- msg.mode() == RTNLMessage::kMessageModeAdd);
+ DCHECK(msg.type() == RTNLMessage::kTypeLink &&
+ msg.mode() == RTNLMessage::kModeAdd);
int dev_index = msg.interface_index();
Device::Technology technology = Device::kUnknown;
@@ -208,8 +216,8 @@
void DeviceInfo::DelLinkMsgHandler(const RTNLMessage &msg) {
VLOG(2) << __func__ << "(index=" << msg.interface_index() << ")";
- DCHECK(msg.type() == RTNLMessage::kMessageTypeLink &&
- msg.mode() == RTNLMessage::kMessageModeDelete);
+ DCHECK(msg.type() == RTNLMessage::kTypeLink &&
+ msg.mode() == RTNLMessage::kModeDelete);
VLOG(2) << __func__ << "(index=" << msg.interface_index()
<< std::showbase << std::hex
<< ", flags=" << msg.link_status().flags
@@ -231,6 +239,34 @@
return true;
}
+bool DeviceInfo::GetAddresses(int interface_index,
+ vector<AddressData> *addresses) const {
+ const Info *info = GetInfo(interface_index);
+ if (!info) {
+ return false;
+ }
+ *addresses = info->ip_addresses;
+ return true;
+}
+
+void DeviceInfo::FlushAddresses(int interface_index) const {
+ const Info *info = GetInfo(interface_index);
+ if (!info) {
+ return;
+ }
+ const vector<AddressData> &addresses = info->ip_addresses;
+ vector<AddressData>::const_iterator iter;
+ for (iter = addresses.begin(); iter != addresses.end(); ++iter) {
+ if (iter->address.family() == IPAddress::kAddressFamilyIPv4 ||
+ (iter->scope == RT_SCOPE_UNIVERSE &&
+ (iter->flags & ~IFA_F_TEMPORARY) == 0)) {
+ VLOG(2) << __func__ << ": removing ip address from interface "
+ << interface_index;
+ rtnl_handler_->RemoveInterfaceAddress(interface_index, iter->address);
+ }
+ }
+}
+
bool DeviceInfo::GetFlags(int interface_index, unsigned int *flags) const {
const Info *info = GetInfo(interface_index);
if (!info) {
@@ -262,16 +298,50 @@
}
void DeviceInfo::LinkMsgHandler(const RTNLMessage &msg) {
- DCHECK(msg.type() == RTNLMessage::kMessageTypeLink);
- if (msg.mode() == RTNLMessage::kMessageModeAdd) {
+ DCHECK(msg.type() == RTNLMessage::kTypeLink);
+ if (msg.mode() == RTNLMessage::kModeAdd) {
AddLinkMsgHandler(msg);
- } else if (msg.mode() == RTNLMessage::kMessageModeDelete) {
+ } else if (msg.mode() == RTNLMessage::kModeDelete) {
DelLinkMsgHandler(msg);
} else {
NOTREACHED();
}
}
+void DeviceInfo::AddressMsgHandler(const RTNLMessage &msg) {
+ DCHECK(msg.type() == RTNLMessage::kTypeAddress);
+ int interface_index = msg.interface_index();
+ if (!ContainsKey(infos_, interface_index)) {
+ LOG(ERROR) << "Got address type message for unknown index "
+ << interface_index;
+ return;
+ }
+ const RTNLMessage::AddressStatus &status = msg.address_status();
+ IPAddress address(msg.family(),
+ msg.GetAttribute(IFA_ADDRESS),
+ status.prefix_len);
+
+ vector<AddressData> &address_list = infos_[interface_index].ip_addresses;
+ vector<AddressData>::iterator iter;
+ for (iter = address_list.begin(); iter != address_list.end(); ++iter) {
+ if (address.Equals(iter->address)) {
+ break;
+ }
+ }
+ if (iter != address_list.end()) {
+ if (msg.mode() == RTNLMessage::kModeDelete) {
+ VLOG(2) << "Delete address for interface " << interface_index;
+ address_list.erase(iter);
+ } else {
+ iter->flags = status.flags;
+ iter->scope = status.scope;
+ }
+ } else if (msg.mode() == RTNLMessage::kModeAdd) {
+ address_list.push_back(AddressData(address, status.flags, status.scope));
+ VLOG(2) << "Add address for interface " << interface_index;
+ }
+}
+
void DeviceInfo::EnableDeviceIPv6Privacy(const string &iface_name) {
FilePath priv_file(StringPrintf(kInterfaceIPv6Privacy, iface_name.c_str()));
LOG_IF(ERROR, file_util::WriteFile(priv_file, "2", 1) != 1)