shill: Connection: Add facility to add host routes
This requires a facility for tracking outstanding RTNL route requests,
and adding routes when they the response arrives. A few small fixes
to RTNL handling needed to be added.
BUG=chromium-os:27483
TEST=New Unit Tests, manual: Assocated my new Neptune proto to test
network.
Change-Id: I701fa244041ad9e0d0a502a263d83792ab3c9114
Reviewed-on: https://gerrit.chromium.org/gerrit/17889
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/connection.cc b/connection.cc
index fde6b0a..a69a690 100644
--- a/connection.cc
+++ b/connection.cc
@@ -10,6 +10,7 @@
#include "shill/device_info.h"
#include "shill/resolver.h"
#include "shill/routing_table.h"
+#include "shill/routing_table_entry.h"
#include "shill/rtnl_handler.h"
using std::string;
@@ -127,6 +128,19 @@
}
}
+bool Connection::RequestHostRoute(const IPAddress &address) {
+ // Set the prefix to be the entire address size.
+ IPAddress address_prefix(address);
+ address_prefix.set_prefix(address_prefix.GetLength() * 8);
+
+ if (!routing_table_->RequestRouteToHost(address_prefix, interface_index_)) {
+ LOG(ERROR) << "Could not request route to " << address.ToString();
+ return false;
+ }
+
+ return true;
+}
+
uint32 Connection::GetMetric(bool is_default) {
// If this is not the default route, assign a metric based on the interface
// index. This way all non-default routes (even to the same gateway IP) end
diff --git a/connection.h b/connection.h
index 0e7b5ab..a2e4374 100644
--- a/connection.h
+++ b/connection.h
@@ -16,6 +16,7 @@
namespace shill {
class DeviceInfo;
+class IPAddress;
class Resolver;
class RoutingTable;
class RTNLHandler;
@@ -49,6 +50,9 @@
virtual void RequestRouting();
virtual void ReleaseRouting();
+ // Request a host route through this connection.
+ virtual bool RequestHostRoute(const IPAddress &destination);
+
private:
friend class ConnectionTest;
FRIEND_TEST(ConnectionTest, InitState);
diff --git a/connection_unittest.cc b/connection_unittest.cc
index 08d85b9..690c7a7 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -170,38 +170,36 @@
}
TEST_F(ConnectionTest, RouteRequest) {
- {
- ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
- kTestDeviceName0,
- device_info_.get()));
- ReplaceSingletons(connection);
- scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
- &control_,
- reinterpret_cast<EventDispatcher *>(NULL),
- reinterpret_cast<Metrics *>(NULL),
- reinterpret_cast<Manager *>(NULL),
- kTestDeviceName0,
- string(),
- kTestDeviceInterfaceIndex0));
- EXPECT_CALL(*device_info_, GetDevice(kTestDeviceInterfaceIndex0))
- .WillRepeatedly(Return(device));
- EXPECT_CALL(*device.get(), DisableReversePathFilter()).Times(1);
- connection->RequestRouting();
- connection->RequestRouting();
+ ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+ kTestDeviceName0,
+ device_info_.get()));
+ ReplaceSingletons(connection);
+ scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
+ &control_,
+ reinterpret_cast<EventDispatcher *>(NULL),
+ reinterpret_cast<Metrics *>(NULL),
+ reinterpret_cast<Manager *>(NULL),
+ kTestDeviceName0,
+ string(),
+ kTestDeviceInterfaceIndex0));
+ EXPECT_CALL(*device_info_, GetDevice(kTestDeviceInterfaceIndex0))
+ .WillRepeatedly(Return(device));
+ EXPECT_CALL(*device.get(), DisableReversePathFilter()).Times(1);
+ connection->RequestRouting();
+ connection->RequestRouting();
- // The first release should only decrement the reference counter.
- connection->ReleaseRouting();
+ // The first release should only decrement the reference counter.
+ connection->ReleaseRouting();
- // Another release will re-enable reverse-path filter.
- EXPECT_CALL(*device.get(), EnableReversePathFilter());
- EXPECT_CALL(routing_table_, FlushCache());
- connection->ReleaseRouting();
+ // Another release will re-enable reverse-path filter.
+ EXPECT_CALL(*device.get(), EnableReversePathFilter());
+ EXPECT_CALL(routing_table_, FlushCache());
+ connection->ReleaseRouting();
- // The destructor will remove the routes and addresses.
- EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
- EXPECT_CALL(*device_info_.get(),
- FlushAddresses(kTestDeviceInterfaceIndex0));
- }
+ // The destructor will remove the routes and addresses.
+ EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+ EXPECT_CALL(*device_info_.get(),
+ FlushAddresses(kTestDeviceInterfaceIndex0));
}
TEST_F(ConnectionTest, Destructor) {
@@ -217,4 +215,29 @@
}
}
+MATCHER_P2(IsIPAddress, address, prefix, "") {
+ IPAddress match_address(address);
+ match_address.set_prefix(prefix);
+ return match_address.Equals(arg);
+}
+
+TEST_F(ConnectionTest, RequestHostRoute) {
+ ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+ kTestDeviceName0,
+ device_info_.get()));
+ ReplaceSingletons(connection);
+ IPAddress address(IPAddress::kFamilyIPv4);
+ ASSERT_TRUE(address.SetAddressFromString(kIPAddress0));
+ size_t prefix_len = address.GetLength() * 8;
+ EXPECT_CALL(routing_table_, RequestRouteToHost(
+ IsIPAddress(address, prefix_len), kTestDeviceInterfaceIndex0))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(connection->RequestHostRoute(address));
+
+ // The destructor will remove the routes and addresses.
+ EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+ EXPECT_CALL(*device_info_.get(),
+ FlushAddresses(kTestDeviceInterfaceIndex0));
+}
+
} // namespace shill
diff --git a/ip_address.cc b/ip_address.cc
index 8dda572..5281d85 100644
--- a/ip_address.cc
+++ b/ip_address.cc
@@ -24,6 +24,13 @@
// static
const IPAddress::Family IPAddress::kFamilyIPv6 = AF_INET6;
+// static
+const char IPAddress::kFamilyNameUnknown[] = "Unknown";
+// static
+const char IPAddress::kFamilyNameIPv4[] = "IPv4";
+// static
+const char IPAddress::kFamilyNameIPv6[] = "IPv6";
+
IPAddress::IPAddress(Family family, const ByteString &address)
: family_(family) ,
address_(address),
@@ -42,6 +49,7 @@
IPAddress::~IPAddress() {}
+// static
size_t IPAddress::GetAddressLength(Family family) {
switch (family) {
case kFamilyIPv4:
@@ -75,6 +83,18 @@
return 0;
}
+// static
+string IPAddress::GetAddressFamilyName(Family family) {
+ switch (family) {
+ case kFamilyIPv4:
+ return kFamilyNameIPv4;
+ case kFamilyIPv6:
+ return kFamilyNameIPv6;
+ default:
+ return kFamilyNameUnknown;
+ }
+}
+
bool IPAddress::SetAddressFromString(const string &address_string) {
size_t address_length = GetAddressLength(family_);
diff --git a/ip_address.h b/ip_address.h
index f13c303..bca47ed 100644
--- a/ip_address.h
+++ b/ip_address.h
@@ -17,6 +17,9 @@
static const Family kFamilyUnknown;
static const Family kFamilyIPv4;
static const Family kFamilyIPv6;
+ static const char kFamilyNameUnknown[];
+ static const char kFamilyNameIPv4[];
+ static const char kFamilyNameIPv6[];
explicit IPAddress(Family family);
IPAddress(Family family, const ByteString &address);
@@ -43,6 +46,9 @@
// example, returns 24 for an IPv4 mask 255.255.255.0.
static int GetPrefixLengthFromMask(Family family, const std::string &mask);
+ // Returns the name of an address family.
+ static std::string GetAddressFamilyName(Family family);
+
// Getters and Setters
Family family() const { return family_; }
const ByteString &address() const { return address_; }
diff --git a/mock_routing_table.h b/mock_routing_table.h
index ec65029..5fce84b 100644
--- a/mock_routing_table.h
+++ b/mock_routing_table.h
@@ -31,6 +31,8 @@
MOCK_METHOD0(FlushCache, bool());
MOCK_METHOD1(ResetTable, void(int interface_index));
MOCK_METHOD2(SetDefaultMetric, void(int interface_index, uint32 metric));
+ MOCK_METHOD2(RequestRouteToHost, bool(const IPAddress &addresss,
+ int interface_index));
private:
DISALLOW_COPY_AND_ASSIGN(MockRoutingTable);
diff --git a/routing_table.cc b/routing_table.cc
index 88db886..4986f29 100644
--- a/routing_table.cc
+++ b/routing_table.cc
@@ -48,7 +48,8 @@
RoutingTable::RoutingTable()
: route_callback_(NewCallback(this, &RoutingTable::RouteMsgHandler)),
- route_listener_(NULL) {
+ route_listener_(NULL),
+ rtnl_handler_(RTNLHandler::GetInstance()) {
VLOG(2) << __func__;
}
@@ -63,8 +64,7 @@
route_listener_.reset(
new RTNLListener(RTNLHandler::kRequestRoute, route_callback_.get()));
- RTNLHandler::GetInstance()->RequestDump(
- RTNLHandler::kRequestRoute);
+ rtnl_handler_->RequestDump(RTNLHandler::kRequestRoute);
}
void RoutingTable::Stop() {
@@ -75,10 +75,11 @@
bool RoutingTable::AddRoute(int interface_index,
const RoutingTableEntry &entry) {
- VLOG(2) << __func__ << " "
- << "index " << interface_index << " "
- << "gateway " << entry.gateway.ToString() << " "
- << "metric " << entry.metric;
+ VLOG(2) << __func__ << ": "
+ << "destination " << entry.dst.ToString()
+ << " index " << interface_index
+ << " gateway " << entry.gateway.ToString()
+ << " metric " << entry.metric;
CHECK(!entry.from_rtnl);
if (!ApplyRoute(interface_index,
@@ -105,7 +106,8 @@
bool RoutingTable::GetDefaultRouteInternal(int interface_index,
IPAddress::Family family,
RoutingTableEntry **entry) {
- VLOG(2) << __func__ << " index " << interface_index << " family " << family;
+ VLOG(2) << __func__ << " index " << interface_index
+ << " family " << IPAddress::GetAddressFamilyName(family);
base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
tables_.find(interface_index);
@@ -120,9 +122,9 @@
for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
if (nent->dst.IsDefault() && nent->dst.family() == family) {
*entry = &(*nent);
- VLOG(2) << __func__ << " found "
- << "gateway " << nent->gateway.ToString() << " "
- << "metric " << nent->metric;
+ VLOG(2) << __func__ << ": found"
+ << " gateway " << nent->gateway.ToString()
+ << " metric " << nent->metric;
return true;
}
}
@@ -195,8 +197,7 @@
}
void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
- VLOG(2) << __func__ << " "
- << "index " << interface_index << " metric " << metric;
+ VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
RoutingTableEntry *entry;
if (GetDefaultRouteInternal(
@@ -212,54 +213,100 @@
}
}
-void RoutingTable::RouteMsgHandler(const RTNLMessage &msg) {
- if (msg.type() != RTNLMessage::kTypeRoute ||
- msg.family() == IPAddress::kFamilyUnknown ||
- !msg.HasAttribute(RTA_OIF)) {
- return;
+// static
+bool RoutingTable::ParseRoutingTableMessage(const RTNLMessage &message,
+ int *interface_index,
+ RoutingTableEntry *entry) {
+ if (message.type() != RTNLMessage::kTypeRoute ||
+ message.family() == IPAddress::kFamilyUnknown ||
+ !message.HasAttribute(RTA_OIF)) {
+ return false;
}
- const RTNLMessage::RouteStatus &route_status = msg.route_status();
+ const RTNLMessage::RouteStatus &route_status = message.route_status();
if (route_status.type != RTN_UNICAST ||
- route_status.protocol != RTPROT_BOOT ||
route_status.table != RT_TABLE_MAIN) {
- return;
+ return false;
}
- uint32 interface_index = 0;
- if (!msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index)) {
- return;
+ uint32 interface_index_u32 = 0;
+ if (!message.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index_u32)) {
+ return false;
}
+ *interface_index = interface_index_u32;
uint32 metric = 0;
- if (msg.HasAttribute(RTA_PRIORITY)) {
- msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
+ if (message.HasAttribute(RTA_PRIORITY)) {
+ message.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
}
- IPAddress default_addr(msg.family());
+ IPAddress default_addr(message.family());
default_addr.SetAddressToDefault();
ByteString dst_bytes(default_addr.address());
- if (msg.HasAttribute(RTA_DST)) {
- dst_bytes = msg.GetAttribute(RTA_DST);
+ if (message.HasAttribute(RTA_DST)) {
+ dst_bytes = message.GetAttribute(RTA_DST);
}
ByteString src_bytes(default_addr.address());
- if (msg.HasAttribute(RTA_SRC)) {
- src_bytes = msg.GetAttribute(RTA_SRC);
+ if (message.HasAttribute(RTA_SRC)) {
+ src_bytes = message.GetAttribute(RTA_SRC);
}
ByteString gateway_bytes(default_addr.address());
- if (msg.HasAttribute(RTA_GATEWAY)) {
- gateway_bytes = msg.GetAttribute(RTA_GATEWAY);
+ if (message.HasAttribute(RTA_GATEWAY)) {
+ gateway_bytes = message.GetAttribute(RTA_GATEWAY);
}
- RoutingTableEntry entry(
- IPAddress(msg.family(), dst_bytes, route_status.dst_prefix),
- IPAddress(msg.family(), src_bytes, route_status.src_prefix),
- IPAddress(msg.family(), gateway_bytes),
- metric,
- route_status.scope,
- true);
+ entry->dst = IPAddress(message.family(), dst_bytes, route_status.dst_prefix);
+ entry->src = IPAddress(message.family(), src_bytes, route_status.src_prefix);
+ entry->gateway = IPAddress(message.family(), gateway_bytes);
+ entry->metric = metric;
+ entry->scope = route_status.scope;
+ entry->from_rtnl = true;
+
+ return true;
+}
+
+void RoutingTable::RouteMsgHandler(const RTNLMessage &message) {
+ int interface_index;
+ RoutingTableEntry entry;
+
+ if (!ParseRoutingTableMessage(message, &interface_index, &entry)) {
+ return;
+ }
+
+ if (!route_query_sequences_.empty() &&
+ message.route_status().protocol == RTPROT_UNSPEC) {
+ VLOG(3) << __func__ << ": Message seq: " << message.seq()
+ << " mode " << message.mode()
+ << ", next query seq: " << route_query_sequences_.front();
+
+ // Purge queries that have expired (sequence number of this message is
+ // greater than that of the head of the route query sequence). Do the
+ // math in a way that's roll-over independent.
+ while (route_query_sequences_.front() - message.seq() > kuint32max / 2) {
+ LOG(ERROR) << __func__ << ": Purging un-replied route request sequence "
+ << route_query_sequences_.front()
+ << " (< " << message.seq() << ")";
+ route_query_sequences_.pop();
+ if (route_query_sequences_.empty())
+ return;
+ }
+
+ if (route_query_sequences_.front() == message.seq()) {
+ VLOG(2) << __func__ << ": Adding host route to " << entry.dst.ToString();
+ route_query_sequences_.pop();
+ RoutingTableEntry add_entry(entry);
+ add_entry.from_rtnl = false;
+ AddRoute(interface_index, add_entry);
+ }
+ return;
+ } else if (message.route_status().protocol != RTPROT_BOOT) {
+ // Responses to route queries come back with a protocol of
+ // RTPROT_UNSPEC. Otherwise, normal route updates that we are
+ // interested in come with a protocol of RTPROT_BOOT.
+ return;
+ }
vector<RoutingTableEntry> &table = tables_[interface_index];
vector<RoutingTableEntry>::iterator nent;
@@ -268,10 +315,10 @@
nent->src.Equals(entry.src) &&
nent->gateway.Equals(entry.gateway) &&
nent->scope == entry.scope) {
- if (msg.mode() == RTNLMessage::kModeDelete &&
+ if (message.mode() == RTNLMessage::kModeDelete &&
nent->metric == entry.metric) {
table.erase(nent);
- } else if (msg.mode() == RTNLMessage::kModeAdd) {
+ } else if (message.mode() == RTNLMessage::kModeAdd) {
nent->from_rtnl = true;
nent->metric = entry.metric;
}
@@ -279,11 +326,12 @@
}
}
- if (msg.mode() == RTNLMessage::kModeAdd) {
- VLOG(2) << __func__ << " adding "
- << "index " << interface_index
- << "gateway " << entry.gateway.ToString() << " "
- << "metric " << entry.metric;
+ if (message.mode() == RTNLMessage::kModeAdd) {
+ VLOG(2) << __func__ << " adding"
+ << " destination " << entry.dst.ToString()
+ << " index " << interface_index
+ << " gateway " << entry.gateway.ToString()
+ << " metric " << entry.metric;
table.push_back(entry);
}
}
@@ -296,7 +344,7 @@
__func__, entry.dst.ToString().c_str(),
interface_index, mode, flags);
- RTNLMessage msg(
+ RTNLMessage message(
RTNLMessage::kTypeRoute,
mode,
NLM_F_REQUEST | flags,
@@ -305,7 +353,7 @@
0,
entry.dst.family());
- msg.set_route_status(RTNLMessage::RouteStatus(
+ message.set_route_status(RTNLMessage::RouteStatus(
entry.dst.prefix(),
entry.src.prefix(),
RT_TABLE_MAIN,
@@ -314,17 +362,19 @@
RTN_UNICAST,
0));
- msg.SetAttribute(RTA_DST, entry.dst.address());
+ message.SetAttribute(RTA_DST, entry.dst.address());
if (!entry.src.IsDefault()) {
- msg.SetAttribute(RTA_SRC, entry.src.address());
+ message.SetAttribute(RTA_SRC, entry.src.address());
}
if (!entry.gateway.IsDefault()) {
- msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
+ message.SetAttribute(RTA_GATEWAY, entry.gateway.address());
}
- msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
- msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
+ message.SetAttribute(RTA_PRIORITY,
+ ByteString::CreateFromCPUUInt32(entry.metric));
+ message.SetAttribute(RTA_OIF,
+ ByteString::CreateFromCPUUInt32(interface_index));
- return RTNLHandler::GetInstance()->SendMessage(&msg);
+ return rtnl_handler_->SendMessage(&message);
}
// Somewhat surprisingly, the kernel allows you to create multiple routes
@@ -336,8 +386,7 @@
void RoutingTable::ReplaceMetric(uint32 interface_index,
RoutingTableEntry *entry,
uint32 metric) {
- VLOG(2) << __func__ << " "
- << "index " << interface_index << " metric " << metric;
+ VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
RoutingTableEntry new_entry = *entry;
new_entry.metric = metric;
// First create the route at the new metric.
@@ -366,4 +415,33 @@
return ret;
}
+bool RoutingTable::RequestRouteToHost(const IPAddress &address,
+ int interface_index) {
+ RTNLMessage message(
+ RTNLMessage::kTypeRoute,
+ RTNLMessage::kModeQuery,
+ NLM_F_REQUEST,
+ 0,
+ 0,
+ interface_index,
+ address.family());
+
+ RTNLMessage::RouteStatus status;
+ status.dst_prefix = address.prefix();
+ message.set_route_status(status);
+ message.SetAttribute(RTA_DST, address.address());
+ message.SetAttribute(RTA_OIF,
+ ByteString::CreateFromCPUUInt32(interface_index));
+
+ if (!rtnl_handler_->SendMessage(&message)) {
+ return false;
+ }
+
+ // Save the sequence number of the request so we can create a route for
+ // this host when we get a reply.
+ route_query_sequences_.push(message.seq());
+
+ return true;
+}
+
} // namespace shill
diff --git a/routing_table.h b/routing_table.h
index 436acc0..c015379 100644
--- a/routing_table.h
+++ b/routing_table.h
@@ -5,6 +5,7 @@
#ifndef SHILL_ROUTING_TABLE_
#define SHILL_ROUTING_TABLE_
+#include <queue>
#include <string>
#include <vector>
@@ -21,7 +22,9 @@
namespace shill {
class RoutingTableEntry;
+class RTNLHandler;
class RTNLListener;
+class RTNLMessage;
// This singleton maintains an in-process copy of the routing table on
// a per-interface basis. It offers the ability for other modules to
@@ -63,6 +66,11 @@
// Set the metric (priority) on existing default routes for an interface.
virtual void SetDefaultMetric(int interface_index, uint32 metric);
+ // Get the default route to |destination| through |interface_index| and
+ // create a host route to that destination.
+ virtual bool RequestRouteToHost(const IPAddress &destination,
+ int interface_index);
+
protected:
RoutingTable();
@@ -70,6 +78,10 @@
friend struct base::DefaultLazyInstanceTraits<RoutingTable>;
friend class RoutingTableTest;
+
+ static bool ParseRoutingTableMessage(const RTNLMessage &message,
+ int *interface_index,
+ RoutingTableEntry *entry);
void RouteMsgHandler(const RTNLMessage &msg);
bool ApplyRoute(uint32 interface_index,
const RoutingTableEntry &entry,
@@ -93,6 +105,10 @@
scoped_ptr<Callback1<const RTNLMessage &>::Type> route_callback_;
scoped_ptr<RTNLListener> route_listener_;
+ std::queue<uint32> route_query_sequences_;
+
+ // Cache singleton pointer for performance and test purposes.
+ RTNLHandler *rtnl_handler_;
DISALLOW_COPY_AND_ASSIGN(RoutingTable);
};
diff --git a/routing_table_unittest.cc b/routing_table_unittest.cc
index c1d22e7..6e36cb5 100644
--- a/routing_table_unittest.cc
+++ b/routing_table_unittest.cc
@@ -12,14 +12,16 @@
#include "shill/byte_string.h"
#include "shill/mock_control.h"
-#include "shill/mock_sockets.h"
+#include "shill/mock_rtnl_handler.h"
#include "shill/routing_table.h"
#include "shill/routing_table_entry.h"
#include "shill/rtnl_handler.h"
#include "shill/rtnl_message.h"
using testing::_;
+using testing::Invoke;
using testing::Return;
+using testing::StrictMock;
using testing::Test;
namespace shill {
@@ -37,20 +39,41 @@
public:
RoutingTableTest() : routing_table_(RoutingTable::GetInstance()) {}
- base::hash_map<int, std::vector<RoutingTableEntry> > *GetRoutingTables() {
- return &routing_table_->tables_;
+ virtual void SetUp() {
+ routing_table_->rtnl_handler_ = &rtnl_handler_;
+ ON_CALL(rtnl_handler_, SendMessage(_)).WillByDefault(Return(true));
}
- void SendRouteMsg(RTNLMessage::Mode mode,
- uint32 interface_index,
- const RoutingTableEntry &entry);
-
virtual void TearDown() {
RTNLHandler::GetInstance()->Stop();
}
+ base::hash_map<int, std::vector<RoutingTableEntry> > *GetRoutingTables() {
+ return &routing_table_->tables_;
+ }
+
+ std::queue<uint32> *GetQuerySequences() {
+ return &routing_table_->route_query_sequences_;
+ }
+
+ void SendRouteEntry(RTNLMessage::Mode mode,
+ uint32 interface_index,
+ const RoutingTableEntry &entry);
+
+ void SendRouteEntryWithSeqAndProto(RTNLMessage::Mode mode,
+ uint32 interface_index,
+ const RoutingTableEntry &entry,
+ uint32 seq,
+ unsigned char proto);
+
+ void SendRouteMessage(const RTNLMessage &msg);
+
+ bool SetSequenceForMessage(RTNLMessage *message) {
+ message->set_seq(RoutingTableTest::kTestRequestSeq);
+ return true;
+ }
+
protected:
- static const int kTestSocket;
static const uint32 kTestDeviceIndex0;
static const uint32 kTestDeviceIndex1;
static const char kTestDeviceName0[];
@@ -59,18 +82,17 @@
static const char kTestForeignNetGateway4[];
static const char kTestForeignNetAddress6[];
static const char kTestForeignNetGateway6[];
+ static const char kTestGatewayAddress4[];
static const char kTestNetAddress0[];
static const char kTestNetAddress1[];
+ static const char kTestRemoteAddress4[];
+ static const uint32 kTestRequestSeq;
- void StartRTNLHandler();
- void StopRTNLHandler();
-
- MockSockets sockets_;
RoutingTable *routing_table_;
TestEventDispatcher dispatcher_;
+ StrictMock<MockRTNLHandler> rtnl_handler_;
};
-const int RoutingTableTest::kTestSocket = 123;
const uint32 RoutingTableTest::kTestDeviceIndex0 = 12345;
const uint32 RoutingTableTest::kTestDeviceIndex1 = 67890;
const char RoutingTableTest::kTestDeviceName0[] = "test-device0";
@@ -79,58 +101,57 @@
const char RoutingTableTest::kTestForeignNetGateway4[] = "192.168.2.1";
const char RoutingTableTest::kTestForeignNetAddress6[] = "2000::/3";
const char RoutingTableTest::kTestForeignNetGateway6[] = "fe80:::::1";
+const char RoutingTableTest::kTestGatewayAddress4[] = "192.168.2.254";
const char RoutingTableTest::kTestNetAddress0[] = "192.168.1.1";
const char RoutingTableTest::kTestNetAddress1[] = "192.168.1.2";
-
+const char RoutingTableTest::kTestRemoteAddress4[] = "192.168.2.254";
+const uint32 RoutingTableTest::kTestRequestSeq = 456;
MATCHER_P4(IsRoutingPacket, mode, index, entry, flags, "") {
- // NB: Matchers don't get passed multiple arguments, so we can
- // get the address of a Send(), its length, but not both.
- // We have to punt and assume the length is correct -- which
- // should already be tested in rtnl_message_unittest.
- struct nlmsghdr hdr;
- memcpy(&hdr, arg, sizeof(hdr));
-
- RTNLMessage msg;
- if (!msg.Decode(ByteString(reinterpret_cast<const unsigned char *>(arg),
- hdr.nlmsg_len))) {
- return false;
- }
-
- const RTNLMessage::RouteStatus &status = msg.route_status();
+ const RTNLMessage::RouteStatus &status = arg->route_status();
uint32 oif;
uint32 priority;
return
- msg.type() == RTNLMessage::kTypeRoute &&
- msg.family() == entry.gateway.family() &&
- msg.flags() == (NLM_F_REQUEST | flags) &&
- status.table == RT_TABLE_MAIN &&
- status.protocol == RTPROT_BOOT &&
- status.scope == entry.scope &&
- status.type == RTN_UNICAST &&
- msg.HasAttribute(RTA_DST) &&
- IPAddress(msg.family(),
- msg.GetAttribute(RTA_DST)).Equals(entry.dst) &&
- !msg.HasAttribute(RTA_SRC) &&
- msg.HasAttribute(RTA_GATEWAY) &&
- IPAddress(msg.family(),
- msg.GetAttribute(RTA_GATEWAY)).Equals(entry.gateway) &&
- msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
- oif == index &&
- msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&priority) &&
- priority == entry.metric;
+ arg->type() == RTNLMessage::kTypeRoute &&
+ arg->family() == entry.gateway.family() &&
+ arg->flags() == (NLM_F_REQUEST | flags) &&
+ status.table == RT_TABLE_MAIN &&
+ status.protocol == RTPROT_BOOT &&
+ status.scope == entry.scope &&
+ status.type == RTN_UNICAST &&
+ arg->HasAttribute(RTA_DST) &&
+ IPAddress(arg->family(),
+ arg->GetAttribute(RTA_DST),
+ status.dst_prefix).Equals(entry.dst) &&
+ !arg->HasAttribute(RTA_SRC) &&
+ arg->HasAttribute(RTA_GATEWAY) &&
+ IPAddress(arg->family(),
+ arg->GetAttribute(RTA_GATEWAY)).Equals(entry.gateway) &&
+ arg->GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
+ oif == index &&
+ arg->GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&priority) &&
+ priority == entry.metric;
}
-void RoutingTableTest::SendRouteMsg(RTNLMessage::Mode mode,
- uint32 interface_index,
- const RoutingTableEntry &entry) {
+void RoutingTableTest::SendRouteEntry(RTNLMessage::Mode mode,
+ uint32 interface_index,
+ const RoutingTableEntry &entry) {
+ SendRouteEntryWithSeqAndProto(mode, interface_index, entry, 0, RTPROT_BOOT);
+}
+
+void RoutingTableTest::SendRouteEntryWithSeqAndProto(
+ RTNLMessage::Mode mode,
+ uint32 interface_index,
+ const RoutingTableEntry &entry,
+ uint32 seq,
+ unsigned char proto) {
RTNLMessage msg(
RTNLMessage::kTypeRoute,
mode,
0,
- 0,
+ seq,
0,
0,
entry.dst.family());
@@ -139,7 +160,7 @@
entry.dst.prefix(),
entry.src.prefix(),
RT_TABLE_MAIN,
- RTPROT_BOOT,
+ proto,
entry.scope,
RTN_UNICAST,
0));
@@ -154,31 +175,19 @@
msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
- ByteString msgdata = msg.Encode();
- EXPECT_NE(0, msgdata.GetLength());
-
- InputData data(msgdata.GetData(), msgdata.GetLength());
- RTNLHandler::GetInstance()->ParseRTNL(&data);
+ routing_table_->RouteMsgHandler(msg);
}
-void RoutingTableTest::StartRTNLHandler() {
- EXPECT_CALL(sockets_, Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))
- .WillOnce(Return(kTestSocket));
- EXPECT_CALL(sockets_, Bind(kTestSocket, _, sizeof(sockaddr_nl)))
- .WillOnce(Return(0));
- RTNLHandler::GetInstance()->Start(&dispatcher_, &sockets_);
+void RoutingTableTest::SendRouteMessage(const RTNLMessage &msg) {
+ routing_table_->RouteMsgHandler(msg);
}
-void RoutingTableTest::StopRTNLHandler() {
- EXPECT_CALL(sockets_, Close(_)).WillOnce(Return(0));
- RTNLHandler::GetInstance()->Stop();
+TEST_F(RoutingTableTest, Start) {
+ EXPECT_CALL(rtnl_handler_, RequestDump(RTNLHandler::kRequestRoute));
+ routing_table_->Start();
}
TEST_F(RoutingTableTest, RouteAddDelete) {
- EXPECT_CALL(sockets_, Send(kTestSocket, _, _, 0));
- StartRTNLHandler();
- routing_table_->Start();
-
// Expect the tables to be empty by default.
EXPECT_EQ(0, GetRoutingTables()->size());
@@ -197,9 +206,9 @@
RT_SCOPE_UNIVERSE,
true);
// Add a single entry.
- SendRouteMsg(RTNLMessage::kModeAdd,
- kTestDeviceIndex0,
- entry0);
+ SendRouteEntry(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry0);
base::hash_map<int, std::vector<RoutingTableEntry> > *tables =
GetRoutingTables();
@@ -213,9 +222,9 @@
EXPECT_TRUE(entry0.Equals(test_entry));
// Add a second entry for a different interface.
- SendRouteMsg(RTNLMessage::kModeAdd,
- kTestDeviceIndex1,
- entry0);
+ SendRouteEntry(RTNLMessage::kModeAdd,
+ kTestDeviceIndex1,
+ entry0);
// We should have two tables, which should have a single entry each.
EXPECT_EQ(2, tables->size());
@@ -237,9 +246,9 @@
true);
// Add a second gateway route to the second interface.
- SendRouteMsg(RTNLMessage::kModeAdd,
- kTestDeviceIndex1,
- entry1);
+ SendRouteEntry(RTNLMessage::kModeAdd,
+ kTestDeviceIndex1,
+ entry1);
// We should have two tables, one of which has a single entry, the other has
// two.
@@ -251,9 +260,9 @@
EXPECT_TRUE(entry1.Equals(test_entry));
// Remove the first gateway route from the second interface.
- SendRouteMsg(RTNLMessage::kModeDelete,
- kTestDeviceIndex1,
- entry0);
+ SendRouteEntry(RTNLMessage::kModeDelete,
+ kTestDeviceIndex1,
+ entry0);
// We should be back to having one route per table.
EXPECT_EQ(2, tables->size());
@@ -266,9 +275,9 @@
// Send a duplicate of the second gatway route message, changing the metric.
RoutingTableEntry entry2(entry1);
entry2.metric++;
- SendRouteMsg(RTNLMessage::kModeAdd,
- kTestDeviceIndex1,
- entry2);
+ SendRouteEntry(RTNLMessage::kModeAdd,
+ kTestDeviceIndex1,
+ entry2);
// Routing table size shouldn't change, but the new metric should match.
EXPECT_EQ(1, (*tables)[kTestDeviceIndex1].size());
@@ -287,9 +296,9 @@
&test_entry));
// Remove last entry from an existing interface and test that we now fail.
- SendRouteMsg(RTNLMessage::kModeDelete,
- kTestDeviceIndex1,
- entry2);
+ SendRouteEntry(RTNLMessage::kModeDelete,
+ kTestDeviceIndex1,
+ entry2);
EXPECT_FALSE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
IPAddress::kFamilyIPv4,
@@ -304,14 +313,11 @@
properties.address = kTestNetAddress1;
ipconfig->UpdateProperties(properties, true);
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeAdd,
- kTestDeviceIndex1,
- entry0,
- NLM_F_CREATE | NLM_F_EXCL),
- _,
- 0));
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+ kTestDeviceIndex1,
+ entry0,
+ NLM_F_CREATE | NLM_F_EXCL)));
EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
ipconfig,
metric));
@@ -330,22 +336,16 @@
// then it should delete the old entry.
RoutingTableEntry entry4(entry3);
entry4.metric += 10;
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeAdd,
- kTestDeviceIndex1,
- entry4,
- NLM_F_CREATE | NLM_F_REPLACE),
- _,
- 0));
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeDelete,
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+ kTestDeviceIndex1,
+ entry4,
+ NLM_F_CREATE | NLM_F_REPLACE)));
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
kTestDeviceIndex1,
entry3,
- 0),
- _,
- 0));
+ 0)));
EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
ipconfig,
entry4.metric));
@@ -362,22 +362,16 @@
// operation should occur.
RoutingTableEntry entry5(entry4);
entry5.metric += 10;
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeAdd,
- kTestDeviceIndex0,
- entry5,
- NLM_F_CREATE | NLM_F_REPLACE),
- _,
- 0));
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeDelete,
- kTestDeviceIndex0,
- entry0,
- 0),
- _,
- 0));
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry5,
+ NLM_F_CREATE | NLM_F_REPLACE)));
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
+ kTestDeviceIndex0,
+ entry0,
+ 0)));
routing_table_->SetDefaultMetric(kTestDeviceIndex0, entry5.metric);
// Furthermore, the routing table should reflect the change in the metric
// for the default route for the interface.
@@ -388,27 +382,122 @@
EXPECT_EQ(entry5.metric, default_route.metric);
// Ask to flush table0. We should see a delete message sent.
- EXPECT_CALL(sockets_,
- Send(kTestSocket,
- IsRoutingPacket(RTNLMessage::kModeDelete,
- kTestDeviceIndex0,
- entry5,
- 0),
- _,
- 0));
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
+ kTestDeviceIndex0,
+ entry5,
+ 0)));
routing_table_->FlushRoutes(kTestDeviceIndex0);
EXPECT_EQ(0, (*tables)[kTestDeviceIndex0].size());
// Test that the routing table size returns to zero.
- SendRouteMsg(RTNLMessage::kModeAdd,
- kTestDeviceIndex0,
- entry5);
+ SendRouteEntry(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry5);
EXPECT_EQ(1, GetRoutingTables()->size());
routing_table_->ResetTable(kTestDeviceIndex0);
EXPECT_EQ(0, GetRoutingTables()->size());
routing_table_->Stop();
- StopRTNLHandler();
+}
+
+MATCHER_P2(IsRoutingQuery, destination, index, "") {
+ const RTNLMessage::RouteStatus &status = arg->route_status();
+
+ uint32 oif;
+
+ return
+ arg->type() == RTNLMessage::kTypeRoute &&
+ arg->family() == destination.family() &&
+ arg->flags() == NLM_F_REQUEST &&
+ status.table == 0 &&
+ status.protocol == 0 &&
+ status.scope == 0 &&
+ status.type == 0 &&
+ arg->HasAttribute(RTA_DST) &&
+ IPAddress(arg->family(),
+ arg->GetAttribute(RTA_DST),
+ status.dst_prefix).Equals(destination) &&
+ !arg->HasAttribute(RTA_SRC) &&
+ !arg->HasAttribute(RTA_GATEWAY) &&
+ arg->GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
+ oif == index &&
+ !arg->HasAttribute(RTA_PRIORITY);
+
+ return false;
+}
+
+TEST_F(RoutingTableTest, RequestHostRoute) {
+ IPAddress destination_address(IPAddress::kFamilyIPv4);
+ destination_address.SetAddressFromString(kTestRemoteAddress4);
+ destination_address.set_prefix(24);
+
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingQuery(destination_address,
+ kTestDeviceIndex0)))
+ .WillOnce(Invoke(this, &RoutingTableTest::SetSequenceForMessage));
+ EXPECT_TRUE(routing_table_->RequestRouteToHost(destination_address,
+ kTestDeviceIndex0));
+
+ IPAddress gateway_address(IPAddress::kFamilyIPv4);
+ gateway_address.SetAddressFromString(kTestGatewayAddress4);
+
+ IPAddress local_address(IPAddress::kFamilyIPv4);
+ local_address.SetAddressFromString(kTestDeviceNetAddress4);
+
+ const int kMetric = 10;
+ RoutingTableEntry entry(destination_address,
+ local_address,
+ gateway_address,
+ kMetric,
+ RT_SCOPE_UNIVERSE,
+ true);
+
+ EXPECT_CALL(rtnl_handler_,
+ SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry,
+ NLM_F_CREATE | NLM_F_EXCL)));
+ SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry,
+ kTestRequestSeq,
+ RTPROT_UNSPEC);
+}
+
+TEST_F(RoutingTableTest, RequestHostRouteBadSequence) {
+ IPAddress destination_address(IPAddress::kFamilyIPv4);
+ destination_address.SetAddressFromString(kTestRemoteAddress4);
+ EXPECT_CALL(rtnl_handler_, SendMessage(_))
+ .WillOnce(Invoke(this, &RoutingTableTest::SetSequenceForMessage));
+ EXPECT_TRUE(routing_table_->RequestRouteToHost(destination_address,
+ kTestDeviceIndex0));
+ EXPECT_FALSE(GetQuerySequences()->empty());
+
+ RoutingTableEntry entry(destination_address,
+ destination_address,
+ destination_address,
+ 0,
+ RT_SCOPE_UNIVERSE,
+ true);
+
+ // Try a sequence arriving before the one RoutingTable is looking for.
+ // This should be a no-op.
+ SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry,
+ kTestRequestSeq-1,
+ RTPROT_UNSPEC);
+ EXPECT_FALSE(GetQuerySequences()->empty());
+
+ // Try a sequence arriving after the one RoutingTable is looking for.
+ // This should cause the request to be purged.
+ SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+ kTestDeviceIndex0,
+ entry,
+ kTestRequestSeq+1,
+ RTPROT_UNSPEC);
+ EXPECT_TRUE(GetQuerySequences()->empty());
}
} // namespace shill
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
index 66639f5..0c6cee8 100644
--- a/rtnl_handler.cc
+++ b/rtnl_handler.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -165,7 +165,7 @@
}
}
-void RTNLHandler::NextRequest(uint32_t seq) {
+void RTNLHandler::NextRequest(uint32 seq) {
int flag = 0;
RTNLMessage::Type type;
diff --git a/rtnl_handler.h b/rtnl_handler.h
index 6b88f0b..d9d815b 100644
--- a/rtnl_handler.h
+++ b/rtnl_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -118,7 +118,7 @@
// Dispatches an rtnl message to all listeners
void DispatchEvent(int type, const RTNLMessage &msg);
// Send the next table-dump request to the kernel
- void NextRequest(uint32_t seq);
+ void NextRequest(uint32 seq);
// Parse an incoming rtnl message from the kernel
void ParseRTNL(InputData *data);
@@ -131,9 +131,9 @@
bool in_request_;
int rtnl_socket_;
- uint32_t request_flags_;
- uint32_t request_sequence_;
- uint32_t last_dump_sequence_;
+ uint32 request_flags_;
+ uint32 request_sequence_;
+ uint32 last_dump_sequence_;
std::vector<RTNLListener *> listeners_;
scoped_ptr<Callback1<InputData *>::Type> rtnl_callback_;
diff --git a/rtnl_message.cc b/rtnl_message.cc
index bf9e8d0..6b4c6a0 100644
--- a/rtnl_message.cc
+++ b/rtnl_message.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -50,8 +50,7 @@
bool RTNLMessage::Decode(const ByteString &msg) {
bool ret = DecodeInternal(msg);
if (!ret) {
- mode_ = kModeUnknown;
- type_ = kTypeUnknown;
+ Reset();
}
return ret;
}
@@ -193,7 +192,7 @@
return true;
}
-ByteString RTNLMessage::Encode() {
+ByteString RTNLMessage::Encode() const {
if (type_ != kTypeLink &&
type_ != kTypeAddress &&
type_ != kTypeRoute) {
@@ -204,7 +203,6 @@
hdr.hdr.nlmsg_flags = flags_;
hdr.hdr.nlmsg_seq = seq_;
hdr.hdr.nlmsg_pid = pid_;
- hdr.hdr.nlmsg_seq = 0;
if (mode_ == kModeGet) {
if (type_ == kTypeLink) {
@@ -213,6 +211,9 @@
hdr.hdr.nlmsg_type = RTM_GETADDR;
} else if (type_ == kTypeRoute) {
hdr.hdr.nlmsg_type = RTM_GETROUTE;
+ } else {
+ NOTIMPLEMENTED();
+ return ByteString();
}
hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
@@ -220,15 +221,21 @@
} else {
switch (type_) {
case kTypeLink:
- EncodeLink(&hdr);
+ if (!EncodeLink(&hdr)) {
+ return ByteString();
+ }
break;
case kTypeAddress:
- EncodeAddress(&hdr);
+ if (!EncodeAddress(&hdr)) {
+ return ByteString();
+ }
break;
case kTypeRoute:
- EncodeRoute(&hdr);
+ if (!EncodeRoute(&hdr)) {
+ return ByteString();
+ }
break;
default:
@@ -239,7 +246,7 @@
size_t header_length = hdr.hdr.nlmsg_len;
ByteString attributes;
- base::hash_map<uint16, ByteString>::iterator attr;
+ base::hash_map<uint16, ByteString>::const_iterator attr;
for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
size_t len = RTA_LENGTH(attr->second.GetLength());
hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
@@ -261,28 +268,69 @@
return packet;
}
-void RTNLMessage::EncodeLink(RTNLHeader *hdr) {
- hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWLINK : RTM_DELLINK;
+bool RTNLMessage::EncodeLink(RTNLHeader *hdr) const {
+ switch (mode_) {
+ case kModeAdd:
+ hdr->hdr.nlmsg_type = RTM_NEWLINK;
+ break;
+ case kModeDelete:
+ hdr->hdr.nlmsg_type = RTM_DELLINK;
+ break;
+ case kModeQuery:
+ hdr->hdr.nlmsg_type = RTM_GETLINK;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ return false;
+ }
hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
hdr->ifi.ifi_family = family_;
hdr->ifi.ifi_index = interface_index_;
hdr->ifi.ifi_type = link_status_.type;
hdr->ifi.ifi_flags = link_status_.flags;
hdr->ifi.ifi_change = link_status_.change;
+ return true;
}
-void RTNLMessage::EncodeAddress(RTNLHeader *hdr) {
- hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWADDR : RTM_DELADDR;
+bool RTNLMessage::EncodeAddress(RTNLHeader *hdr) const {
+ switch (mode_) {
+ case kModeAdd:
+ hdr->hdr.nlmsg_type = RTM_NEWADDR;
+ break;
+ case kModeDelete:
+ hdr->hdr.nlmsg_type = RTM_DELADDR;
+ break;
+ case kModeQuery:
+ hdr->hdr.nlmsg_type = RTM_GETADDR;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ return false;
+ }
hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
hdr->ifa.ifa_family = family_;
hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
hdr->ifa.ifa_flags = address_status_.flags;
hdr->ifa.ifa_scope = address_status_.scope;
hdr->ifa.ifa_index = interface_index_;
+ return true;
}
-void RTNLMessage::EncodeRoute(RTNLHeader *hdr) {
- hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWROUTE : RTM_DELROUTE;
+bool RTNLMessage::EncodeRoute(RTNLHeader *hdr) const {
+ switch (mode_) {
+ case kModeAdd:
+ hdr->hdr.nlmsg_type = RTM_NEWROUTE;
+ break;
+ case kModeDelete:
+ hdr->hdr.nlmsg_type = RTM_DELROUTE;
+ break;
+ case kModeQuery:
+ hdr->hdr.nlmsg_type = RTM_GETROUTE;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ return false;
+ }
hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
hdr->rtm.rtm_family = family_;
hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
@@ -292,6 +340,20 @@
hdr->rtm.rtm_scope = route_status_.scope;
hdr->rtm.rtm_type = route_status_.type;
hdr->rtm.rtm_flags = route_status_.flags;
+ return true;
+}
+
+void RTNLMessage::Reset() {
+ mode_ = kModeUnknown;
+ type_ = kTypeUnknown;
+ flags_ = 0;
+ seq_ = 0;
+ interface_index_ = 0;
+ family_ = IPAddress::kFamilyUnknown;
+ link_status_ = LinkStatus();
+ address_status_ = AddressStatus();
+ route_status_ = RouteStatus();
+ attributes_.clear();
}
} // namespace shill
diff --git a/rtnl_message.h b/rtnl_message.h
index f626cd0..8b2e194 100644
--- a/rtnl_message.h
+++ b/rtnl_message.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -31,7 +31,8 @@
kModeUnknown,
kModeGet,
kModeAdd,
- kModeDelete
+ kModeDelete,
+ kModeQuery
};
struct LinkStatus {
@@ -112,7 +113,9 @@
// Parse an RTNL message. Returns true on success.
bool Decode(const ByteString &data);
// Encode an RTNL message. Returns empty ByteString on failure.
- ByteString Encode();
+ ByteString Encode() const;
+ // Reset all fields.
+ void Reset();
// Getters and setters
Type type() const { return type_; }
@@ -164,9 +167,9 @@
Mode mode,
rtattr **attr_data,
int *attr_length);
- void EncodeLink(RTNLHeader *hdr);
- void EncodeAddress(RTNLHeader *hdr);
- void EncodeRoute(RTNLHeader *hdr);
+ bool EncodeLink(RTNLHeader *hdr) const;
+ bool EncodeAddress(RTNLHeader *hdr) const;
+ bool EncodeRoute(RTNLHeader *hdr) const;
Type type_;
Mode mode_;