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_