tools: add network bridge support
Add support to the host tool for sending the image data over a network
connection.
Signed-off-by: Benjamin Fair <benjaminfair@google.com>
Change-Id: I88630d79499254d6c80ceaa8c7721c241d394fc8
diff --git a/tools/test/Makefile.am b/tools/test/Makefile.am
index c05654b..1fe35a0 100644
--- a/tools/test/Makefile.am
+++ b/tools/test/Makefile.am
@@ -18,6 +18,7 @@
check_PROGRAMS = \
tools_bt_unittest \
tools_lpc_unittest \
+ tools_net_unittest \
tools_updater_unittest \
tools_helper_unittest
@@ -29,6 +30,9 @@
tools_lpc_unittest_SOURCES = tools_lpc_unittest.cpp
tools_lpc_unittest_LDADD = $(top_builddir)/tools/libupdater.la
+tools_net_unittest_SOURCES = tools_net_unittest.cpp
+tools_net_unittest_LDADD = $(top_builddir)/tools/libupdater.la
+
tools_updater_unittest_SOURCES = tools_updater_unittest.cpp
tools_updater_unittest_LDADD = $(top_builddir)/tools/libupdater.la
diff --git a/tools/test/tools_net_unittest.cpp b/tools/test/tools_net_unittest.cpp
new file mode 100644
index 0000000..51556ff
--- /dev/null
+++ b/tools/test/tools_net_unittest.cpp
@@ -0,0 +1,230 @@
+#include "data.hpp"
+#include "internal_sys_mock.hpp"
+#include "net.hpp"
+#include "progress_mock.hpp"
+
+#include <cstring>
+#include <ipmiblob/test/blob_interface_mock.hpp>
+
+#include <gtest/gtest.h>
+
+namespace host_tool
+{
+namespace
+{
+
+using namespace std::literals;
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::ContainerEq;
+using ::testing::Field;
+using ::testing::Gt;
+using ::testing::InSequence;
+using ::testing::NotNull;
+using ::testing::Pointee;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::SetErrnoAndReturn;
+using ::testing::StrEq;
+
+class NetHandleTest : public ::testing::Test
+{
+ protected:
+ NetHandleTest() : handler(&blobMock, &progMock, host, port, &sysMock)
+ {
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(622);
+ sa.sin6_flowinfo = 0;
+ sa.sin6_addr = in6addr_loopback; // ::1
+ sa.sin6_scope_id = 0;
+
+ addr.ai_family = AF_INET6;
+ addr.ai_socktype = SOCK_STREAM;
+ addr.ai_addr = reinterpret_cast<struct sockaddr*>(&sa);
+ addr.ai_addrlen = sizeof(sa);
+ addr.ai_protocol = 0;
+ addr.ai_next = nullptr;
+ }
+
+ void expectOpenFile()
+ {
+ EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
+ .WillOnce(Return(inFd));
+ EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0));
+ EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str())))
+ .WillOnce(Return(fakeFileSize));
+
+ EXPECT_CALL(progMock, start(fakeFileSize));
+ }
+
+ void expectAddrInfo()
+ {
+ EXPECT_CALL(
+ sysMock,
+ getaddrinfo(StrEq(host), StrEq(port),
+ AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
+ Field(&addrinfo::ai_family, AF_INET),
+ Field(&addrinfo::ai_socktype, SOCK_STREAM)),
+ NotNull()))
+ .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0)));
+ EXPECT_CALL(sysMock, freeaddrinfo(&addr));
+ }
+
+ void expectConnection()
+ {
+ EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
+ .WillOnce(Return(connFd));
+ EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
+ EXPECT_CALL(sysMock,
+ connect(connFd, reinterpret_cast<struct sockaddr*>(&sa),
+ sizeof(sa)))
+ .WillOnce(Return(0));
+ }
+
+ internal::InternalSysMock sysMock;
+ ipmiblob::BlobInterfaceMock blobMock;
+ ProgressMock progMock;
+
+ const std::string host = "::1"s;
+ const std::string port = "622"s;
+
+ struct sockaddr_in6 sa;
+ struct addrinfo addr;
+
+ static constexpr std::uint16_t session = 0xbeef;
+ const std::string filePath = "/asdf"s;
+ static constexpr int inFd = 5;
+ static constexpr int connFd = 7;
+ static constexpr size_t fakeFileSize = 128;
+ static constexpr size_t chunkSize = 16;
+
+ NetDataHandler handler;
+};
+
+TEST_F(NetHandleTest, openFileFail)
+{
+ EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
+ .WillOnce(SetErrnoAndReturn(EACCES, -1));
+
+ EXPECT_FALSE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, getSizeFail)
+{
+ EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
+ .WillOnce(Return(inFd));
+ EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0));
+ EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))).WillOnce(Return(0));
+
+ EXPECT_FALSE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, getaddrinfoFail)
+{
+ expectOpenFile();
+
+ EXPECT_CALL(sysMock,
+ getaddrinfo(StrEq(host), StrEq(port),
+ AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
+ Field(&addrinfo::ai_family, AF_INET),
+ Field(&addrinfo::ai_socktype, SOCK_STREAM)),
+ NotNull()))
+ .WillOnce(Return(EAI_ADDRFAMILY));
+
+ EXPECT_FALSE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, connectFail)
+{
+ expectOpenFile();
+ expectAddrInfo();
+
+ EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
+ .WillOnce(Return(connFd));
+ EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
+ EXPECT_CALL(
+ sysMock,
+ connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)))
+ .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1));
+
+ EXPECT_FALSE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, sendfileFail)
+{
+ expectOpenFile();
+ expectAddrInfo();
+ expectConnection();
+
+ EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _))
+ .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1));
+
+ EXPECT_FALSE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, successOneChunk)
+{
+ expectOpenFile();
+ expectAddrInfo();
+ expectConnection();
+
+ {
+ InSequence seq;
+
+ EXPECT_CALL(sysMock,
+ sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize)))
+ .WillOnce(
+ DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize)));
+ EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
+ Gt(fakeFileSize)))
+ .WillOnce(Return(0));
+ }
+
+ struct ipmi_flash::ExtChunkHdr chunk;
+ chunk.length = fakeFileSize;
+ std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
+ std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
+ EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes)));
+
+ EXPECT_CALL(progMock, updateProgress(fakeFileSize));
+
+ EXPECT_TRUE(handler.sendContents(filePath, session));
+}
+
+TEST_F(NetHandleTest, successMultiChunk)
+{
+ expectOpenFile();
+ expectAddrInfo();
+ expectConnection();
+
+ struct ipmi_flash::ExtChunkHdr chunk;
+ chunk.length = chunkSize;
+ std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
+ std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
+
+ {
+ InSequence seq;
+
+ for (std::uint32_t offset = 0; offset < fakeFileSize;
+ offset += chunkSize)
+ {
+ EXPECT_CALL(sysMock,
+ sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize)))
+ .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize),
+ Return(chunkSize)));
+
+ EXPECT_CALL(blobMock,
+ writeBytes(session, offset, ContainerEq(chunkBytes)));
+ EXPECT_CALL(progMock, updateProgress(chunkSize));
+ }
+ EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
+ Gt(chunkSize)))
+ .WillOnce(Return(0));
+ }
+
+ EXPECT_TRUE(handler.sendContents(filePath, session));
+}
+
+} // namespace
+} // namespace host_tool