Move network config libraries from GCE.
This change brings network management libraries to CF. Libraries are near exact version of
GCE gce_network component - changes include:
- Removed dependency on namespaces
- Removed dependency on sys_client
- Header guards update.
This change will allow VSOC RIL apply changes to RMNET0 interface.
Change-Id: I767cad2d371d1a899287be0cfdf9614a7489e53b
diff --git a/common/libs/net/Android.mk b/common/libs/net/Android.mk
new file mode 100644
index 0000000..79ce69a
--- /dev/null
+++ b/common/libs/net/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcuttlefish_net
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ netlink_client.cpp \
+ network_interface_manager.cpp
+
+LOCAL_C_INCLUDES := \
+ device/google/cuttlefish_common
+
+LOCAL_SHARED_LIBRARIES := \
+ libcuttlefish_fs \
+ libbase
+
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/common/libs/net/netlink_client.cpp b/common/libs/net/netlink_client.cpp
new file mode 100644
index 0000000..898ee0f
--- /dev/null
+++ b/common/libs/net/netlink_client.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/libs/net/netlink_client.h"
+
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/glog/logging.h"
+
+namespace avd {
+namespace {
+// Representation of Network link request. Used to supply kernel with
+// information about which interface needs to be changed, and how.
+class NetlinkRequestImpl : public NetlinkRequest {
+ public:
+ NetlinkRequestImpl(int32_t command, int32_t seq_no, int32_t flags);
+
+ virtual void AddString(uint16_t type, const std::string& value);
+ virtual void AddInt32(uint16_t type, int32_t value);
+ virtual void AddIfInfo(int32_t if_index);
+ virtual void PushList(uint16_t type);
+ virtual void PopList();
+ virtual void* RequestData();
+ virtual size_t RequestLength();
+ virtual uint32_t SeqNo() {
+ return header_->nlmsg_seq;
+ }
+
+ private:
+ class RequestBuffer {
+ public:
+ RequestBuffer()
+ : current_(0),
+ buffer_length_(512),
+ buffer_(new uint8_t[buffer_length_]) {}
+
+ ~RequestBuffer() {
+ delete[] buffer_;
+ }
+
+ // Append data to buffer. If |data| is NULL, erase |length| bytes instead.
+ void Append(const void* data, size_t length) {
+ // Replace old buffer with new one. This is not thread safe (and does not
+ // have to be).
+ if (length > (buffer_length_ - current_)) {
+ uint8_t* new_buffer = new uint8_t[buffer_length_ * 2];
+ memcpy(new_buffer, buffer_, buffer_length_);
+ delete[] buffer_;
+
+ buffer_length_ *= 2;
+ buffer_ = new_buffer;
+ }
+
+ if (data) {
+ memcpy(&buffer_[current_], data, length);
+ } else {
+ memset(&buffer_[current_], 0, length);
+ }
+ // Pad with zeroes until buffer size is aligned.
+ memset(&buffer_[current_ + length], 0, RTA_ALIGN(length) - length);
+ current_ += RTA_ALIGN(length);
+ }
+
+ template <typename T>
+ T* AppendAs(const T* data) {
+ T* target = static_cast<T*>(static_cast<void*>(&buffer_[current_]));
+ Append(data, sizeof(T));
+ return target;
+ }
+
+ size_t Length() {
+ return current_;
+ }
+
+ private:
+ size_t current_;
+ size_t buffer_length_;
+ uint8_t* buffer_;
+ };
+
+ nlattr* AppendTag(uint16_t type, const void* data, uint16_t length);
+
+ std::vector<std::pair<nlattr*, int32_t> > lists_;
+ RequestBuffer request_;
+ nlmsghdr* header_;
+};
+
+nlattr* NetlinkRequestImpl::AppendTag(
+ uint16_t type, const void* data, uint16_t data_length) {
+ nlattr* attr = request_.AppendAs<nlattr>(NULL);
+ attr->nla_type = type;
+ attr->nla_len = RTA_LENGTH(data_length);
+ request_.Append(data, data_length);
+ return attr;
+}
+
+NetlinkRequestImpl::NetlinkRequestImpl(
+ int32_t command, int32_t seq_no, int32_t flags)
+ : header_(request_.AppendAs<nlmsghdr>(NULL)) {
+ header_->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
+ header_->nlmsg_type = command;
+ header_->nlmsg_seq = seq_no;
+}
+
+void NetlinkRequestImpl::AddString(uint16_t type, const std::string& value) {
+ AppendTag(type, value.c_str(), value.length());
+}
+
+void NetlinkRequestImpl::AddInt32(uint16_t type, int32_t value) {
+ AppendTag(type, &value, sizeof(value));
+}
+
+void NetlinkRequestImpl::AddIfInfo(int32_t if_index) {
+ ifinfomsg* if_info = request_.AppendAs<ifinfomsg>(NULL);
+ if_info->ifi_family = AF_UNSPEC;
+ if_info->ifi_index = if_index;
+}
+
+void NetlinkRequestImpl::PushList(uint16_t type) {
+ int length = request_.Length();
+ nlattr* list = AppendTag(type, NULL, 0);
+ lists_.push_back(std::make_pair(list, length));
+}
+
+void NetlinkRequestImpl::PopList() {
+ if (lists_.empty()) {
+ LOG(ERROR) << "List pop with no lists left on stack.";
+ return;
+ }
+
+ std::pair<nlattr*, int> list = lists_.back();
+ lists_.pop_back();
+ list.first->nla_len = request_.Length() - list.second;
+}
+
+void* NetlinkRequestImpl::RequestData() {
+ // Update request length before reporting raw data.
+ header_->nlmsg_len = request_.Length();
+ return header_;
+}
+
+size_t NetlinkRequestImpl::RequestLength() {
+ return request_.Length();
+}
+
+// NetlinkClient implementation.
+// Talks to libnetlink to apply network changes.
+class NetlinkClientImpl : public NetlinkClient {
+ public:
+ NetlinkClientImpl() = default;
+ virtual ~NetlinkClientImpl() = default;
+
+ virtual int32_t NameToIndex(const std::string& name);
+ virtual bool Send(NetlinkRequest* message);
+ virtual NetlinkRequest* CreateRequest(bool create_new_iface);
+
+ // Initialize NetlinkClient instance.
+ // Open netlink channel and initialize interface list.
+ // Returns true, if initialization was successful.
+ bool OpenNetlink();
+
+ private:
+ bool CheckResponse(uint32_t seq_no);
+
+ SharedFD netlink_fd_;
+ SharedFD network_fd_;
+ int seq_no_;
+};
+
+int32_t NetlinkClientImpl::NameToIndex(const std::string& name) {
+ ifreq ifr;
+ if (name.length() >= sizeof(ifr.ifr_name)) {
+ LOG(ERROR) << "Interface name '" << name << "' too long.";
+ return -1;
+ }
+
+ strcpy(ifr.ifr_name, name.c_str());
+ if (network_fd_->Ioctl(SIOCGIFINDEX, &ifr) < 0) {
+ LOG(ERROR) << "Could not get index of '" << name << "': "
+ << ": " << strerror(errno);
+ return -1;
+ }
+
+ return ifr.ifr_ifindex;
+}
+
+NetlinkRequest* NetlinkClientImpl::CreateRequest(bool create_new_iface) {
+ if (create_new_iface) {
+ return new NetlinkRequestImpl(
+ RTM_NEWLINK, seq_no_++, NLM_F_CREATE | NLM_F_EXCL);
+ } else {
+ return new NetlinkRequestImpl(RTM_SETLINK, seq_no_++, 0);
+ }
+}
+
+bool NetlinkClientImpl::CheckResponse(uint32_t seq_no) {
+ uint32_t len;
+ char buf[4096];
+ struct iovec iov = { buf, sizeof(buf) };
+ struct sockaddr_nl sa;
+ struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
+ struct nlmsghdr *nh;
+
+ int result = netlink_fd_->RecvMsg(&msg, 0);
+ if (result < 0) {
+ LOG(ERROR) << "Netlink error: " << strerror(errno);
+ return false;
+ }
+
+ len = (uint32_t)result;
+ LOG(INFO) << "Received netlink response (" << len << " bytes)";
+
+ for (nh = (struct nlmsghdr*)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
+ if (nh->nlmsg_seq != seq_no) {
+ // This really shouldn't happen. If we see this, it means somebody is
+ // issuing netlink requests using the same socket as us, and ignoring
+ // responses.
+ LOG(WARNING) << "Sequence number mismatch: "
+ << nh->nlmsg_seq << " != " << seq_no;
+ continue;
+ }
+
+ // This is the end of multi-part message.
+ // It indicates there's nothing more netlink wants to tell us.
+ // It also means we failed to find the response to our request.
+ if (nh->nlmsg_type == NLMSG_DONE)
+ break;
+
+ // This is the 'nlmsgerr' package carrying response to our request.
+ // It carries an 'error' value (errno) along with the netlink header info
+ // that caused this error.
+ if (nh->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = reinterpret_cast<nlmsgerr*>(nh + 1);
+ if (err->error < 0) {
+ LOG(ERROR) << "Failed to complete netlink request: "
+ << ": " << strerror(errno);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ LOG(ERROR) << "No response from netlink.";
+ return false;
+}
+
+bool NetlinkClientImpl::Send(NetlinkRequest* message) {
+ struct sockaddr_nl netlink_addr;
+ struct iovec netlink_iov = {
+ message->RequestData(),
+ message->RequestLength()
+ };
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ memset(&netlink_addr, 0, sizeof(netlink_addr));
+
+ netlink_addr.nl_family = AF_NETLINK;
+ msg.msg_name = &netlink_addr;
+ msg.msg_namelen = sizeof(netlink_addr);
+ msg.msg_iov = &netlink_iov;
+ msg.msg_iovlen = sizeof(netlink_iov) / sizeof(iovec);
+
+ if (netlink_fd_->SendMsg(&msg, 0) < 0) {
+ LOG(ERROR) << "Failed to send netlink message: "
+ << ": " << strerror(errno);
+
+ return false;
+ }
+
+ return CheckResponse(message->SeqNo());
+}
+
+bool NetlinkClientImpl::OpenNetlink() {
+ netlink_fd_ = SharedFD::Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ network_fd_ = SharedFD::Socket(AF_UNIX, SOCK_DGRAM, 0);
+ seq_no_ = 0;
+ return true;
+}
+} // namespace
+
+NetlinkClient* NetlinkClient::New() {
+ NetlinkClientImpl* client = new NetlinkClientImpl();
+
+ if (!client->OpenNetlink()) {
+ delete client;
+ client = NULL;
+ }
+ return client;
+}
+
+} // namespace avd
diff --git a/common/libs/net/netlink_client.h b/common/libs/net/netlink_client.h
new file mode 100644
index 0000000..9ecba32
--- /dev/null
+++ b/common/libs/net/netlink_client.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef COMMON_LIBS_NET_NETLINK_CLIENT_H_
+#define COMMON_LIBS_NET_NETLINK_CLIENT_H_
+
+#include <stddef.h>
+#include <string>
+
+namespace avd {
+
+// Abstraction of Network link request.
+// Used to supply kernel with information about which interface needs to be
+// changed, and how.
+class NetlinkRequest {
+ public:
+ NetlinkRequest() {}
+ virtual ~NetlinkRequest() {}
+
+ // Add an IFLA tag followed by a string.
+ // Returns true, if successful.
+ virtual void AddString(uint16_t type, const std::string& value) = 0;
+
+ // Add an IFLA tag followed by int32.
+ // Returns true, if successful.
+ virtual void AddInt32(uint16_t type, int32_t value) = 0;
+
+ // Add an interface info structure.
+ // Parameter |if_index| specifies particular interface index to which the
+ // attributes following the IfInfo apply.
+ virtual void AddIfInfo(int32_t if_index) = 0;
+
+ // Creates new list.
+ // List mimmic recursive structures in a flat, contiuous representation.
+ // Each call to PushList() should have a corresponding call to PopList
+ // indicating end of sub-attribute list.
+ virtual void PushList(uint16_t type) = 0;
+
+ // Indicates end of previously declared list.
+ virtual void PopList() = 0;
+
+ // Request data.
+ virtual void* RequestData() = 0;
+
+ // Request length.
+ virtual size_t RequestLength() = 0;
+
+ // Request Sequence Number.
+ virtual uint32_t SeqNo() = 0;
+
+ private:
+ NetlinkRequest(const NetlinkRequest&);
+ NetlinkRequest& operator= (const NetlinkRequest&);
+};
+
+// Abstraction of Netlink client class.
+class NetlinkClient {
+ public:
+ NetlinkClient() {}
+ virtual ~NetlinkClient() {}
+
+ // Get interface index.
+ // Returns 0 if interface does not exist.
+ virtual int32_t NameToIndex(const std::string& name) = 0;
+
+ // Create new Netlink Request structure.
+ // When |create_new_interface| is true, the request will create a new,
+ // not previously existing interface.
+ virtual NetlinkRequest* CreateRequest(bool create_new_interface) = 0;
+
+ // Send netlink message to kernel.
+ virtual bool Send(NetlinkRequest* message) = 0;
+
+ // Create default instance of NetlinkClient.
+ static NetlinkClient* New();
+
+ private:
+ NetlinkClient(const NetlinkClient&);
+ NetlinkClient& operator= (const NetlinkClient&);
+};
+
+} // namespace avd
+
+#endif // COMMON_LIBS_NET_NETLINK_CLIENT_H_
diff --git a/common/libs/net/netlink_client_test.cpp b/common/libs/net/netlink_client_test.cpp
new file mode 100644
index 0000000..0b50ad2
--- /dev/null
+++ b/common/libs/net/netlink_client_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/libs/net/netlink_client.h"
+
+#include <linux/rtnetlink.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <memory>
+
+using ::testing::ElementsAreArray;
+using ::testing::MatchResultListener;
+using ::testing::Return;
+
+namespace avd {
+namespace {
+extern "C" void klog_write(int /* level */, const char* /* format */, ...) {}
+
+// Dump hex buffer to test log.
+void Dump(MatchResultListener* result_listener, const char* title,
+ const uint8_t* data, size_t length) {
+ for (size_t item = 0; item < length;) {
+ *result_listener << title;
+ do {
+ result_listener->stream()->width(2);
+ result_listener->stream()->fill('0');
+ *result_listener << std::hex << +data[item] << " ";
+ ++item;
+ } while (item & 0xf);
+ *result_listener << "\n";
+ }
+}
+
+// Compare two memory areas byte by byte, print information about first
+// difference. Dumps both bufferst to user log.
+bool Compare(MatchResultListener* result_listener,
+ const uint8_t* exp, const uint8_t* act, size_t length) {
+ for (size_t index = 0; index < length; ++index) {
+ if (exp[index] != act[index]) {
+ *result_listener << "\nUnexpected data at offset " << index << "\n";
+ Dump(result_listener, "Data Expected: ", exp, length);
+ Dump(result_listener, " Data Actual: ", act, length);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Matcher validating Netlink Request data.
+MATCHER_P2(RequestDataIs, data, length, "Matches expected request data") {
+ size_t offset = sizeof(nlmsghdr);
+ if (offset + length != arg->RequestLength()) {
+ *result_listener << "Unexpected request length: "
+ << arg->RequestLength() - offset << " vs " << length;
+ return false;
+ }
+
+ // Note: Request begins with header (nlmsghdr). Header is not covered by this
+ // call.
+ const uint8_t* exp_data = static_cast<const uint8_t*>(
+ static_cast<const void*>(data));
+ const uint8_t* act_data = static_cast<const uint8_t*>(arg->RequestData());
+ return Compare(
+ result_listener, exp_data, &act_data[offset], length);
+}
+
+MATCHER_P4(RequestHeaderIs, length, type, flags, seq,
+ "Matches request header") {
+ nlmsghdr* header = static_cast<nlmsghdr*>(arg->RequestData());
+ if (arg->RequestLength() < sizeof(header)) {
+ *result_listener << "Malformed header: too short.";
+ return false;
+ }
+
+ if (header->nlmsg_len != length) {
+ *result_listener << "Invalid message length: "
+ << header->nlmsg_len << " vs " << length;
+ return false;
+ }
+
+ if (header->nlmsg_type != type) {
+ *result_listener << "Invalid header type: "
+ << header->nlmsg_type << " vs " << type;
+ return false;
+ }
+
+ if (header->nlmsg_flags != flags) {
+ *result_listener << "Invalid header flags: "
+ << header->nlmsg_flags << " vs " << flags;
+ return false;
+ }
+
+ if (header->nlmsg_seq != seq) {
+ *result_listener << "Invalid header sequence number: "
+ << header->nlmsg_seq << " vs " << seq;
+ return false;
+ }
+
+ return true;
+}
+
+constexpr int kNetlinkSocket = 1;
+constexpr int kNetworkSocket = 2;
+
+} // namespace
+
+class NetlinkClientTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<NetlinkClient> nl_client_;
+};
+
+TEST_F(NetlinkClientTest, BasicStringNode) {
+ constexpr uint16_t kDummyTag = 0xfce2;
+ constexpr char kLongString[] = "long string";
+
+ struct {
+ const uint16_t attr_length = 0xf; // 11 bytes of text + 4 bytes of header.
+ const uint16_t attr_type = kDummyTag;
+ char text[sizeof(kLongString)]; // sizeof includes padding 0.
+ } expected;
+
+ memcpy(&expected.text, kLongString, sizeof(kLongString));
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->AddString(kDummyTag, kLongString);
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, NoForcedStringPadding) {
+ // Strings with length being a multiple of four should not get additional zero
+ // padding.
+ constexpr uint16_t kDummyTag = 0xfce2;
+ constexpr char kShortString[] = "four"; // Keep 4 bytes
+
+ struct {
+ const uint16_t attr_length = 0x8; // 4 bytes of text + 4 bytes of header.
+ const uint16_t attr_type = kDummyTag;
+ char text[4];
+ } expected;
+
+ memcpy(&expected.text, kShortString, sizeof(expected.text));
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->AddString(kDummyTag, kShortString);
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, BasicIntNode) {
+ // Basic { Dummy: Value } test.
+ constexpr uint16_t kDummyTag = 0xfce2;
+ constexpr int32_t kValue = 0x1badd00d;
+
+ struct {
+ const uint16_t attr_length = 0x8; // 4 bytes of value + 4 bytes of header.
+ const uint16_t attr_type = kDummyTag;
+ const uint32_t attr_value = kValue;
+ } expected;
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->AddInt32(kDummyTag, kValue);
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, SingleList) {
+ // List: { Dummy: Value}
+ constexpr uint16_t kDummyTag = 0xfce2;
+ constexpr uint16_t kListTag = 0xcafe;
+ constexpr int32_t kValue = 0x1badd00d;
+
+ struct {
+ const uint16_t list_length = 0xc;
+ const uint16_t list_type = kListTag;
+ const uint16_t attr_length = 0x8; // 4 bytes of value + 4 bytes of header.
+ const uint16_t attr_type = kDummyTag;
+ const uint32_t attr_value = kValue;
+ } expected;
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->PushList(kListTag);
+ request->AddInt32(kDummyTag, kValue);
+ request->PopList();
+
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, NestedList) {
+ // List1: { List2: { Dummy: Value}}
+ constexpr uint16_t kDummyTag = 0xfce2;
+ constexpr uint16_t kList1Tag = 0xcafe;
+ constexpr uint16_t kList2Tag = 0xfeed;
+ constexpr int32_t kValue = 0x1badd00d;
+
+ struct {
+ const uint16_t list1_length = 0x10;
+ const uint16_t list1_type = kList1Tag;
+ const uint16_t list2_length = 0xc;
+ const uint16_t list2_type = kList2Tag;
+ const uint16_t attr_length = 0x8;
+ const uint16_t attr_type = kDummyTag;
+ const uint32_t attr_value = kValue;
+ } expected;
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->PushList(kList1Tag);
+ request->PushList(kList2Tag);
+ request->AddInt32(kDummyTag, kValue);
+ request->PopList();
+ request->PopList();
+
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, ListSequence) {
+ // List1: { Dummy1: Value1}, List2: { Dummy2: Value2 }
+ constexpr uint16_t kDummy1Tag = 0xfce2;
+ constexpr uint16_t kDummy2Tag = 0xfd38;
+ constexpr uint16_t kList1Tag = 0xcafe;
+ constexpr uint16_t kList2Tag = 0xfeed;
+ constexpr int32_t kValue1 = 0x1badd00d;
+ constexpr int32_t kValue2 = 0xfee1;
+
+ struct {
+ const uint16_t list1_length = 0xc;
+ const uint16_t list1_type = kList1Tag;
+ const uint16_t attr1_length = 0x8;
+ const uint16_t attr1_type = kDummy1Tag;
+ const uint32_t attr1_value = kValue1;
+ const uint16_t list2_length = 0xc;
+ const uint16_t list2_type = kList2Tag;
+ const uint16_t attr2_length = 0x8;
+ const uint16_t attr2_type = kDummy2Tag;
+ const uint32_t attr2_value = kValue2;
+ } expected;
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->PushList(kList1Tag);
+ request->AddInt32(kDummy1Tag, kValue1);
+ request->PopList();
+ request->PushList(kList2Tag);
+ request->AddInt32(kDummy2Tag, kValue2);
+ request->PopList();
+
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, ComplexList) {
+ // List1: { List2: { Dummy1: Value1 }, Dummy2: Value2 }
+ constexpr uint16_t kDummy1Tag = 0xfce2;
+ constexpr uint16_t kDummy2Tag = 0xfd38;
+ constexpr uint16_t kList1Tag = 0xcafe;
+ constexpr uint16_t kList2Tag = 0xfeed;
+ constexpr int32_t kValue1 = 0x1badd00d;
+ constexpr int32_t kValue2 = 0xfee1;
+
+ struct {
+ const uint16_t list1_length = 0x18;
+ const uint16_t list1_type = kList1Tag;
+ const uint16_t list2_length = 0xc; // Note, this only covers until kValue1.
+ const uint16_t list2_type = kList2Tag;
+ const uint16_t attr1_length = 0x8;
+ const uint16_t attr1_type = kDummy1Tag;
+ const uint32_t attr1_value = kValue1;
+ const uint16_t attr2_length = 0x8;
+ const uint16_t attr2_type = kDummy2Tag;
+ const uint32_t attr2_value = kValue2;
+ } expected;
+
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ request->PushList(kList1Tag);
+ request->PushList(kList2Tag);
+ request->AddInt32(kDummy1Tag, kValue1);
+ request->PopList();
+ request->AddInt32(kDummy2Tag, kValue2);
+ request->PopList();
+
+ EXPECT_THAT(request, RequestDataIs(&expected, sizeof(expected)));
+}
+
+TEST_F(NetlinkClientTest, SimpleNetlinkCreateHeader) {
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(true));
+ constexpr char kValue[] = "random string";
+ request->AddString(0, kValue); // Have something to work with.
+
+ constexpr size_t kMsgLength =
+ sizeof(nlmsghdr) + sizeof(nlattr) + RTA_ALIGN(sizeof(kValue));
+
+ EXPECT_THAT(request, RequestHeaderIs(
+ kMsgLength, // Expected size of message.
+ RTM_NEWLINK, // Results from creane_new_iface=true in CreateRequest.
+ NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST, // Ditto.
+ 0u));
+}
+
+TEST_F(NetlinkClientTest, SimpleNetlinkUpdateHeader) {
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ constexpr char kValue[] = "random string";
+ request->AddString(0, kValue); // Have something to work with.
+
+ constexpr size_t kMsgLength =
+ sizeof(nlmsghdr) + sizeof(nlattr) + RTA_ALIGN(sizeof(kValue));
+
+ EXPECT_THAT(request, RequestHeaderIs(
+ kMsgLength, // Expected size of message.
+ RTM_SETLINK, // Results from creane_new_iface=true in CreateRequest.
+ NLM_F_REQUEST | NLM_F_ACK, // Ditto.
+ 0u));
+}
+
+TEST_F(NetlinkClientTest, SequenceNumbers) {
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ constexpr size_t kMsgLength =
+ sizeof(nlmsghdr) + sizeof(nlattr) + sizeof(int32_t);
+
+ request->AddInt32(0, 0);
+ EXPECT_THAT(
+ request,
+ RequestHeaderIs(kMsgLength, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, 0u));
+
+ request.reset(nl_client_->CreateRequest(false));
+ request->AddInt32(0, 0);
+ EXPECT_THAT(
+ request,
+ RequestHeaderIs(kMsgLength, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, 1u));
+
+ request.reset(nl_client_->CreateRequest(false));
+ request->AddInt32(0, 0);
+ EXPECT_THAT(
+ request,
+ RequestHeaderIs(kMsgLength, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, 2u));
+}
+
+} // namespace avd
diff --git a/common/libs/net/network_interface.h b/common/libs/net/network_interface.h
new file mode 100644
index 0000000..2040667
--- /dev/null
+++ b/common/libs/net/network_interface.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
+#define GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
+
+#include <string>
+
+namespace avd {
+
+// Abstraction of network interfaces.
+// This interface provides means to modify network interface parameters.
+class NetworkInterface {
+ public:
+ explicit NetworkInterface(size_t if_index)
+ : if_index_(if_index) {}
+
+ NetworkInterface()
+ : if_index_(0) {}
+
+ virtual ~NetworkInterface() {}
+
+ // Get network interface index.
+ size_t index() const {
+ return if_index_;
+ }
+
+ // Set name of the network interface.
+ virtual NetworkInterface& set_name(const std::string& new_name) {
+ name_ = new_name;
+ return *this;
+ }
+
+ // Get name of the network interface.
+ // Returns name, if previously set.
+ virtual const std::string& name() const {
+ return name_;
+ }
+
+ private:
+ // Index of the network interface in the system table. 0 indicates new
+ // interface.
+ size_t if_index_;
+ // Name of the interface, e.g. "eth0".
+ std::string name_;
+
+ NetworkInterface(const NetworkInterface&);
+ NetworkInterface& operator= (const NetworkInterface&);
+};
+
+} // namespace avd
+
+#endif // GUEST_GCE_NETWORK_NETWORK_INTERFACE_H_
diff --git a/common/libs/net/network_interface_manager.cpp b/common/libs/net/network_interface_manager.cpp
new file mode 100644
index 0000000..71ceb94
--- /dev/null
+++ b/common/libs/net/network_interface_manager.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "common/libs/net/network_interface_manager.h"
+
+#include <arpa/inet.h>
+#include <linux/if_link.h>
+#include <memory>
+
+#include "common/libs/glog/logging.h"
+#include "common/libs/net/network_interface.h"
+
+namespace avd {
+
+// VETH and IF constants, taken from linux/veth.h and linux/if_link.h
+// To break direct dependency from linux headers, which conflict with bionic
+// headers, we keep a copy of these values here.
+// Names have been modified to avoid potential conflict with future Android
+// releases.
+enum {
+ kVEth_Info_Unspec,
+ kVEth_Info_Peer,
+};
+
+enum {
+ kIFLA_Unspec,
+ kIFLA_Address,
+ kIFLA_Broadcast,
+ kIFLA_IfName,
+ kIFLA_MTU,
+ kIFLA_Link,
+ kIFLA_QDisc,
+ kIFLA_Stats,
+ kIFLA_Cost,
+ kIFLA_Priority,
+ kIFLA_Master,
+ kIFLA_Wireless,
+ kIFLA_ProtInfo,
+ kIFLA_TxQLen,
+ kIFLA_Map,
+ kIFLA_Weight,
+ kIFLA_OperState,
+ kIFLA_LinkMode,
+ kIFLA_LinkInfo,
+ kIFLA_NetNsPID,
+ kIFLA_IfAlias,
+ kIFLA_NumVF,
+ kIFLA_VFInfoList,
+ kIFLA_Stats64,
+ kIFLA_VfPorts,
+ kIFLA_PortSelf,
+ kIFLA_AFSpec,
+ kIFLA_Group,
+ kIFLA_NetNsFD,
+ kIFLA_ExtMask,
+ kIFLA_Promiscuity,
+ kIFLA_NumTxQueues,
+ kIFLA_NumRxQueues,
+ kIFLA_Carrier,
+ kIFLA_PhysPortId,
+ kIFLA_CarrierChanges,
+};
+
+enum {
+ kIFLA_Info_Unspec,
+ kIFLA_Info_Kind,
+ kIFLA_Info_Data,
+ kIFLA_Info_XStats,
+ kIFLA_Info_SlaveKind,
+ kIFLA_Info_SlaveData,
+};
+
+namespace {
+// Virtual interface kind. Used to create new VETH pairs.
+const char kVethLinkKind[] = "veth";
+} // namespace
+
+NetworkInterfaceManager *NetworkInterfaceManager::New(
+ NetlinkClient* nl_client) {
+ if (nl_client == NULL) {
+ LOG(ERROR) << "NetlinkClient is NULL!";
+ return NULL;
+ }
+
+ return new NetworkInterfaceManager(nl_client);
+}
+
+NetworkInterfaceManager::NetworkInterfaceManager(
+ NetlinkClient* nl_client)
+ : nl_client_(nl_client) {}
+
+NetworkInterface* NetworkInterfaceManager::Open(const std::string& if_name) {
+ const int32_t index = nl_client_->NameToIndex(if_name);
+ if (index < 0) {
+ LOG(ERROR) << "Failed to get interface (" << if_name << ") index.";
+ return NULL;
+ }
+
+ return new NetworkInterface(index);
+}
+
+bool NetworkInterfaceManager::CreateVethPair(
+ const NetworkInterface& veth1, const NetworkInterface& veth2) {
+ // The IFLA structure is not linear and can carry multiple embedded chunks.
+ // This is the case when we create a new link.
+ // IFLA_LINKINFO contains a substructure describing the link.
+ // Each IFLA tag has associated data length. In order to provide the length of
+ // the structure, we build substructures directly inside the buffer, and later
+ // update the length.
+ // Structure looks like this:
+ //
+ // RTM_NEWLINK[
+ // [ ... interface 1 details ... ]
+ // IFLA_LINKINFO[
+ // length,
+ // IFLA_INFO_KIND[length, "veth"],
+ // IFLA_INFO_DATA[
+ // length,
+ // VETH_INFO_PEER[
+ // length,
+ // [ ... interface 2 details ... ]
+ // ],
+ // ],
+ // ],
+ // ]
+ //
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(true));
+
+ if (!request.get()) return false;
+ if (!BuildRequest(request.get(), veth1)) return false;
+ request->PushList(kIFLA_LinkInfo);
+ request->AddString(kIFLA_Info_Kind, kVethLinkKind);
+ request->PushList(kIFLA_Info_Data);
+ request->PushList(kVEth_Info_Peer);
+ if (!BuildRequest(request.get(), veth2)) return false;
+
+ request->PopList(); // kVEth_Info_Peer
+ request->PopList(); // kIFLA_Info_Data
+ request->PopList(); // kIFLA_LinkInfo
+
+ return nl_client_->Send(request.get());
+}
+
+bool NetworkInterfaceManager::ApplyChanges(const NetworkInterface& iface) {
+ std::unique_ptr<NetlinkRequest> request(nl_client_->CreateRequest(false));
+ if (!BuildRequest(request.get(), iface)) return false;
+ return nl_client_->Send(request.get());
+}
+
+// private
+bool NetworkInterfaceManager::BuildRequest(
+ NetlinkRequest* request, const NetworkInterface& interface) {
+ request->AddIfInfo(interface.index());
+
+ // The following changes are idempotent, e.g. changing interface name to
+ // itself is essentially a no-op.
+ if (!interface.name().empty()) {
+ request->AddString(kIFLA_IfName, interface.name());
+ }
+
+ return true;
+}
+
+} // namespace avd
+
diff --git a/common/libs/net/network_interface_manager.h b/common/libs/net/network_interface_manager.h
new file mode 100644
index 0000000..526cf81
--- /dev/null
+++ b/common/libs/net/network_interface_manager.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_
+#define COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_
+
+#include <string>
+
+#include "common/libs/net/netlink_client.h"
+#include "common/libs/net/network_interface.h"
+
+namespace avd {
+
+// Network interface manager class.
+// - Provides access for existing network interfaces,
+// - provides means to create new virtual interfaces.
+//
+// Example usage:
+//
+// std::unique_ptr<NetlinkClient> client(NetlinkClient::GetDefault());
+// NetworkInterfaceManager manager(client.get());
+// std::unique_ptr<NetworkInterface> iface(manager.Open("eth0"));
+//
+class NetworkInterfaceManager {
+ public:
+ // Open existing network interface.
+ //
+ // NOTE: this method does not fill in any NetworkInterface details yet.
+ NetworkInterface* Open(const std::string& if_name);
+
+ // Apply changes made to existing network interface.
+ // This method cannot be used to instantiate new network interfaces.
+ bool ApplyChanges(const NetworkInterface& interface);
+
+ // Create new connected pair of virtual (veth) interfaces.
+ // Supplied pair of interfaces describe both endpoints' properties.
+ bool CreateVethPair(const NetworkInterface& first,
+ const NetworkInterface& second);
+
+ // Creates new NetworkInterfaceManager.
+ // Returns NULL if parameters are invalid.
+ static NetworkInterfaceManager* New(NetlinkClient* nl_client);
+
+ private:
+ NetworkInterfaceManager(NetlinkClient* nl_client);
+
+ // Build (partial) netlink request.
+ bool BuildRequest(NetlinkRequest* request, const NetworkInterface& interface);
+
+ NetlinkClient* nl_client_;
+
+ NetworkInterfaceManager(const NetworkInterfaceManager&);
+ NetworkInterfaceManager& operator= (const NetworkInterfaceManager&);
+};
+
+} // namespace avd
+
+#endif // COMMON_LIBS_NET_NETWORK_INTERFACE_MANAGER_H_