shill: Add RTNLMessage: rtnl message parser/composer

Add RTNLMessage type.  This will be used to remove some bespoke
messaging parsing, but more immediately, it will be used by the
routing code.

BUG=chromium-os:17277
TEST=New unittest

Change-Id: Idb0559b907f018e021193c51d30f5027c8debec3
Reviewed-on: http://gerrit.chromium.org/gerrit/4183
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/Makefile b/Makefile
index 22d66a1..e03f580 100644
--- a/Makefile
+++ b/Makefile
@@ -94,6 +94,7 @@
 	proxy_factory.o \
 	rtnl_handler.o \
 	rtnl_listener.o \
+	rtnl_message.o \
 	service.o \
 	service_dbus_adaptor.o \
 	shill_config.o \
@@ -141,6 +142,7 @@
 	property_store_unittest.o \
 	rtnl_handler_unittest.o \
 	rtnl_listener_unittest.o \
+	rtnl_message_unittest.o \
 	service_unittest.o \
 	shill_unittest.o \
 	testrunner.o \
diff --git a/byte_string.cc b/byte_string.cc
index 3a3412f..3c9db10 100644
--- a/byte_string.cc
+++ b/byte_string.cc
@@ -49,4 +49,8 @@
   return data_ == b.data_;
 }
 
+void ByteString::Append(const ByteString &b) {
+  data_.insert(data_.end(), b.data_.begin(), b.data_.end());
+}
+
 }  // namespace shill
diff --git a/byte_string.h b/byte_string.h
index c4c38b2..0077ff0 100644
--- a/byte_string.h
+++ b/byte_string.h
@@ -5,6 +5,7 @@
 #ifndef SHILL_BYTE_STRING_
 #define SHILL_BYTE_STRING_
 
+#include <string>
 #include <vector>
 
 #include <base/basictypes.h>
@@ -19,6 +20,12 @@
   explicit ByteString(size_t length) : data_(length) {}
   ByteString(const unsigned char *data, size_t length)
       : data_(data, data + length) {}
+  ByteString(const std::string &data, bool copy_terminator)
+    : data_(reinterpret_cast<const unsigned char *>(data.c_str()),
+            reinterpret_cast<const unsigned char *>(data.c_str() +
+                                                    data.length() +
+                                                    (copy_terminator ?
+                                                     1 : 0))) {}
 
   ByteString &operator=(const ByteString &b) {
     data_ = b.data_;
@@ -43,6 +50,10 @@
 
   bool IsZero() const;
   bool Equals(const ByteString &b) const;
+  void Append(const ByteString &b);
+  void Resize(int size) {
+    data_.resize(size, 0);
+  }
 
  private:
   std::vector<unsigned char> data_;
diff --git a/byte_string_unittest.cc b/byte_string_unittest.cc
index a7e8f04..e8e6048 100644
--- a/byte_string_unittest.cc
+++ b/byte_string_unittest.cc
@@ -16,6 +16,7 @@
 const unsigned char kTest1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 const unsigned char kTest2[] = { 1, 2, 3, 4 };
 const unsigned char kTest3[] = { 0, 0, 0, 0 };
+const char kTest4[] = "Hello world";
 }  // namespace {}
 
 class ByteStringTest : public Test {
@@ -63,8 +64,16 @@
   EXPECT_FALSE(bs2.Equals(bs1));
   EXPECT_FALSE(bs3.Equals(bs1));
 
-  ByteString bs4(kTest1, sizeof(kTest1));
-  EXPECT_TRUE(bs4.Equals(bs1));
+  ByteString bs4(kTest4, false);
+  EXPECT_EQ(strlen(kTest4), bs4.GetLength());
+  EXPECT_EQ(0, memcmp(kTest4, bs4.GetData(), bs4.GetLength()));
+
+  ByteString bs5(kTest4, true);
+  EXPECT_EQ(strlen(kTest4) + 1, bs5.GetLength());
+  EXPECT_EQ(0, memcmp(kTest4, bs5.GetData(), bs5.GetLength()));
+
+  ByteString bs6(kTest1, sizeof(kTest1));
+  EXPECT_TRUE(bs6.Equals(bs1));
 }
 
 TEST_F(ByteStringTest, UInt32) {
@@ -96,4 +105,21 @@
 #endif
 }
 
+TEST_F(ByteStringTest, Resize) {
+  ByteString bs1(kTest2, sizeof(kTest2));
+  uint32 val;
+
+  bs1.Resize(sizeof(kTest2) + 10);
+  EXPECT_EQ(sizeof(kTest2) + 10, bs1.GetLength());
+  EXPECT_TRUE(bs1.GetData() != NULL);
+  EXPECT_EQ(0, memcmp(bs1.GetData(), kTest2, sizeof(kTest2)));
+  for (size_t i = sizeof(kTest2); i < sizeof(kTest2) + 10; ++i) {
+    EXPECT_EQ(0, bs1.GetData()[i]);
+  }
+
+  bs1.Resize(sizeof(kTest2) - 2);
+  EXPECT_EQ(sizeof(kTest2) - 2, bs1.GetLength());
+  EXPECT_EQ(0, memcmp(bs1.GetData(), kTest2, sizeof(kTest2) - 2));
+}
+
 }  // namespace shill
