Support decoding route attributes, cacheinfo, uid.

Change-Id: Id7e84ba961a2dbb903e92cdd000017caee54299d
diff --git a/net/test/iproute.py b/net/test/iproute.py
index 3fc2092..104a440 100644
--- a/net/test/iproute.py
+++ b/net/test/iproute.py
@@ -71,14 +71,21 @@
 RTA_GATEWAY = 5
 RTA_PRIORITY = 6
 RTA_PREFSRC = 7
+RTA_METRICS = 8
+RTA_CACHEINFO = 12
 RTA_TABLE = 15
 RTA_MARK = 16
 EXPERIMENTAL_RTA_UID = 18
 
+# Route metric attributes.
+RTAX_MTU = 2
+
 # Data structure formats.
 RTMsg = cstruct.Struct(
     "RTMsg", "=BBBBBBBBI",
     "family dst_len src_len tos table protocol scope type flags")
+RTACacheinfo = cstruct.Struct(
+    "RTACacheinfo", "=IIiII", "clntref lastuse expires error used")
 
 
 ### Interface address constants. See include/uapi/linux/if_addr.h.
@@ -126,42 +133,6 @@
   return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4]
 
 
-def Decode(command, family, nla_type, nla_data):
-  """Decodes netlink attributes to Python types.
-
-  Values for which the code knows the type (e.g., the fwmark ID in a
-  RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
-  of unknown type are returned as raw byte strings.
-
-  Args:
-    command: An integer, the rtnetlink command being carried out. This is used
-      to interpret the attributes. For example, for an RTM_NEWROUTE command,
-      attribute type 3 is the incoming interface and is an integer, but for a
-      RTM_NEWRULE command, attribute type 3 is the incoming interface name
-      and is a string.
-    family: The address family. Used to convert IP addresses into strings.
-    nla_type: An integer, then netlink attribute type.
-    nla_data: A byte string, the netlink attribute data.
-
-  Returns:
-    A Python object. (Currently an integer, a string, or a raw byte string.)
-  """
-  if CommandSubject(command) == "RULE":
-    if nla_type in [FRA_PRIORITY, FRA_FWMARK, FRA_TABLE,
-                    EXPERIMENTAL_FRA_UID_START, EXPERIMENTAL_FRA_UID_END]:
-      return struct.unpack("=I", nla_data)[0]
-    elif nla_type in [FRA_OIFNAME]:
-      return nla_data.strip("\x00")
-  elif CommandSubject(command) == "ROUTE":
-    if nla_type in [RTA_DST, RTA_SRC, RTA_GATEWAY, RTA_PREFSRC]:
-      return socket.inet_ntop(family, nla_data)
-    elif nla_type in [RTA_OIF, RTA_PRIORITY, RTA_TABLE]:
-      return struct.unpack("=I", nla_data)[0]
-
-  # Don't know what this is.
-  return nla_data
-
-
 def PaddedLength(length):
   # TODO: This padding is probably overly simplistic.
   return NLA_ALIGNTO * ((length / NLA_ALIGNTO) + (length % NLA_ALIGNTO != 0))
@@ -191,6 +162,53 @@
   def _NlAttrIPAddress(self, nla_type, family, address):
     return self._NlAttr(nla_type, socket.inet_pton(family, address))
 
+  def _Decode(self, command, family, nla_type, nla_data):
+    """Decodes netlink attributes to Python types.
+
+    Values for which the code knows the type (e.g., the fwmark ID in a
+    RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
+    of unknown type are returned as raw byte strings.
+
+    Args:
+      command: An integer.
+        - If positive, the number of the rtnetlink command being carried out.
+          This is used to interpret the attributes. For example, for an
+          RTM_NEWROUTE command, attribute type 3 is the incoming interface and
+          is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
+          incoming interface name and is a string.
+        - If negative, one of the following (negative) values:
+          - RTA_METRICS: Interpret as nested route metrics.
+      family: The address family. Used to convert IP addresses into strings.
+      nla_type: An integer, then netlink attribute type.
+      nla_data: A byte string, the netlink attribute data.
+
+    Returns:
+      A Python object. Might be an integer, a string, a raw byte string, a
+      nested list of attributes (e.g., for RTA_METRICS), a cstruct.Struct
+      (e.g., RTACacheinfo), etc.
+    """
+    if command == -RTA_METRICS:
+      if nla_type == RTAX_MTU:
+        return struct.unpack("=I", nla_data)[0]
+    elif CommandSubject(command) == "RULE":
+      if nla_type in [FRA_PRIORITY, FRA_FWMARK, FRA_TABLE,
+                      EXPERIMENTAL_FRA_UID_START, EXPERIMENTAL_FRA_UID_END]:
+        return struct.unpack("=I", nla_data)[0]
+      elif nla_type in [FRA_OIFNAME]:
+        return nla_data.strip("\x00")
+    elif CommandSubject(command) == "ROUTE":
+      if nla_type in [RTA_DST, RTA_SRC, RTA_GATEWAY, RTA_PREFSRC]:
+        return socket.inet_ntop(family, nla_data)
+      elif nla_type in [RTA_OIF, RTA_PRIORITY, RTA_TABLE, EXPERIMENTAL_RTA_UID]:
+        return struct.unpack("=I", nla_data)[0]
+      elif nla_type == RTA_METRICS:
+        return self._ParseAttributes(-RTA_METRICS, family, nla_data)
+      elif nla_type == RTA_CACHEINFO:
+        return RTACacheinfo(nla_data)
+
+    # Don't know what this is.
+    return nla_data
+
   def _ParseAttributes(self, command, family, data):
     """Parses and decodes netlink attributes.
 
@@ -219,7 +237,7 @@
       nla_data, data = data[:datalen], data[padded_len:]
 
       # If it's an attribute we know about, try to decode it.
-      nla_data = Decode(command, family, nla.nla_type, nla_data)
+      nla_data = self._Decode(command, family, nla.nla_type, nla_data)
 
       # We only support unique attributes for now.
       if nla.nla_type in attributes: