shill: Add IPv6 address support in SocketInfoReader.
BUG=chromium:225907
TEST=Build and run unit tests.
Change-Id: I53fc90e29870fad44ae739a720ebc8dba5e4f563
Reviewed-on: https://gerrit.chromium.org/gerrit/47703
Commit-Queue: Ben Chan <benchan@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/socket_info_reader.cc b/socket_info_reader.cc
index 26f7db7..2155096 100644
--- a/socket_info_reader.cc
+++ b/socket_info_reader.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include <limits>
-#include <base/file_path.h>
#include <base/string_number_conversions.h>
#include <base/string_split.h>
@@ -22,7 +21,8 @@
namespace {
-const char kTcpSocketInfoFilePath[] = "/proc/net/tcp";
+const char kTcpv4SocketInfoFilePath[] = "/proc/net/tcp";
+const char kTcpv6SocketInfoFilePath[] = "/proc/net/tcp6";
} // namespace
@@ -30,12 +30,25 @@
SocketInfoReader::~SocketInfoReader() {}
-bool SocketInfoReader::LoadTcpSocketInfo(vector<SocketInfo> *info_list) {
- return LoadSocketInfo(FilePath(kTcpSocketInfoFilePath), info_list);
+FilePath SocketInfoReader::GetTcpv4SocketInfoFilePath() const {
+ return FilePath(kTcpv4SocketInfoFilePath);
}
-bool SocketInfoReader::LoadSocketInfo(const FilePath &info_file_path,
- vector<SocketInfo> *info_list) {
+FilePath SocketInfoReader::GetTcpv6SocketInfoFilePath() const {
+ return FilePath(kTcpv6SocketInfoFilePath);
+}
+
+bool SocketInfoReader::LoadTcpSocketInfo(vector<SocketInfo> *info_list) {
+ info_list->clear();
+ bool v4_loaded = AppendSocketInfo(GetTcpv4SocketInfoFilePath(), info_list);
+ bool v6_loaded = AppendSocketInfo(GetTcpv6SocketInfoFilePath(), info_list);
+ // Return true if we can load either /proc/net/tcp or /proc/net/tcp6
+ // successfully.
+ return v4_loaded || v6_loaded;
+}
+
+bool SocketInfoReader::AppendSocketInfo(const FilePath &info_file_path,
+ vector<SocketInfo> *info_list) {
FileReader file_reader;
if (!file_reader.Open(info_file_path)) {
SLOG(Link, 2) << __func__ << ": Failed to open '"
@@ -43,7 +56,6 @@
return false;
}
- info_list->clear();
string line;
while (file_reader.ReadLine(&line)) {
SocketInfo socket_info;
@@ -116,24 +128,26 @@
bool SocketInfoReader::ParseIPAddress(const string &input,
IPAddress *ip_address) {
- vector<uint8> bytes;
- if (!base::HexStringToBytes(input, &bytes))
+ ByteString byte_string = ByteString::CreateFromHexString(input);
+ if (byte_string.IsEmpty())
return false;
IPAddress::Family family;
- if (bytes.size() == 4)
+ if (byte_string.GetLength() ==
+ IPAddress::GetAddressLength(IPAddress::kFamilyIPv4)) {
family = IPAddress::kFamilyIPv4;
- else if (bytes.size() == 6)
+ } else if (byte_string.GetLength() ==
+ IPAddress::GetAddressLength(IPAddress::kFamilyIPv6)) {
family = IPAddress::kFamilyIPv6;
- else
+ } else {
return false;
+ }
- // TODO(benchan): This doesn't work with IPv6 addresses. Fix it
- // by introducing a proper method in ByteString to handle byte order
- // conversion.
- std::reverse(bytes.begin(), bytes.end());
+ // Linux kernel prints out IP addresses in network order via
+ // /proc/net/tcp{,6}.
+ byte_string.ConvertFromNetToCPUUInt32Array();
- *ip_address = IPAddress(family, ByteString(&bytes[0], bytes.size()));
+ *ip_address = IPAddress(family, byte_string);
return true;
}
diff --git a/socket_info_reader.h b/socket_info_reader.h
index af32525..f8ccea5 100644
--- a/socket_info_reader.h
+++ b/socket_info_reader.h
@@ -9,16 +9,11 @@
#include <vector>
#include <base/basictypes.h>
+#include <base/file_path.h>
#include <gtest/gtest_prod.h>
#include "shill/socket_info.h"
-namespace base {
-
-class FilePath;
-
-} // namespace base
-
namespace shill {
class SocketInfoReader {
@@ -26,10 +21,23 @@
SocketInfoReader();
virtual ~SocketInfoReader();
+ // Returns the file path (/proc/net/tcp by default) from where TCP/IPv4
+ // socket information are read. Overloadded by unit tests to return a
+ // different file path.
+ virtual base::FilePath GetTcpv4SocketInfoFilePath() const;
+
+ // Returns the file path (/proc/net/tcp6 by default) from where TCP/IPv6
+ // socket information are read. Overloadded by unit tests to return a
+ // different file path.
+ virtual base::FilePath GetTcpv6SocketInfoFilePath() const;
+
+ // Loads TCP socket information from /proc/net/tcp and /proc/net/tcp6.
+ // Existing entries in |info_list| are always discarded. Returns false
+ // if when neither /proc/net/tcp nor /proc/net/tcp6 can be read.
virtual bool LoadTcpSocketInfo(std::vector<SocketInfo> *info_list);
private:
- FRIEND_TEST(SocketInfoReaderTest, LoadSocketInfo);
+ FRIEND_TEST(SocketInfoReaderTest, AppendSocketInfo);
FRIEND_TEST(SocketInfoReaderTest, ParseConnectionState);
FRIEND_TEST(SocketInfoReaderTest, ParseIPAddress);
FRIEND_TEST(SocketInfoReaderTest, ParseIPAddressAndPort);
@@ -38,8 +46,8 @@
FRIEND_TEST(SocketInfoReaderTest, ParseTimerState);
FRIEND_TEST(SocketInfoReaderTest, ParseTransimitAndReceiveQueueValues);
- bool LoadSocketInfo(const base::FilePath &info_file_path,
- std::vector<SocketInfo> *info_list);
+ bool AppendSocketInfo(const base::FilePath &info_file_path,
+ std::vector<SocketInfo> *info_list);
bool ParseSocketInfo(const std::string &input, SocketInfo *socket_info);
bool ParseIPAddressAndPort(
const std::string &input, IPAddress *ip_address, uint16 *port);
diff --git a/socket_info_reader_unittest.cc b/socket_info_reader_unittest.cc
index 1fda67b..7dff7dc 100644
--- a/socket_info_reader_unittest.cc
+++ b/socket_info_reader_unittest.cc
@@ -7,24 +7,28 @@
#include <base/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/stringprintf.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
using base::FilePath;
using base::ScopedTempDir;
using std::string;
using std::vector;
-
-// TODO(benchan): Test IPv6 addresses.
+using testing::Return;
namespace shill {
namespace {
-const char kIPAddress_0_0_0_0[] = "0.0.0.0";
-const char kIPAddress_127_0_0_1[] = "127.0.0.1";
-const char kIPAddress_192_168_1_10[] = "192.168.1.10";
-const char kIPAddress_255_255_255_255[] = "255.255.255.255";
-const char *kSocketInfoLines[] = {
+const char kIPv4AddressAllZeros[] = "0.0.0.0";
+const char kIPv4AddressAllOnes[] = "255.255.255.255";
+const char kIPv4Address_127_0_0_1[] = "127.0.0.1";
+const char kIPv4Address_192_168_1_10[] = "192.168.1.10";
+const char kIPv6AddressAllZeros[] = "0000:0000:0000:0000:0000:0000:0000:0000";
+const char kIPv6AddressAllOnes[] = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
+const char kIPv6AddressPattern1[] = "0123:4567:89ab:cdef:ffee:ddcc:bbaa:9988";
+
+const char *kIPv4SocketInfoLines[] = {
" sl local_address rem_address st tx_queue rx_queue tr tm->when "
"retrnsmt uid timeout inode ",
" 0: 0100007F:0019 00000000:0000 0A 0000000A:00000005 00:00000000 "
@@ -32,9 +36,29 @@
" 1: 0A01A8C0:0050 0100007F:03FC 01 00000000:00000000 00:00000000 "
"00000000 65534 0 2787034 1 0000000000000000 100 0 0 10 -1 ",
};
+const char *kIPv6SocketInfoLines[] = {
+ " sl local_address "
+ "remote_address st tx_queue rx_queue tr tm->when "
+ "retrnsmt uid timeout inode",
+ " 0: 67452301EFCDAB89CCDDEEFF8899AABB:0019 "
+ "00000000000000000000000000000000:0000 0A 0000000A:00000005 00:00000000 "
+ "00000000 0 0 36412 1 0000000000000000 100 0 0 2 -1",
+ " 1: 00000000000000000000000000000000:0050 "
+ "67452301EFCDAB89CCDDEEFF8899AABB:03FC 01 00000000:00000000 00:00000000 "
+ "00000000 0 0 36412 1 0000000000000000 100 0 0 2 -1",
+};
} // namespace
+class SocketInfoReaderUnderTest : public SocketInfoReader {
+ public:
+ // Mock out GetTcpv4SocketInfoFilePath and GetTcpv6SocketInfoFilePath to
+ // use a temporary created socket info file instead of the actual path
+ // in procfs (i.e. /proc/net/tcp and /proc/net/tcp6).
+ MOCK_CONST_METHOD0(GetTcpv4SocketInfoFilePath, FilePath ());
+ MOCK_CONST_METHOD0(GetTcpv6SocketInfoFilePath, FilePath ());
+};
+
class SocketInfoReaderTest : public testing::Test {
protected:
IPAddress StringToIPv4Address(const string &address_string) {
@@ -43,6 +67,12 @@
return ip_address;
}
+ IPAddress StringToIPv6Address(const string &address_string) {
+ IPAddress ip_address(IPAddress::kFamilyIPv6);
+ EXPECT_TRUE(ip_address.SetAddressFromString(address_string));
+ return ip_address;
+ }
+
void CreateSocketInfoFile(const char **lines, size_t num_lines,
const FilePath &dir_path, FilePath *file_path) {
ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_path, file_path));
@@ -65,58 +95,143 @@
EXPECT_EQ(info1.timer_state(), info2.timer_state());
}
- SocketInfoReader reader_;
+ SocketInfoReaderUnderTest reader_;
};
-TEST_F(SocketInfoReaderTest, LoadSocketInfo) {
+TEST_F(SocketInfoReaderTest, LoadTcpSocketInfo) {
+ FilePath invalid_path("/non-existent-file"), v4_path, v6_path;
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ CreateSocketInfoFile(kIPv4SocketInfoLines, 2, temp_dir.path(), &v4_path);
+ CreateSocketInfoFile(kIPv6SocketInfoLines, 2, temp_dir.path(), &v6_path);
+
+ SocketInfo v4_info(SocketInfo::kConnectionStateListen,
+ StringToIPv4Address(kIPv4Address_127_0_0_1),
+ 25,
+ StringToIPv4Address(kIPv4AddressAllZeros),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending);
+ SocketInfo v6_info(SocketInfo::kConnectionStateListen,
+ StringToIPv6Address(kIPv6AddressPattern1),
+ 25,
+ StringToIPv6Address(kIPv6AddressAllZeros),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending);
+
+ vector<SocketInfo> info_list;
+ EXPECT_CALL(reader_, GetTcpv4SocketInfoFilePath())
+ .WillOnce(Return(invalid_path));
+ EXPECT_CALL(reader_, GetTcpv6SocketInfoFilePath())
+ .WillOnce(Return(invalid_path));
+ EXPECT_FALSE(reader_.LoadTcpSocketInfo(&info_list));
+
+ EXPECT_CALL(reader_, GetTcpv4SocketInfoFilePath())
+ .WillOnce(Return(v4_path));
+ EXPECT_CALL(reader_, GetTcpv6SocketInfoFilePath())
+ .WillOnce(Return(invalid_path));
+ EXPECT_TRUE(reader_.LoadTcpSocketInfo(&info_list));
+ EXPECT_EQ(1, info_list.size());
+ ExpectSocketInfoEqual(v4_info, info_list[0]);
+
+ EXPECT_CALL(reader_, GetTcpv4SocketInfoFilePath())
+ .WillOnce(Return(invalid_path));
+ EXPECT_CALL(reader_, GetTcpv6SocketInfoFilePath())
+ .WillOnce(Return(v6_path));
+ EXPECT_TRUE(reader_.LoadTcpSocketInfo(&info_list));
+ EXPECT_EQ(1, info_list.size());
+ ExpectSocketInfoEqual(v6_info, info_list[0]);
+
+ EXPECT_CALL(reader_, GetTcpv4SocketInfoFilePath())
+ .WillOnce(Return(v4_path));
+ EXPECT_CALL(reader_, GetTcpv6SocketInfoFilePath())
+ .WillOnce(Return(v6_path));
+ EXPECT_TRUE(reader_.LoadTcpSocketInfo(&info_list));
+ EXPECT_EQ(2, info_list.size());
+ ExpectSocketInfoEqual(v4_info, info_list[0]);
+ ExpectSocketInfoEqual(v6_info, info_list[1]);
+}
+
+TEST_F(SocketInfoReaderTest, AppendSocketInfo) {
FilePath file_path("/non-existent-file");
vector<SocketInfo> info_list;
- EXPECT_FALSE(reader_.LoadSocketInfo(file_path, &info_list));
+ EXPECT_FALSE(reader_.AppendSocketInfo(file_path, &info_list));
EXPECT_TRUE(info_list.empty());
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- CreateSocketInfoFile(kSocketInfoLines, 1, temp_dir.path(), &file_path);
- EXPECT_TRUE(reader_.LoadSocketInfo(file_path, &info_list));
+ CreateSocketInfoFile(kIPv4SocketInfoLines, 1, temp_dir.path(), &file_path);
+ EXPECT_TRUE(reader_.AppendSocketInfo(file_path, &info_list));
EXPECT_TRUE(info_list.empty());
- CreateSocketInfoFile(kSocketInfoLines, arraysize(kSocketInfoLines),
+ SocketInfo v4_info1(SocketInfo::kConnectionStateListen,
+ StringToIPv4Address(kIPv4Address_127_0_0_1),
+ 25,
+ StringToIPv4Address(kIPv4AddressAllZeros),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending);
+ SocketInfo v4_info2(SocketInfo::kConnectionStateEstablished,
+ StringToIPv4Address(kIPv4Address_192_168_1_10),
+ 80,
+ StringToIPv4Address(kIPv4Address_127_0_0_1),
+ 1020,
+ 0,
+ 0,
+ SocketInfo::kTimerStateNoTimerPending);
+ SocketInfo v6_info1(SocketInfo::kConnectionStateListen,
+ StringToIPv6Address(kIPv6AddressPattern1),
+ 25,
+ StringToIPv6Address(kIPv6AddressAllZeros),
+ 0,
+ 10,
+ 5,
+ SocketInfo::kTimerStateNoTimerPending);
+ SocketInfo v6_info2(SocketInfo::kConnectionStateEstablished,
+ StringToIPv6Address(kIPv6AddressAllZeros),
+ 80,
+ StringToIPv6Address(kIPv6AddressPattern1),
+ 1020,
+ 0,
+ 0,
+ SocketInfo::kTimerStateNoTimerPending);
+
+ CreateSocketInfoFile(kIPv4SocketInfoLines, arraysize(kIPv4SocketInfoLines),
temp_dir.path(), &file_path);
- EXPECT_TRUE(reader_.LoadSocketInfo(file_path, &info_list));
- EXPECT_EQ(arraysize(kSocketInfoLines) - 1, info_list.size());
- ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateListen,
- StringToIPv4Address(kIPAddress_127_0_0_1),
- 25,
- StringToIPv4Address(kIPAddress_0_0_0_0),
- 0,
- 10,
- 5,
- SocketInfo::kTimerStateNoTimerPending),
- info_list[0]);
- ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateEstablished,
- StringToIPv4Address(kIPAddress_192_168_1_10),
- 80,
- StringToIPv4Address(kIPAddress_127_0_0_1),
- 1020,
- 0,
- 0,
- SocketInfo::kTimerStateNoTimerPending),
- info_list[1]);
+ EXPECT_TRUE(reader_.AppendSocketInfo(file_path, &info_list));
+ EXPECT_EQ(arraysize(kIPv4SocketInfoLines) - 1, info_list.size());
+ ExpectSocketInfoEqual(v4_info1, info_list[0]);
+ ExpectSocketInfoEqual(v4_info2, info_list[1]);
+
+ CreateSocketInfoFile(kIPv6SocketInfoLines, arraysize(kIPv6SocketInfoLines),
+ temp_dir.path(), &file_path);
+ EXPECT_TRUE(reader_.AppendSocketInfo(file_path, &info_list));
+ EXPECT_EQ(
+ arraysize(kIPv4SocketInfoLines) + arraysize(kIPv6SocketInfoLines) - 2,
+ info_list.size());
+ ExpectSocketInfoEqual(v4_info1, info_list[0]);
+ ExpectSocketInfoEqual(v4_info2, info_list[1]);
+ ExpectSocketInfoEqual(v6_info1, info_list[2]);
+ ExpectSocketInfoEqual(v6_info2, info_list[3]);
}
TEST_F(SocketInfoReaderTest, ParseSocketInfo) {
SocketInfo info;
EXPECT_FALSE(reader_.ParseSocketInfo("", &info));
- EXPECT_FALSE(reader_.ParseSocketInfo(kSocketInfoLines[0], &info));
+ EXPECT_FALSE(reader_.ParseSocketInfo(kIPv4SocketInfoLines[0], &info));
- EXPECT_TRUE(reader_.ParseSocketInfo(kSocketInfoLines[1], &info));
+ EXPECT_TRUE(reader_.ParseSocketInfo(kIPv4SocketInfoLines[1], &info));
ExpectSocketInfoEqual(SocketInfo(SocketInfo::kConnectionStateListen,
- StringToIPv4Address(kIPAddress_127_0_0_1),
+ StringToIPv4Address(kIPv4Address_127_0_0_1),
25,
- StringToIPv4Address(kIPAddress_0_0_0_0),
+ StringToIPv4Address(kIPv4AddressAllZeros),
0,
10,
5,
@@ -132,15 +247,30 @@
EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000", &ip_address, &port));
EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000:", &ip_address, &port));
EXPECT_FALSE(reader_.ParseIPAddressAndPort(":0000", &ip_address, &port));
- EXPECT_FALSE(reader_.ParseIPAddressAndPort("0000000Y:0000",
- &ip_address, &port));
- EXPECT_FALSE(reader_.ParseIPAddressAndPort("00000000:000Y", &ip_address,
- &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "0000000Y:0000", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "00000000:000Y", &ip_address, &port));
- EXPECT_TRUE(reader_.ParseIPAddressAndPort("0a01A8c0:0050",
- &ip_address, &port));
- EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_192_168_1_10)));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "00000000000000000000000000000000", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "00000000000000000000000000000000:", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "00000000000000000000000000000000Y:0000", &ip_address, &port));
+ EXPECT_FALSE(reader_.ParseIPAddressAndPort(
+ "000000000000000000000000000000000:000Y", &ip_address, &port));
+
+ EXPECT_TRUE(reader_.ParseIPAddressAndPort(
+ "0a01A8c0:0050", &ip_address, &port));
+ EXPECT_TRUE(ip_address.Equals(
+ StringToIPv4Address(kIPv4Address_192_168_1_10)));
EXPECT_EQ(80, port);
+
+ EXPECT_TRUE(reader_.ParseIPAddressAndPort(
+ "67452301efcdab89CCDDEEFF8899AABB:1F90", &ip_address, &port));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv6Address(kIPv6AddressPattern1)));
+ EXPECT_EQ(8080, port);
}
TEST_F(SocketInfoReaderTest, ParseIPAddress) {
@@ -150,19 +280,34 @@
EXPECT_FALSE(reader_.ParseIPAddress("0", &ip_address));
EXPECT_FALSE(reader_.ParseIPAddress("00", &ip_address));
EXPECT_FALSE(reader_.ParseIPAddress("0000000Y", &ip_address));
+ EXPECT_FALSE(reader_.ParseIPAddress("0000000000000000000000000000000Y",
+ &ip_address));
EXPECT_TRUE(reader_.ParseIPAddress("00000000", &ip_address));
- EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_0_0_0_0)));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPv4AddressAllZeros)));
EXPECT_TRUE(reader_.ParseIPAddress("0100007F", &ip_address));
- EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_127_0_0_1)));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPv4Address_127_0_0_1)));
EXPECT_TRUE(reader_.ParseIPAddress("0a01A8c0", &ip_address));
- EXPECT_TRUE(ip_address.Equals(StringToIPv4Address(kIPAddress_192_168_1_10)));
+ EXPECT_TRUE(ip_address.Equals(
+ StringToIPv4Address(kIPv4Address_192_168_1_10)));
EXPECT_TRUE(reader_.ParseIPAddress("ffffffff", &ip_address));
EXPECT_TRUE(ip_address.Equals(
- StringToIPv4Address(kIPAddress_255_255_255_255)));
+ StringToIPv4Address(kIPv4AddressAllOnes)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("00000000000000000000000000000000",
+ &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv6Address(kIPv6AddressAllZeros)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("67452301efcdab89CCDDEEFF8899AABB",
+ &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv6Address(kIPv6AddressPattern1)));
+
+ EXPECT_TRUE(reader_.ParseIPAddress("ffffffffffffffffffffffffffffffff",
+ &ip_address));
+ EXPECT_TRUE(ip_address.Equals(StringToIPv6Address(kIPv6AddressAllOnes)));
}
TEST_F(SocketInfoReaderTest, ParsePort) {