From ad7a79fd6a78760e95e01336a6ab870d20bbcf4d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 21 Nov 2022 17:43:38 +0900 Subject: Better/more explicit support for nested attributes. Currently, parsing nested attributes is super hacky, relying on passing a negative command to the method that parses attributes. Instead, reuse the currently-unused "nested" parameter, changing it from an int representing the current level of nesting to a list containing all the attributes that the current attributes are nested under. Test: python3 iproute.py Change-Id: Id80150e766235e73ccaa72dce9ca4297fa43dcd3 --- net/test/genetlink.py | 3 ++- net/test/iproute.py | 27 +++++++++++---------------- net/test/netlink.py | 11 ++++++----- net/test/sock_diag.py | 2 +- net/test/tcp_metrics.py | 2 +- net/test/xfrm.py | 2 +- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/net/test/genetlink.py b/net/test/genetlink.py index 6234677..aa46726 100755 --- a/net/test/genetlink.py +++ b/net/test/genetlink.py @@ -75,6 +75,7 @@ class GenericNetlinkControl(GenericNetlink): def _DecodeOps(self, data): ops = [] Op = collections.namedtuple("Op", ["id", "flags"]) + # TODO: call _ParseAttributes on the nested data instead of manual parsing. while data: # Skip the nest marker. datalen, index, data = data[:2], data[2:4], data[4:] @@ -92,7 +93,7 @@ class GenericNetlinkControl(GenericNetlink): ops.append(Op(op_id, op_flags)) return ops - def _Decode(self, command, msg, nla_type, nla_data): + def _Decode(self, command, msg, nla_type, nla_data, nested): """Decodes generic netlink control attributes to human-readable format.""" name = self._GetConstantName(__name__, nla_type, "CTRL_ATTR_") diff --git a/net/test/iproute.py b/net/test/iproute.py index 313efc9..4483d93 100644 --- a/net/test/iproute.py +++ b/net/test/iproute.py @@ -264,7 +264,7 @@ class IPRoute(netlink.NetlinkSocket): def _GetConstantName(self, value, prefix): return super(IPRoute, self)._GetConstantName(__name__, value, prefix) - def _Decode(self, command, msg, nla_type, nla_data, nested=0): + def _Decode(self, command, msg, nla_type, nla_data, nested): """Decodes netlink attributes to Python types. Values for which the code knows the type (e.g., the fwmark ID in a @@ -278,13 +278,11 @@ class IPRoute(netlink.NetlinkSocket): 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. - - IFLA_LINKINFO: Nested interface information. 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. - nested: An integer, how deep we're currently nested. + nested: A list, outermost first, of each of the attributes the NLAttrs are + nested inside. Empty for non-nested attributes. Returns: A tuple (name, data): @@ -295,11 +293,12 @@ class IPRoute(netlink.NetlinkSocket): (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it will be the raw byte string. """ - if command == -RTA_METRICS: + lastnested = nested[-1] if nested else None + if lastnested == "RTA_METRICS": name = self._GetConstantName(nla_type, "RTAX_") - elif command == -IFLA_LINKINFO: + elif lastnested == "IFLA_LINKINFO": name = self._GetConstantName(nla_type, "IFLA_INFO_") - elif command == -IFLA_INFO_DATA: + elif lastnested == "IFLA_INFO_DATA": name = self._GetConstantName(nla_type, "IFLA_VTI_") elif CommandSubject(command) == "ADDR": name = self._GetConstantName(nla_type, "IFA_") @@ -335,12 +334,8 @@ class IPRoute(netlink.NetlinkSocket): elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC", "IFA_LABEL", "IFLA_INFO_KIND"]: data = nla_data.strip(b"\x00") - elif name == "RTA_METRICS": - data = self._ParseAttributes(-RTA_METRICS, None, nla_data, nested + 1) - elif name == "IFLA_LINKINFO": - data = self._ParseAttributes(-IFLA_LINKINFO, None, nla_data, nested + 1) - elif name == "IFLA_INFO_DATA": - data = self._ParseAttributes(-IFLA_INFO_DATA, None, nla_data) + elif name in ["RTA_METRICS", "IFLA_LINKINFO", "IFLA_INFO_DATA"]: + data = self._ParseAttributes(command, None, nla_data, nested + [name]) elif name == "RTA_CACHEINFO": data = RTACacheinfo(nla_data) elif name == "IFA_CACHEINFO": @@ -694,13 +689,13 @@ class IPRoute(netlink.NetlinkSocket): def GetIfaceStats(self, dev_name): """Returns an RtnlLinkStats64 stats object for the specified interface.""" _, attrs = self.GetIfinfo(dev_name) - attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs) + attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, []) return attrs["IFLA_STATS64"] def GetIfinfoData(self, dev_name): """Returns an IFLA_INFO_DATA dict object for the specified interface.""" _, attrs = self.GetIfinfo(dev_name) - attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs) + attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, []) return attrs["IFLA_LINKINFO"]["IFLA_INFO_DATA"] def GetRxTxPackets(self, dev_name): diff --git a/net/test/netlink.py b/net/test/netlink.py index 9821091..ae3b885 100644 --- a/net/test/netlink.py +++ b/net/test/netlink.py @@ -118,7 +118,7 @@ class NetlinkSocket(object): return name return value - def _Decode(self, command, msg, nla_type, nla_data): + def _Decode(self, command, msg, nla_type, nla_data, nested): """No-op, nonspecific version of decode.""" return nla_type, nla_data @@ -133,7 +133,7 @@ class NetlinkSocket(object): return nla, nla_data, data - def _ParseAttributes(self, command, msg, data, nested=0): + def _ParseAttributes(self, command, msg, data, nested): """Parses and decodes netlink attributes. Takes a block of NLAttr data structures, decodes them using Decode, and @@ -143,7 +143,8 @@ class NetlinkSocket(object): command: An integer, the rtnetlink command being carried out. msg: A Struct, the type of the data after the netlink header. data: A byte string containing a sequence of NLAttr data structures. - nested: An integer, how deep we're currently nested. + nested: A list, outermost first, of each of the attributes the NLAttrs are + nested inside. Empty for non-nested attributes. Returns: A dictionary mapping attribute types (integers) to decoded values. @@ -156,7 +157,7 @@ class NetlinkSocket(object): nla, nla_data, data = self._ReadNlAttr(data) # If it's an attribute we know about, try to decode it. - nla_name, nla_data = self._Decode(command, msg, nla.nla_type, nla_data) + nla_name, nla_data = self._Decode(command, msg, nla.nla_type, nla_data, nested) if nla_name in attributes and nla_name not in DUP_ATTRS_OK: raise ValueError("Duplicate attribute %s" % nla_name) @@ -241,7 +242,7 @@ class NetlinkSocket(object): # Parse the attributes in the nlmsg. attrlen = nlmsghdr.length - len(nlmsghdr) - len(nlmsg) - attributes = self._ParseAttributes(nlmsghdr.type, nlmsg, data[:attrlen]) + attributes = self._ParseAttributes(nlmsghdr.type, nlmsg, data[:attrlen], []) data = data[attrlen:] return (nlmsg, attributes), data diff --git a/net/test/sock_diag.py b/net/test/sock_diag.py index 30752ba..924d652 100755 --- a/net/test/sock_diag.py +++ b/net/test/sock_diag.py @@ -117,7 +117,7 @@ class SockDiag(netlink.NetlinkSocket): def __init__(self): super(SockDiag, self).__init__(netlink.NETLINK_SOCK_DIAG) - def _Decode(self, command, msg, nla_type, nla_data): + def _Decode(self, command, msg, nla_type, nla_data, nested): """Decodes netlink attributes to Python types.""" if msg.family == AF_INET or msg.family == AF_INET6: if isinstance(msg, InetDiagReqV2): diff --git a/net/test/tcp_metrics.py b/net/test/tcp_metrics.py index c4e3d69..1076a55 100755 --- a/net/test/tcp_metrics.py +++ b/net/test/tcp_metrics.py @@ -62,7 +62,7 @@ class TcpMetrics(genetlink.GenericNetlink): ctrl = genetlink.GenericNetlinkControl() self.family = ctrl.GetFamily(TCP_METRICS_GENL_NAME) - def _Decode(self, command, msg, nla_type, nla_data): + def _Decode(self, command, msg, nla_type, nla_data, nested): """Decodes TCP metrics netlink attributes to human-readable format.""" name = self._GetConstantName(__name__, nla_type, "TCP_METRICS_ATTR_") diff --git a/net/test/xfrm.py b/net/test/xfrm.py index 3cd1af9..d1e1871 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -368,7 +368,7 @@ class Xfrm(netlink.NetlinkSocket): else: print("%s" % cmdname) - def _Decode(self, command, unused_msg, nla_type, nla_data): + def _Decode(self, command, unused_msg, nla_type, nla_data, nested): """Decodes netlink attributes to Python types.""" name = self._GetConstantName(nla_type, "XFRMA_") -- cgit v1.2.3