diff --git a/ip_address.h b/ip_address.h
index 1677ca9..f931aee 100644
--- a/ip_address.h
+++ b/ip_address.h
@@ -15,11 +15,10 @@
 
 class IPAddress {
  public:
-  enum Family {
-    kAddressFamilyUnknown,
-    kAddressFamilyIPv4 = AF_INET,
-    kAddressFamilyIPv6 = AF_INET6
-  };
+  typedef unsigned char Family;
+  static const Family kAddressFamilyUnknown = AF_UNSPEC;
+  static const Family kAddressFamilyIPv4 = AF_INET;
+  static const Family kAddressFamilyIPv6 = AF_INET6;
 
   explicit IPAddress(Family family);
   IPAddress(Family family, const ByteString &address);
diff --git a/rtnl_message.cc b/rtnl_message.cc
new file mode 100644
index 0000000..92636e4
--- /dev/null
+++ b/rtnl_message.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/rtnl_message.h"
+
+#include <base/logging.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+namespace shill {
+
+struct RTNLHeader {
+  RTNLHeader() {
+    memset(this, 0, sizeof(*this));
+  }
+  struct nlmsghdr hdr;
+  union {
+    struct ifinfomsg ifi;
+    struct ifaddrmsg ifa;
+    struct rtmsg rtm;
+    struct rtgenmsg gen;
+  };
+};
+
+RTNLMessage::RTNLMessage()
+    : type_(kMessageTypeUnknown),
+      mode_(kMessageModeUnknown),
+      flags_(0),
+      interface_index_(0),
+      family_(IPAddress::kAddressFamilyUnknown) {}
+
+RTNLMessage::RTNLMessage(MessageType type,
+                         MessageMode mode,
+                         unsigned int flags,
+                         uint32 seq,
+                         uint32 pid,
+                         int interface_index,
+                         IPAddress::Family family)
+    : type_(type),
+      mode_(mode),
+      flags_(flags),
+      seq_(seq),
+      pid_(pid),
+      interface_index_(interface_index),
+      family_(family) {}
+
+bool RTNLMessage::Decode(const ByteString &msg) {
+  bool ret = DecodeInternal(msg);
+  if (!ret) {
+    mode_ = kMessageModeUnknown;
+    type_ = kMessageTypeUnknown;
+  }
+  return ret;
+}
+
+bool RTNLMessage::DecodeInternal(const ByteString &msg) {
+  const RTNLHeader *hdr =
+      reinterpret_cast<const RTNLHeader *>(msg.GetConstData());
+
+  if (msg.GetLength() < sizeof(hdr->hdr) ||
+      msg.GetLength() < hdr->hdr.nlmsg_len)
+    return false;
+
+  MessageMode mode = kMessageModeUnknown;
+  switch (hdr->hdr.nlmsg_type) {
+  case RTM_NEWLINK:
+  case RTM_NEWADDR:
+  case RTM_NEWROUTE:
+    mode = kMessageModeAdd;
+    break;
+
+  case RTM_DELLINK:
+  case RTM_DELADDR:
+  case RTM_DELROUTE:
+    mode = kMessageModeDelete;
+    break;
+
+  default:
+    return false;
+  }
+
+  rtattr *attr_data = NULL;
+  int attr_length = 0;
+
+  switch (hdr->hdr.nlmsg_type) {
+  case RTM_NEWLINK:
+  case RTM_DELLINK:
+    if (!DecodeLink(hdr, mode, &attr_data, &attr_length))
+      return false;
+    break;
+
+  case RTM_NEWADDR:
+  case RTM_DELADDR:
+    if (!DecodeAddress(hdr, mode, &attr_data, &attr_length))
+      return false;
+    break;
+
+  case RTM_NEWROUTE:
+  case RTM_DELROUTE:
+    if (!DecodeRoute(hdr, mode, &attr_data, &attr_length))
+      return false;
+    break;
+
+  default:
+    NOTREACHED();
+  }
+
+  flags_ = hdr->hdr.nlmsg_flags;
+  seq_ = hdr->hdr.nlmsg_seq;
+  pid_ = hdr->hdr.nlmsg_pid;
+
+  while (attr_data && RTA_OK(attr_data, attr_length)) {
+    SetAttribute(
+        attr_data->rta_type,
+        ByteString(reinterpret_cast<unsigned char *>(RTA_DATA(attr_data)),
+                   RTA_PAYLOAD(attr_data)));
+    attr_data = RTA_NEXT(attr_data, attr_length);
+  }
+
+  if (attr_length) {
+    // We hit a parse error while going through the attributes
+    attributes_.clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool RTNLMessage::DecodeLink(const RTNLHeader *hdr,
+                             MessageMode mode,
+                             rtattr **attr_data,
+                             int *attr_length) {
+  if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
+    return false;
+  }
+
+  mode_ = mode;
+  *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr));
+  *attr_length = IFLA_PAYLOAD(&hdr->hdr);
+
+  type_ = kMessageTypeLink;
+  family_ = hdr->ifi.ifi_family;
+  interface_index_ = hdr->ifi.ifi_index;
+  set_link_status(LinkStatus(hdr->ifi.ifi_type,
+                             hdr->ifi.ifi_flags,
+                             hdr->ifi.ifi_change));
+  return true;
+}
+
+bool RTNLMessage::DecodeAddress(const RTNLHeader *hdr,
+                                MessageMode mode,
+                                rtattr **attr_data,
+                                int *attr_length) {
+  if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
+    return false;
+  }
+  mode_ = mode;
+  *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr));
+  *attr_length = IFA_PAYLOAD(&hdr->hdr);
+
+  type_ = kMessageTypeAddress;
+  family_ = hdr->ifa.ifa_family;
+  interface_index_ = hdr->ifa.ifa_index;
+  set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen,
+                                   hdr->ifa.ifa_flags,
+                                   hdr->ifa.ifa_scope));
+  return true;
+}
+
+bool RTNLMessage::DecodeRoute(const RTNLHeader *hdr,
+                              MessageMode mode,
+                              rtattr **attr_data,
+                              int *attr_length) {
+  if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
+    return false;
+  }
+  mode_ = mode;
+  *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr));
+  *attr_length = RTM_PAYLOAD(&hdr->hdr);
+
+  type_ = kMessageTypeRoute;
+  family_ = hdr->rtm.rtm_family;
+  set_route_status(RouteStatus(hdr->rtm.rtm_dst_len,
+                               hdr->rtm.rtm_src_len,
+                               hdr->rtm.rtm_table,
+                               hdr->rtm.rtm_protocol,
+                               hdr->rtm.rtm_scope,
+                               hdr->rtm.rtm_type,
+                               hdr->rtm.rtm_flags));
+  return true;
+}
+
+ByteString RTNLMessage::Encode() {
+  if (type_ != kMessageTypeLink &&
+      type_ != kMessageTypeAddress &&
+      type_ != kMessageTypeRoute) {
+    return ByteString();
+  }
+
+  RTNLHeader hdr;
+  hdr.hdr.nlmsg_flags = flags_;
+  hdr.hdr.nlmsg_seq = seq_;
+  hdr.hdr.nlmsg_pid = pid_;
+  hdr.hdr.nlmsg_seq = 0;
+
+  if (mode_ == kMessageModeGet) {
+    if (type_ == kMessageTypeLink) {
+      hdr.hdr.nlmsg_type = RTM_GETLINK;
+    } else if (type_ == kMessageTypeAddress) {
+      hdr.hdr.nlmsg_type = RTM_GETADDR;
+    } else if (type_ == kMessageTypeRoute) {
+      hdr.hdr.nlmsg_type = RTM_GETROUTE;
+    }
+    hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
+    hdr.gen.rtgen_family = family_;
+  } else {
+    switch (type_) {
+    case kMessageTypeLink:
+      EncodeLink(&hdr);
+      break;
+
+    case kMessageTypeAddress:
+      EncodeAddress(&hdr);
+      break;
+
+    case kMessageTypeRoute:
+      EncodeRoute(&hdr);
+      break;
+
+    default:
+      NOTREACHED();
+    }
+  }
+
+  size_t header_length = hdr.hdr.nlmsg_len;
+  ByteString attributes;
+
+  base::hash_map<uint16, ByteString>::iterator attr;
+  for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
+    size_t len = RTA_LENGTH(attr->second.GetLength());
+    hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
+
+    struct rtattr rt_attr = { len, attr->first };
+    ByteString attr_header(reinterpret_cast<unsigned char *>(&rt_attr),
+                           sizeof(rt_attr));
+    attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
+    attributes.Append(attr_header);
+
+    ByteString attr_data(attr->second);
+    attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
+    attributes.Append(attr_data);
+  }
+
+  ByteString packet(reinterpret_cast<unsigned char *>(&hdr), header_length);
+  packet.Append(attributes);
+
+  return packet;
+}
+
+void RTNLMessage::EncodeLink(RTNLHeader *hdr) {
+  hdr->hdr.nlmsg_type = (mode_ == kMessageModeAdd) ? RTM_NEWLINK : RTM_DELLINK;
+  hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
+  hdr->ifi.ifi_family = family_;
+  hdr->ifi.ifi_type = link_status_.type;
+  hdr->ifi.ifi_flags = link_status_.flags;
+  hdr->ifi.ifi_change = link_status_.change;
+}
+
+void RTNLMessage::EncodeAddress(RTNLHeader *hdr) {
+  hdr->hdr.nlmsg_type = (mode_ == kMessageModeAdd) ? RTM_NEWADDR : RTM_DELADDR;
+  hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
+  hdr->ifa.ifa_family = family_;
+  hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
+  hdr->ifa.ifa_flags = address_status_.flags;
+  hdr->ifa.ifa_scope = address_status_.scope;
+  hdr->ifa.ifa_index = interface_index_;
+}
+
+void RTNLMessage::EncodeRoute(RTNLHeader *hdr) {
+  hdr->hdr.nlmsg_type =
+      (mode_ == kMessageModeAdd) ? RTM_NEWROUTE : RTM_DELROUTE;
+  hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
+  hdr->rtm.rtm_family = family_;
+  hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
+  hdr->rtm.rtm_src_len = route_status_.src_prefix;
+  hdr->rtm.rtm_table = route_status_.table;
+  hdr->rtm.rtm_protocol = route_status_.protocol;
+  hdr->rtm.rtm_scope = route_status_.scope;
+  hdr->rtm.rtm_type = route_status_.type;
+  hdr->rtm.rtm_flags = route_status_.flags;
+}
+
+}  // namespace shill
diff --git a/rtnl_message.h b/rtnl_message.h
new file mode 100644
index 0000000..7d56e9f
--- /dev/null
+++ b/rtnl_message.h
@@ -0,0 +1,185 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_RTNL_MESSAGE_
+#define SHILL_RTNL_MESSAGE_
+
+#include <base/basictypes.h>
+#include <base/hash_tables.h>
+#include <base/stl_util-inl.h>
+
+#include "shill/byte_string.h"
+#include "shill/ip_address.h"
+
+struct rtattr;
+
+namespace shill {
+
+struct RTNLHeader;
+
+class RTNLMessage {
+  public:
+    enum MessageType {
+      kMessageTypeUnknown,
+      kMessageTypeLink,
+      kMessageTypeAddress,
+      kMessageTypeRoute
+    };
+
+    enum MessageMode {
+      kMessageModeUnknown,
+      kMessageModeGet,
+      kMessageModeAdd,
+      kMessageModeDelete
+    };
+
+    struct LinkStatus {
+      LinkStatus()
+          : type(0),
+            flags(0),
+            change(0) {}
+      LinkStatus(unsigned int in_type,
+                 unsigned int in_flags,
+                 unsigned int in_change)
+          : type(in_type),
+            flags(in_flags),
+            change(in_change) {}
+      unsigned int type;
+      unsigned int flags;
+      unsigned int change;
+    };
+
+    struct AddressStatus {
+      AddressStatus()
+          : prefix_len(0),
+            flags(0),
+            scope(0) {}
+      AddressStatus(unsigned char prefix_len_in,
+                    unsigned char flags_in,
+                    unsigned char scope_in)
+          : prefix_len(prefix_len_in),
+            flags(flags_in),
+            scope(scope_in) {}
+      unsigned char prefix_len;
+      unsigned char flags;
+      unsigned char scope;
+    };
+
+    struct RouteStatus {
+      RouteStatus()
+          : dst_prefix(0),
+            src_prefix(0),
+            table(0),
+            protocol(0),
+            scope(0),
+            type(0),
+            flags(0) {}
+      RouteStatus(unsigned char dst_prefix_in,
+                  unsigned char src_prefix_in,
+                  unsigned char table_in,
+                  unsigned char protocol_in,
+                  unsigned char scope_in,
+                  unsigned char type_in,
+                  unsigned char flags_in)
+          : dst_prefix(dst_prefix_in),
+            src_prefix(src_prefix_in),
+            table(table_in),
+            protocol(protocol_in),
+            scope(scope_in),
+            type(type_in),
+            flags(flags_in) {}
+      unsigned char dst_prefix;
+      unsigned char src_prefix;
+      unsigned char table;
+      unsigned char protocol;
+      unsigned char scope;
+      unsigned char type;
+      unsigned char flags;
+    };
+
+    // Empty constructor
+    RTNLMessage();
+    // Build an RTNL message from arguments
+    RTNLMessage(MessageType type,
+                MessageMode mode,
+                unsigned int flags,
+                uint32 seq,
+                uint32 pid,
+                int interface_index,
+                IPAddress::Family family);
+
+    // Parse an RTNL message.  Returns true on success.
+    bool Decode(const ByteString &data);
+    // Encode an RTNL message.  Returns empty ByteString on failure.
+    ByteString Encode();
+
+    // Getters and setters
+    MessageType type() { return type_; }
+    MessageMode mode() { return mode_; }
+    uint16 flags() { return flags_; }
+    uint32 seq() { return seq_; }
+    void set_seq(uint32 seq) { seq_ = seq; }
+    uint32 pid() { return pid_; }
+    uint32 interface_index() { return interface_index_; }
+    IPAddress::Family family() { return family_; }
+
+    const LinkStatus &link_status() { return link_status_; }
+    void set_link_status(LinkStatus link_status) { link_status_ = link_status; }
+    const AddressStatus &address_status() { return address_status_; }
+    void set_address_status(AddressStatus address_status) {
+      address_status_ = address_status;
+    }
+    const RouteStatus &route_status() { return route_status_; }
+    void set_route_status(RouteStatus route_status) {
+      route_status_ = route_status;
+    }
+    // GLint hates "unsigned short", and I don't blame it, but that's the
+    // type that's used in the system headers.  Use uint16 instead and hope
+    // that the conversion never ends up truncating on some strange platform.
+    bool HasAttribute(uint16 attr) {
+      return ContainsKey(attributes_, attr);
+    }
+    const ByteString GetAttribute(uint16 attr) {
+      return HasAttribute(attr) ? attributes_[attr] : ByteString(0);
+    }
+    void SetAttribute(uint16 attr, const ByteString &val) {
+      attributes_[attr] = val;
+    }
+
+  private:
+    bool DecodeInternal(const ByteString &msg);
+    bool DecodeLink(const RTNLHeader *hdr,
+                    MessageMode mode,
+                    rtattr **attr_data,
+                    int *attr_length);
+    bool DecodeAddress(const RTNLHeader *hdr,
+                       MessageMode mode,
+                       rtattr **attr_data,
+                       int *attr_length);
+    bool DecodeRoute(const RTNLHeader *hdr,
+                     MessageMode mode,
+                     rtattr **attr_data,
+                     int *attr_length);
+    void EncodeLink(RTNLHeader *hdr);
+    void EncodeAddress(RTNLHeader *hdr);
+    void EncodeRoute(RTNLHeader *hdr);
+
+    MessageType type_;
+    MessageMode mode_;
+    uint16 flags_;
+    uint32 seq_;
+    uint32 pid_;
+    unsigned int interface_index_;
+    IPAddress::Family family_;
+    LinkStatus link_status_;
+    AddressStatus address_status_;
+    RouteStatus route_status_;
+    base::hash_map<uint16, ByteString> attributes_;
+
+    DISALLOW_COPY_AND_ASSIGN(RTNLMessage);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_RTNL_MESSAGE_
diff --git a/rtnl_message_unittest.cc b/rtnl_message_unittest.cc
new file mode 100644
index 0000000..4ba5876
--- /dev/null
+++ b/rtnl_message_unittest.cc
@@ -0,0 +1,604 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <gtest/gtest.h>
+
+#include "shill/byte_string.h"
+#include "shill/ip_address.h"
+#include "shill/rtnl_message.h"
+
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+// These test strings were generated by cutting up the output of the
+// "rtmon" utility into individual strings.  This tool outputs the raw
+// RTNL packet contents sent by the kernel.  The comments above each
+// of these strings is the markup that the "ip monitor" command
+// outputs for each of these RTNL packets.
+
+
+// 2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP
+//     link/ether c0:f8:da:05:03:0b brd ff:ff:ff:ff:ff:ff
+
+const int kNewLinkMessageWlan0InterfaceIndex = 2;
+const unsigned int kNewLinkMessageWlan0InterfaceFlags =
+    IFF_BROADCAST | IFF_MULTICAST | IFF_UP | IFF_LOWER_UP | IFF_RUNNING;
+const unsigned int kNewLinkMessageWlan0InterfaceFlagsChange = 0;
+const uint32 kNewLinkMessageWlan0MTU = 1500;
+const char kNewLinkMessageWlan0MACAddress[] = "\xc0\xf8\xda\x05\x03\x0b";
+const char kNewLinkMessageWlan0InterfaceName[] = "wlan0";
+const char kNewLinkMessageWlan0Qdisc[] = "mq";
+const int kNewLinkMessageWlan0OperState = IF_OPER_UP;
+
+const unsigned char kNewLinkMessageWlan0[] = {
+  0xe0, 0x03, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00,
+  0x53, 0x18, 0x1f, 0x4e, 0xac, 0x77, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x03, 0x00, 0x77, 0x6c, 0x61, 0x6e,
+  0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00,
+  0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00,
+  0xdc, 0x05, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00,
+  0x6d, 0x71, 0x00, 0x00, 0x20, 0x00, 0x0e, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00,
+  0xc0, 0xf8, 0xda, 0x05, 0x03, 0x0b, 0x00, 0x00,
+  0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0x00, 0x00, 0x60, 0x00, 0x07, 0x00,
+  0xa6, 0x65, 0x0f, 0x00, 0xa6, 0x0b, 0x00, 0x00,
+  0xd2, 0x29, 0x61, 0x06, 0xd7, 0x25, 0x07, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x84, 0x53, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x17, 0x00,
+  0xa6, 0x65, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xa6, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xd2, 0x29, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00,
+  0xd7, 0x25, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x84, 0x53, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x30, 0x02, 0x1a, 0x00, 0x6c, 0x00, 0x02, 0x00,
+  0x68, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xc0, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00,
+  0xff, 0xff, 0x00, 0x00, 0x22, 0x05, 0x00, 0x00,
+  0x4f, 0x53, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00,
+  0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00,
+  0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xfc, 0x00, 0x03, 0x00, 0x1f, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x15, 0xe6, 0x04, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x15, 0xe6, 0x04, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x9b, 0x95, 0xb9, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x9b, 0x95, 0xb9, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+// Deleted 8: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN
+//    link/ether 68:7f:74:ba:ef:c7 brd ff:ff:ff:ff:ff:ff
+
+const int kDelLinkMessageEth0InterfaceIndex = 8;
+const unsigned int kDelLinkMessageEth0InterfaceFlags =
+    IFF_BROADCAST | IFF_MULTICAST;
+const unsigned int kDelLinkMessageEth0InterfaceFlagsChange = 0xffffffff;
+const uint32 kDelLinkMessageEth0MTU = 1500;
+const char kDelLinkMessageEth0MACAddress[] = "\x68\x7f\x74\xba\xef\xc7";
+const char kDelLinkMessageEth0InterfacName[] = "eth0";
+const char kDelLinkMessageEth0Qdisc[] = "noop";
+const int kDelLinkMessageEth0OperState = IF_OPER_DOWN;
+
+const unsigned char kDelLinkMessageEth0[] = {
+  0xb8, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x02, 0x10, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0x09, 0x00, 0x03, 0x00, 0x65, 0x74, 0x68, 0x30,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00,
+  0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00,
+  0xdc, 0x05, 0x00, 0x00, 0x09, 0x00, 0x06, 0x00,
+  0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x01, 0x00, 0x68, 0x7f, 0x74, 0xba,
+  0xef, 0xc7, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+  0x60, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0xfa, 0x05, 0x00, 0x00,
+  0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xbc, 0x00, 0x17, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xfa, 0x05, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x34, 0x06, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x15, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1a, 0x00,
+};
+
+// 8: if8    inet 192.168.10.100/24 brd 192.168.10.255 scope global eth0
+
+const int kNewAddrIPV4InterfaceIndex = 8;
+const char kNewAddrIPV4Address[] = "192.168.10.100";
+const int kNewAddrIPV4AddressPrefix = 24;
+const unsigned char kNewAddrIPV4Scope = RT_SCOPE_UNIVERSE;
+
+const unsigned char kNewAddrIPV4[] = {
+  0x3c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x18, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x01, 0x00, 0xc0, 0xa8, 0x0a, 0x64,
+  0x08, 0x00, 0x02, 0x00, 0xc0, 0xa8, 0x0a, 0x64,
+  0x08, 0x00, 0x04, 0x00, 0xc0, 0xa8, 0x0a, 0xff,
+  0x09, 0x00, 0x03, 0x00, 0x65, 0x74, 0x68, 0x30,
+  0x00, 0x00, 0x00, 0x00,
+};
+
+
+// Deleted ff02::1:ffa0:688 via ff02::1:ffa0:688 dev if2  metric 0
+
+const int kDelRouteIPV6InterfaceIndex = 2;
+const char kDelRouteIPV6Address[] = "ff02::1:ffa0:688";
+const int kDelRouteIPV6Metric = 0;
+
+const unsigned char kDelRouteIPV6[] = {
+  0x80, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0a, 0x80, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01,
+  0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00,
+  0xfe, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00,
+  0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0xff, 0xa0, 0x06, 0x88,
+  0x14, 0x00, 0x05, 0x00, 0xff, 0x02, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+  0xff, 0xa0, 0x06, 0x88, 0x08, 0x00, 0x04, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0c, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x5f, 0x0c, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+// default via 192.168.17.254 dev if12  metric 9
+
+const int kAddRouteIPV4InterfaceIndex = 12;
+const char kAddRouteIPV4Address[] = "192.168.17.254";
+const int kAddRouteIPV4Metric = 9;
+
+const unsigned char kAddRouteIPV4[] = {
+  0x3c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00,
+  0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00,
+  0xc0, 0xa8, 0x11, 0xfe, 0x08, 0x00, 0x04, 0x00,
+  0x0c, 0x00, 0x00, 0x00,
+};
+
+
+// This is the same as kAddRouteIPV4 above, except manually corrupted
+// the second to last parameter
+const unsigned char kAddRouteBusted[] = {
+  0x3c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00,
+  0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x28, 0x00, 0x05, 0x00,
+  0xc0, 0xa8, 0x11, 0xfe, 0x08, 0x00, 0x04, 0x00,
+  0x0c, 0x00, 0x00, 0x00,
+};
+
+// 192.168.10.1 dev if8 lladdr 00:14:d1:cd:d5:2c REACHABLE
+const unsigned char kAddNeighborMessage[] = {
+  0x4c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x01, 0x08, 0x00, 0x01, 0x00,
+  0xc0, 0xa8, 0x0a, 0x01, 0x0a, 0x00, 0x02, 0x00,
+  0x00, 0x14, 0xd1, 0xcd, 0xd5, 0x2c, 0x00, 0x00,
+  0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x14, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00,
+};
+
+}  // namespace {}
+
+class RTNLMessageTest : public Test {
+ protected:
+  void TestParseLink(const ByteString &packet,
+                     RTNLMessage::MessageMode mode,
+                     int interface_index,
+                     unsigned int flags,
+                     unsigned int change,
+                     ByteString address,
+                     ByteString name,
+                     uint32 mtu,
+                     ByteString qdisc,
+                     int oper_state) {
+    RTNLMessage msg;
+    EXPECT_TRUE(msg.Decode(packet));
+
+    EXPECT_EQ(RTNLMessage::kMessageTypeLink, msg.type());
+    EXPECT_EQ(mode, msg.mode());
+    EXPECT_EQ(interface_index, msg.interface_index());
+
+    RTNLMessage::LinkStatus status = msg.link_status();
+    EXPECT_EQ(flags, status.flags);
+    EXPECT_EQ(change, status.change);
+
+    EXPECT_TRUE(msg.HasAttribute(IFLA_ADDRESS));
+    EXPECT_EQ(address.GetLength(), msg.GetAttribute(IFLA_ADDRESS).GetLength());
+    EXPECT_TRUE(msg.GetAttribute(IFLA_ADDRESS).Equals(address));
+
+    EXPECT_TRUE(msg.HasAttribute(IFLA_IFNAME));
+    EXPECT_EQ(name.GetLength(), msg.GetAttribute(IFLA_IFNAME).GetLength());
+    EXPECT_TRUE(msg.GetAttribute(IFLA_IFNAME).Equals(name));
+
+    EXPECT_TRUE(msg.HasAttribute(IFLA_MTU));
+    uint32 mtu_val;
+    EXPECT_TRUE(msg.GetAttribute(IFLA_MTU).ConvertToCPUUInt32(&mtu_val));
+    EXPECT_EQ(mtu, mtu_val);
+
+    EXPECT_TRUE(msg.HasAttribute(IFLA_QDISC));
+    EXPECT_EQ(qdisc.GetLength(), msg.GetAttribute(IFLA_QDISC).GetLength());
+    EXPECT_TRUE(msg.GetAttribute(IFLA_QDISC).Equals(qdisc));
+
+    EXPECT_TRUE(msg.HasAttribute(IFLA_OPERSTATE));
+    EXPECT_EQ(oper_state, msg.GetAttribute(IFLA_OPERSTATE).GetConstData()[0]);
+  }
+
+  void TestParseAddress(const ByteString &packet,
+                        RTNLMessage::MessageMode mode,
+                        int interface_index,
+                        const IPAddress &address,
+                        int prefix,
+                        unsigned char scope) {
+    RTNLMessage msg;
+
+    EXPECT_TRUE(msg.Decode(packet));
+    EXPECT_EQ(RTNLMessage::kMessageTypeAddress, msg.type());
+    EXPECT_EQ(mode, msg.mode());
+    EXPECT_EQ(interface_index, msg.interface_index());
+    EXPECT_EQ(address.family(), msg.family());
+
+    RTNLMessage::AddressStatus status = msg.address_status();
+    EXPECT_EQ(prefix, status.prefix_len);
+    EXPECT_EQ(scope, status.scope);
+
+    EXPECT_TRUE(msg.HasAttribute(IFA_LOCAL));
+    EXPECT_EQ(address.GetLength(), msg.GetAttribute(IFA_LOCAL).GetLength());
+    EXPECT_TRUE(
+        IPAddress(address.family(),
+            msg.GetAttribute(IFA_LOCAL)).Equals(address));
+  }
+
+  void TestParseRoute(const ByteString &packet,
+                      RTNLMessage::MessageMode mode,
+                      IPAddress::Family family,
+                      int interface_index,
+                      const IPAddress &dst,
+                      int dst_prefix,
+                      const IPAddress &src,
+                      int src_prefix,
+                      const IPAddress &gateway,
+                      unsigned char table,
+                      int protocol,
+                      unsigned char scope,
+                      unsigned char type,
+                      int metric) {
+    RTNLMessage msg;
+
+    EXPECT_TRUE(msg.Decode(packet));
+    EXPECT_EQ(RTNLMessage::kMessageTypeRoute, msg.type());
+    EXPECT_EQ(0, msg.interface_index());
+    EXPECT_EQ(family, msg.family());
+
+    RTNLMessage::RouteStatus status = msg.route_status();
+    EXPECT_EQ(table, status.table);
+    EXPECT_EQ(protocol, status.protocol);
+    EXPECT_EQ(scope, status.scope);
+    EXPECT_EQ(type, status.type);
+
+    if (!dst.IsDefault()) {
+      EXPECT_TRUE(msg.HasAttribute(RTA_DST));
+      EXPECT_TRUE(IPAddress(family, msg.GetAttribute(RTA_DST)).Equals(dst));
+    }
+
+    if (!src.IsDefault()) {
+      EXPECT_TRUE(msg.HasAttribute(RTA_SRC));
+      EXPECT_TRUE(IPAddress(family, msg.GetAttribute(RTA_SRC)).Equals(src));
+    }
+
+    if (!gateway.IsDefault()) {
+      EXPECT_TRUE(msg.HasAttribute(RTA_GATEWAY));
+      EXPECT_TRUE(IPAddress(family,
+                            msg.GetAttribute(RTA_GATEWAY)).Equals(gateway));
+    }
+
+    if (interface_index >= 0) {
+      EXPECT_TRUE(msg.HasAttribute(RTA_OIF));
+      uint32 int_val;
+      EXPECT_TRUE(msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&int_val));
+      EXPECT_EQ(interface_index, int_val);
+    } else {
+      EXPECT_FALSE(msg.HasAttribute(RTA_OIF));
+    }
+    if (metric >= 0) {
+      EXPECT_TRUE(msg.HasAttribute(RTA_PRIORITY));
+      uint32 metric_val;
+      EXPECT_TRUE(
+          msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric_val));
+      EXPECT_EQ(metric, metric_val);
+    } else {
+      EXPECT_FALSE(msg.HasAttribute(RTA_PRIORITY));
+    }
+  }
+};
+
+TEST_F(RTNLMessageTest, NewLinkWlan0) {
+  TestParseLink(ByteString(kNewLinkMessageWlan0, sizeof(kNewLinkMessageWlan0)),
+                RTNLMessage::kMessageModeAdd,
+                kNewLinkMessageWlan0InterfaceIndex,
+                kNewLinkMessageWlan0InterfaceFlags,
+                kNewLinkMessageWlan0InterfaceFlagsChange,
+                ByteString(kNewLinkMessageWlan0MACAddress, false),
+                ByteString(kNewLinkMessageWlan0InterfaceName, true),
+                kNewLinkMessageWlan0MTU,
+                ByteString(kNewLinkMessageWlan0Qdisc, true),
+                kNewLinkMessageWlan0OperState);
+}
+
+TEST_F(RTNLMessageTest, DelLinkEth0) {
+  TestParseLink(ByteString(kDelLinkMessageEth0, sizeof(kDelLinkMessageEth0)),
+                RTNLMessage::kMessageModeDelete,
+                kDelLinkMessageEth0InterfaceIndex,
+                kDelLinkMessageEth0InterfaceFlags,
+                kDelLinkMessageEth0InterfaceFlagsChange,
+                ByteString(kDelLinkMessageEth0MACAddress, false),
+                ByteString(kDelLinkMessageEth0InterfacName, true),
+                kDelLinkMessageEth0MTU,
+                ByteString(kDelLinkMessageEth0Qdisc, true),
+                kDelLinkMessageEth0OperState);
+}
+
+TEST_F(RTNLMessageTest, NewAddrIPv4) {
+  IPAddress addr(IPAddress::kAddressFamilyIPv4);
+
+  EXPECT_TRUE(addr.SetAddressFromString(kNewAddrIPV4Address));
+  TestParseAddress(ByteString(kNewAddrIPV4, sizeof(kNewAddrIPV4)),
+                   RTNLMessage::kMessageModeAdd,
+                   kNewAddrIPV4InterfaceIndex,
+                   addr,
+                   kNewAddrIPV4AddressPrefix,
+                   kNewAddrIPV4Scope);
+}
+
+TEST_F(RTNLMessageTest, DelRouteIPv6) {
+  IPAddress dst(IPAddress::kAddressFamilyIPv6);
+  IPAddress src(IPAddress::kAddressFamilyIPv6);
+  IPAddress gateway(IPAddress::kAddressFamilyIPv6);
+
+  EXPECT_TRUE(dst.SetAddressFromString(kDelRouteIPV6Address));
+  src.SetAddressToDefault();
+  EXPECT_TRUE(gateway.SetAddressFromString(kDelRouteIPV6Address));
+
+  TestParseRoute(ByteString(kDelRouteIPV6, sizeof(kDelRouteIPV6)),
+                 RTNLMessage::kMessageModeDelete,
+                 IPAddress::kAddressFamilyIPv6,
+                 kDelRouteIPV6InterfaceIndex,
+                 dst,
+                 0,
+                 src,
+                 0,
+                 gateway,
+                 RT_TABLE_MAIN,
+                 RTPROT_UNSPEC,
+                 RT_SCOPE_UNIVERSE,
+                 RTN_UNICAST,
+                 kDelRouteIPV6Metric);
+}
+
+TEST_F(RTNLMessageTest, AddRouteIPv4) {
+  IPAddress dst(IPAddress::kAddressFamilyIPv4);
+  IPAddress src(IPAddress::kAddressFamilyIPv4);
+  IPAddress gateway(IPAddress::kAddressFamilyIPv4);
+
+  dst.SetAddressToDefault();
+  src.SetAddressToDefault();
+  EXPECT_TRUE(gateway.SetAddressFromString(kAddRouteIPV4Address));
+
+  TestParseRoute(ByteString(kAddRouteIPV4, sizeof(kAddRouteIPV4)),
+                 RTNLMessage::kMessageModeAdd,
+                 IPAddress::kAddressFamilyIPv4,
+                 kAddRouteIPV4InterfaceIndex,
+                 dst,
+                 0,
+                 src,
+                 0,
+                 gateway,
+                 RT_TABLE_MAIN,
+                 RTPROT_BOOT,
+                 RT_SCOPE_UNIVERSE,
+                 RTN_UNICAST,
+                 kAddRouteIPV4Metric);
+}
+
+TEST_F(RTNLMessageTest, AddRouteBusted) {
+  // RTNLMessage should list parse errors as kMessageUnknown
+  RTNLMessage msg;
+  EXPECT_FALSE(msg.Decode(
+      ByteString(kAddRouteBusted, sizeof(kAddRouteBusted))));
+}
+
+TEST_F(RTNLMessageTest, AddNeighbor) {
+  // RTNLMessage doesn't parse Add-Neighbor messages -- ensure this fails
+  RTNLMessage msg;
+  EXPECT_FALSE(msg.Decode(
+      ByteString(kAddNeighborMessage, sizeof(kAddNeighborMessage))));
+}
+
+TEST_F(RTNLMessageTest, Encode) {
+  RTNLMessage msg(RTNLMessage::kMessageTypeRoute,
+                  RTNLMessage::kMessageModeAdd,
+                  0, 1, 2, 0,
+                  IPAddress::kAddressFamilyIPv4);
+  IPAddress dst(IPAddress::kAddressFamilyIPv4);
+  IPAddress src(IPAddress::kAddressFamilyIPv4);
+  IPAddress gateway(IPAddress::kAddressFamilyIPv4);
+
+  dst.SetAddressToDefault();
+  src.SetAddressToDefault();
+  EXPECT_TRUE(gateway.SetAddressFromString("192.168.0.1"));
+
+  msg.set_route_status(RTNLMessage::RouteStatus(
+      0, 0, RT_TABLE_MAIN, RTPROT_BOOT, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0));
+  msg.SetAttribute(RTA_DST, dst.address());
+  msg.SetAttribute(RTA_SRC, src.address());
+  msg.SetAttribute(RTA_GATEWAY, gateway.address());
+  msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(12));
+  msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(13));
+
+
+  TestParseRoute(msg.Encode(),
+                 RTNLMessage::kMessageModeAdd,
+                 IPAddress::kAddressFamilyIPv4,
+                 12,
+                 dst,
+                 0,
+                 src,
+                 0,
+                 gateway,
+                 RT_TABLE_MAIN,
+                 RTPROT_BOOT,
+                 RT_SCOPE_UNIVERSE,
+                 RTN_UNICAST,
+                 13);
+}
+
+}  // namespace shill