def nla_parse(msg, l, mtype, stream, idx): """ parses attributes in stream, putting them in msg :param msg: current message :param l: total length of message :param mtype: message content :param stream: byte stream :param idx: current index in stream """ # get policy family NOTE: cheating here, we know it's either generic or nl80211 pol = 'ctrl_attr' if mtype == nlh.NETLINK_GENERIC else 'nl80211_attr' attrlen = nlh.NLATTRHDRLEN # pull out these to avoid attrhdr = nlh.nl_nlattrhdr # doing so in each iteration # eat the stream until the end while idx < l: a = atype = alen = None # shut pycharm up about unitialized variable try: alen, atype = struct.unpack_from(attrhdr, stream, idx) # get length, type idx += attrlen # move to attr start alen -= attrlen # attr length (w/ padding) a = stream[idx:idx + alen] # attr value dt = nla_datatype(pol, atype) # attr datatype # Note: we use unpack_from which will ignore the null bytes in numeric # datatypes & for strings, strip trailing null bytes # dt == nlh.NLA_UNSPEC: ignore if _PY3_ and (dt == nlh.NLA_STRING or dt == nlh.NLA_UNSPEC): # python 3 returns a bytes object, convert to string try: a = a.decode('ascii') except UnicodeDecodeError: pass # F**k You Python 3 if dt == nlh.NLA_STRING: a = _nla_strip_(a) elif dt == nlh.NLA_U8: a = struct.unpack_from("B", a, 0)[0] elif dt == nlh.NLA_U16: a = struct.unpack_from("H", a, 0)[0] elif dt == nlh.NLA_U32: a = struct.unpack_from("I", a, 0)[0] elif dt == nlh.NLA_U64: a = struct.unpack_from("Q", a, 0)[0] elif dt == nlh.NLA_FLAG: a = '' # flags should be 0 size elif dt == nlh.NLA_MSECS: a = struct.unpack_from("Q", a, 0)[0] elif dt == nlh.NLA_SET_U8: a = nla_parse_set(a, nlh.NLA_U8) elif dt == nlh.NLA_SET_U16: a = nla_parse_set(a, nlh.NLA_U16) elif dt == nlh.NLA_SET_U32: a = nla_parse_set(a, nlh.NLA_U32) elif dt == nlh.NLA_SET_U64: a = nla_parse_set(a, nlh.NLA_U64) elif dt == nlh.NLA_NESTED: a = nla_parse_nested(a) nla_put(msg, a, atype, dt) except struct.error: # append as Error, stripping null bytes nla_put(msg, _nla_strip_(a), atype, nlh.NLA_ERROR) except error as e: if e.errno == errno.EINVAL: # a nested or set failed to parse correctly nla_put(msg, _nla_strip_(a), atype, nlh.NLA_ERROR) else: raise except MemoryError as e: # hopefully don't get here raise error( -1, "Attr type {0} of pol {1} failed: {2}".format(atype, pol, e)) idx = nlh.NLMSG_ALIGN(idx + alen) # move index to next attr
def nla_parse_nested(nested): """ :param nested: the nested attribute with attribute header removed :returns: list of tuples (index, data) where index is the index into an enum structure and attribute is the packed attribute) Callers must parse these themselves - see below for details From libnl (Thomas Graf) When nesting attributes, the nested attributes are included as payload of a container attribute. Attributes are nested by surrounding them with calls to nla_nest_start() and nla_nest_end(). <-------- nla_attr_size(payload) ---------> +------------------+-----+------------------+-----+ | Attribute Header | Pad | Payload | Pad | +------------------+-----+------------------+-----+ Nested attributes (after removing the Attribute header) should have the form: +--------+--------+-----+ | Length |Payload | Pad | +--------+--------+-----+ <-- 2 --><- var ->< var > where: Length (u16) is the length of the nested attribute (excluding padding affixed to the end to align the attribute). The size of padding is determined by NLMSG_ALIGN Payload has the format: +-------+--------+-----+ | Index | Data | Pad | +-------+--------+-----+ <-- 2 --><- var ->< var > where index is the index into an enum structure as determined by the attribute type of the nested attribute which is found in the Attribute Header """ ns = [] idx = 0 l = len(nested) while idx < l: # first two bytes define length, including these bytes, length does not # include pad byte(s) affixed to end for proper alignment alen = struct.unpack_from("H", nested, idx)[0] if alen == 0: raise EnvironmentError(errno.EINVAL, "Invalid nesting") # ns.append(nested[idx+2:idx+alen]) # don't include the length bytes nattr = nested[idx + 2 : idx + alen] ns.append((struct.unpack_from("H", nattr, 0)[0], nattr[2:])) idx += nlh.NLMSG_ALIGN(alen) return ns