137d9df22SJakub Kicinski# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 24e4480e8SJakub Kicinski 37c2435efSDonald Hunterfrom collections import namedtuple 44e4480e8SJakub Kicinskiimport functools 54e4480e8SJakub Kicinskiimport os 64e4480e8SJakub Kicinskiimport random 74e4480e8SJakub Kicinskiimport socket 84e4480e8SJakub Kicinskiimport struct 97c2435efSDonald Hunterfrom struct import Struct 104e4480e8SJakub Kicinskiimport yaml 11d8eea68dSDonald Hunterimport ipaddress 12d8eea68dSDonald Hunterimport uuid 134e4480e8SJakub Kicinski 1430a5c6c8SJakub Kicinskifrom .nlspec import SpecFamily 1530a5c6c8SJakub Kicinski 164e4480e8SJakub Kicinski# 174e4480e8SJakub Kicinski# Generic Netlink code which should really be in some library, but I can't quickly find one. 184e4480e8SJakub Kicinski# 194e4480e8SJakub Kicinski 204e4480e8SJakub Kicinski 214e4480e8SJakub Kicinskiclass Netlink: 224e4480e8SJakub Kicinski # Netlink socket 234e4480e8SJakub Kicinski SOL_NETLINK = 270 244e4480e8SJakub Kicinski 254e4480e8SJakub Kicinski NETLINK_ADD_MEMBERSHIP = 1 264e4480e8SJakub Kicinski NETLINK_CAP_ACK = 10 274e4480e8SJakub Kicinski NETLINK_EXT_ACK = 11 28e46dd903SDonald Hunter NETLINK_GET_STRICT_CHK = 12 294e4480e8SJakub Kicinski 304e4480e8SJakub Kicinski # Netlink message 314e4480e8SJakub Kicinski NLMSG_ERROR = 2 324e4480e8SJakub Kicinski NLMSG_DONE = 3 334e4480e8SJakub Kicinski 344e4480e8SJakub Kicinski NLM_F_REQUEST = 1 354e4480e8SJakub Kicinski NLM_F_ACK = 4 364e4480e8SJakub Kicinski NLM_F_ROOT = 0x100 374e4480e8SJakub Kicinski NLM_F_MATCH = 0x200 381768d8a7SDonald Hunter 391768d8a7SDonald Hunter NLM_F_REPLACE = 0x100 401768d8a7SDonald Hunter NLM_F_EXCL = 0x200 411768d8a7SDonald Hunter NLM_F_CREATE = 0x400 424e4480e8SJakub Kicinski NLM_F_APPEND = 0x800 434e4480e8SJakub Kicinski 444e4480e8SJakub Kicinski NLM_F_CAPPED = 0x100 454e4480e8SJakub Kicinski NLM_F_ACK_TLVS = 0x200 464e4480e8SJakub Kicinski 474e4480e8SJakub Kicinski NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH 484e4480e8SJakub Kicinski 494e4480e8SJakub Kicinski NLA_F_NESTED = 0x8000 504e4480e8SJakub Kicinski NLA_F_NET_BYTEORDER = 0x4000 514e4480e8SJakub Kicinski 524e4480e8SJakub Kicinski NLA_TYPE_MASK = NLA_F_NESTED | NLA_F_NET_BYTEORDER 534e4480e8SJakub Kicinski 544e4480e8SJakub Kicinski # Genetlink defines 554e4480e8SJakub Kicinski NETLINK_GENERIC = 16 564e4480e8SJakub Kicinski 574e4480e8SJakub Kicinski GENL_ID_CTRL = 0x10 584e4480e8SJakub Kicinski 594e4480e8SJakub Kicinski # nlctrl 604e4480e8SJakub Kicinski CTRL_CMD_GETFAMILY = 3 614e4480e8SJakub Kicinski 624e4480e8SJakub Kicinski CTRL_ATTR_FAMILY_ID = 1 634e4480e8SJakub Kicinski CTRL_ATTR_FAMILY_NAME = 2 644e4480e8SJakub Kicinski CTRL_ATTR_MAXATTR = 5 654e4480e8SJakub Kicinski CTRL_ATTR_MCAST_GROUPS = 7 664e4480e8SJakub Kicinski 674e4480e8SJakub Kicinski CTRL_ATTR_MCAST_GRP_NAME = 1 684e4480e8SJakub Kicinski CTRL_ATTR_MCAST_GRP_ID = 2 694e4480e8SJakub Kicinski 704e4480e8SJakub Kicinski # Extack types 714e4480e8SJakub Kicinski NLMSGERR_ATTR_MSG = 1 724e4480e8SJakub Kicinski NLMSGERR_ATTR_OFFS = 2 734e4480e8SJakub Kicinski NLMSGERR_ATTR_COOKIE = 3 744e4480e8SJakub Kicinski NLMSGERR_ATTR_POLICY = 4 754e4480e8SJakub Kicinski NLMSGERR_ATTR_MISS_TYPE = 5 764e4480e8SJakub Kicinski NLMSGERR_ATTR_MISS_NEST = 6 774e4480e8SJakub Kicinski 784e4480e8SJakub Kicinski 7948993e22SStanislav Fomichevclass NlError(Exception): 8048993e22SStanislav Fomichev def __init__(self, nl_msg): 8148993e22SStanislav Fomichev self.nl_msg = nl_msg 8248993e22SStanislav Fomichev 8348993e22SStanislav Fomichev def __str__(self): 8448993e22SStanislav Fomichev return f"Netlink error: {os.strerror(-self.nl_msg.error)}\n{self.nl_msg}" 8548993e22SStanislav Fomichev 8648993e22SStanislav Fomichev 874e4480e8SJakub Kicinskiclass NlAttr: 887c2435efSDonald Hunter ScalarFormat = namedtuple('ScalarFormat', ['native', 'big', 'little']) 897c2435efSDonald Hunter type_formats = { 907c2435efSDonald Hunter 'u8' : ScalarFormat(Struct('B'), Struct("B"), Struct("B")), 917c2435efSDonald Hunter 's8' : ScalarFormat(Struct('b'), Struct("b"), Struct("b")), 927c2435efSDonald Hunter 'u16': ScalarFormat(Struct('H'), Struct(">H"), Struct("<H")), 937c2435efSDonald Hunter 's16': ScalarFormat(Struct('h'), Struct(">h"), Struct("<h")), 947c2435efSDonald Hunter 'u32': ScalarFormat(Struct('I'), Struct(">I"), Struct("<I")), 957c2435efSDonald Hunter 's32': ScalarFormat(Struct('i'), Struct(">i"), Struct("<i")), 967c2435efSDonald Hunter 'u64': ScalarFormat(Struct('Q'), Struct(">Q"), Struct("<Q")), 977c2435efSDonald Hunter 's64': ScalarFormat(Struct('q'), Struct(">q"), Struct("<q")) 987c2435efSDonald Hunter } 99b423c3c8SDonald Hunter 1004e4480e8SJakub Kicinski def __init__(self, raw, offset): 1014e4480e8SJakub Kicinski self._len, self._type = struct.unpack("HH", raw[offset:offset + 4]) 1024e4480e8SJakub Kicinski self.type = self._type & ~Netlink.NLA_TYPE_MASK 1034e4480e8SJakub Kicinski self.payload_len = self._len 1044e4480e8SJakub Kicinski self.full_len = (self.payload_len + 3) & ~3 1054e4480e8SJakub Kicinski self.raw = raw[offset + 4:offset + self.payload_len] 1064e4480e8SJakub Kicinski 1077c2435efSDonald Hunter @classmethod 1087c2435efSDonald Hunter def get_format(cls, attr_type, byte_order=None): 1097c2435efSDonald Hunter format = cls.type_formats[attr_type] 1109f7cc57fSStanislav Fomichev if byte_order: 1117c2435efSDonald Hunter return format.big if byte_order == "big-endian" \ 1127c2435efSDonald Hunter else format.little 1137c2435efSDonald Hunter return format.native 1149f7cc57fSStanislav Fomichev 115d8eea68dSDonald Hunter @classmethod 116d8eea68dSDonald Hunter def formatted_string(cls, raw, display_hint): 117d8eea68dSDonald Hunter if display_hint == 'mac': 118d8eea68dSDonald Hunter formatted = ':'.join('%02x' % b for b in raw) 119d8eea68dSDonald Hunter elif display_hint == 'hex': 120d8eea68dSDonald Hunter formatted = bytes.hex(raw, ' ') 121d8eea68dSDonald Hunter elif display_hint in [ 'ipv4', 'ipv6' ]: 122d8eea68dSDonald Hunter formatted = format(ipaddress.ip_address(raw)) 123d8eea68dSDonald Hunter elif display_hint == 'uuid': 124d8eea68dSDonald Hunter formatted = str(uuid.UUID(bytes=raw)) 125d8eea68dSDonald Hunter else: 126d8eea68dSDonald Hunter formatted = raw 127d8eea68dSDonald Hunter return formatted 128d8eea68dSDonald Hunter 1297c2435efSDonald Hunter def as_scalar(self, attr_type, byte_order=None): 1307c2435efSDonald Hunter format = self.get_format(attr_type, byte_order) 1317c2435efSDonald Hunter return format.unpack(self.raw)[0] 1324e4480e8SJakub Kicinski 1334e4480e8SJakub Kicinski def as_strz(self): 1344e4480e8SJakub Kicinski return self.raw.decode('ascii')[:-1] 1354e4480e8SJakub Kicinski 1364e4480e8SJakub Kicinski def as_bin(self): 1374e4480e8SJakub Kicinski return self.raw 1384e4480e8SJakub Kicinski 139b423c3c8SDonald Hunter def as_c_array(self, type): 1407c2435efSDonald Hunter format = self.get_format(type) 1417c2435efSDonald Hunter return [ x[0] for x in format.iter_unpack(self.raw) ] 142b423c3c8SDonald Hunter 14326071913SDonald Hunter def as_struct(self, members): 14426071913SDonald Hunter value = dict() 14526071913SDonald Hunter offset = 0 14626071913SDonald Hunter for m in members: 14726071913SDonald Hunter # TODO: handle non-scalar members 148d8eea68dSDonald Hunter if m.type == 'binary': 149d8eea68dSDonald Hunter decoded = self.raw[offset:offset+m['len']] 150d8eea68dSDonald Hunter offset += m['len'] 151d8eea68dSDonald Hunter elif m.type in NlAttr.type_formats: 152bddd2e56SDonald Hunter format = self.get_format(m.type, m.byte_order) 153d8eea68dSDonald Hunter [ decoded ] = format.unpack_from(self.raw, offset) 1547c2435efSDonald Hunter offset += format.size 155d8eea68dSDonald Hunter if m.display_hint: 156d8eea68dSDonald Hunter decoded = self.formatted_string(decoded, m.display_hint) 157d8eea68dSDonald Hunter value[m.name] = decoded 15826071913SDonald Hunter return value 15926071913SDonald Hunter 1604e4480e8SJakub Kicinski def __repr__(self): 1614e4480e8SJakub Kicinski return f"[type:{self.type} len:{self._len}] {self.raw}" 1624e4480e8SJakub Kicinski 1634e4480e8SJakub Kicinski 1644e4480e8SJakub Kicinskiclass NlAttrs: 1654e4480e8SJakub Kicinski def __init__(self, msg): 1664e4480e8SJakub Kicinski self.attrs = [] 1674e4480e8SJakub Kicinski 1684e4480e8SJakub Kicinski offset = 0 1694e4480e8SJakub Kicinski while offset < len(msg): 1704e4480e8SJakub Kicinski attr = NlAttr(msg, offset) 1714e4480e8SJakub Kicinski offset += attr.full_len 1724e4480e8SJakub Kicinski self.attrs.append(attr) 1734e4480e8SJakub Kicinski 1744e4480e8SJakub Kicinski def __iter__(self): 1754e4480e8SJakub Kicinski yield from self.attrs 1764e4480e8SJakub Kicinski 1774e4480e8SJakub Kicinski def __repr__(self): 1784e4480e8SJakub Kicinski msg = '' 1794e4480e8SJakub Kicinski for a in self.attrs: 1804e4480e8SJakub Kicinski if msg: 1814e4480e8SJakub Kicinski msg += '\n' 1824e4480e8SJakub Kicinski msg += repr(a) 1834e4480e8SJakub Kicinski return msg 1844e4480e8SJakub Kicinski 1854e4480e8SJakub Kicinski 1864e4480e8SJakub Kicinskiclass NlMsg: 1874e4480e8SJakub Kicinski def __init__(self, msg, offset, attr_space=None): 1884e4480e8SJakub Kicinski self.hdr = msg[offset:offset + 16] 1894e4480e8SJakub Kicinski 1904e4480e8SJakub Kicinski self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \ 1914e4480e8SJakub Kicinski struct.unpack("IHHII", self.hdr) 1924e4480e8SJakub Kicinski 1934e4480e8SJakub Kicinski self.raw = msg[offset + 16:offset + self.nl_len] 1944e4480e8SJakub Kicinski 1954e4480e8SJakub Kicinski self.error = 0 1964e4480e8SJakub Kicinski self.done = 0 1974e4480e8SJakub Kicinski 1984e4480e8SJakub Kicinski extack_off = None 1994e4480e8SJakub Kicinski if self.nl_type == Netlink.NLMSG_ERROR: 2004e4480e8SJakub Kicinski self.error = struct.unpack("i", self.raw[0:4])[0] 2014e4480e8SJakub Kicinski self.done = 1 2024e4480e8SJakub Kicinski extack_off = 20 2034e4480e8SJakub Kicinski elif self.nl_type == Netlink.NLMSG_DONE: 2046a65f015SJakub Kicinski self.error = struct.unpack("i", self.raw[0:4])[0] 2054e4480e8SJakub Kicinski self.done = 1 2064e4480e8SJakub Kicinski extack_off = 4 2074e4480e8SJakub Kicinski 2084e4480e8SJakub Kicinski self.extack = None 2094e4480e8SJakub Kicinski if self.nl_flags & Netlink.NLM_F_ACK_TLVS and extack_off: 2104e4480e8SJakub Kicinski self.extack = dict() 2114e4480e8SJakub Kicinski extack_attrs = NlAttrs(self.raw[extack_off:]) 2124e4480e8SJakub Kicinski for extack in extack_attrs: 2134e4480e8SJakub Kicinski if extack.type == Netlink.NLMSGERR_ATTR_MSG: 2144e4480e8SJakub Kicinski self.extack['msg'] = extack.as_strz() 2154e4480e8SJakub Kicinski elif extack.type == Netlink.NLMSGERR_ATTR_MISS_TYPE: 2167c2435efSDonald Hunter self.extack['miss-type'] = extack.as_scalar('u32') 2174e4480e8SJakub Kicinski elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST: 2187c2435efSDonald Hunter self.extack['miss-nest'] = extack.as_scalar('u32') 2194e4480e8SJakub Kicinski elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: 2207c2435efSDonald Hunter self.extack['bad-attr-offs'] = extack.as_scalar('u32') 2214e4480e8SJakub Kicinski else: 2224e4480e8SJakub Kicinski if 'unknown' not in self.extack: 2234e4480e8SJakub Kicinski self.extack['unknown'] = [] 2244e4480e8SJakub Kicinski self.extack['unknown'].append(extack) 2254e4480e8SJakub Kicinski 2264e4480e8SJakub Kicinski if attr_space: 2274e4480e8SJakub Kicinski # We don't have the ability to parse nests yet, so only do global 2284e4480e8SJakub Kicinski if 'miss-type' in self.extack and 'miss-nest' not in self.extack: 2294e4480e8SJakub Kicinski miss_type = self.extack['miss-type'] 23030a5c6c8SJakub Kicinski if miss_type in attr_space.attrs_by_val: 23130a5c6c8SJakub Kicinski spec = attr_space.attrs_by_val[miss_type] 2324e4480e8SJakub Kicinski desc = spec['name'] 2334e4480e8SJakub Kicinski if 'doc' in spec: 2344e4480e8SJakub Kicinski desc += f" ({spec['doc']})" 2354e4480e8SJakub Kicinski self.extack['miss-type'] = desc 2364e4480e8SJakub Kicinski 237e46dd903SDonald Hunter def cmd(self): 238e46dd903SDonald Hunter return self.nl_type 239e46dd903SDonald Hunter 2404e4480e8SJakub Kicinski def __repr__(self): 2414e4480e8SJakub Kicinski msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n" 2424e4480e8SJakub Kicinski if self.error: 2434e4480e8SJakub Kicinski msg += '\terror: ' + str(self.error) 2444e4480e8SJakub Kicinski if self.extack: 2454e4480e8SJakub Kicinski msg += '\textack: ' + repr(self.extack) 2464e4480e8SJakub Kicinski return msg 2474e4480e8SJakub Kicinski 2484e4480e8SJakub Kicinski 2494e4480e8SJakub Kicinskiclass NlMsgs: 2504e4480e8SJakub Kicinski def __init__(self, data, attr_space=None): 2514e4480e8SJakub Kicinski self.msgs = [] 2524e4480e8SJakub Kicinski 2534e4480e8SJakub Kicinski offset = 0 2544e4480e8SJakub Kicinski while offset < len(data): 2554e4480e8SJakub Kicinski msg = NlMsg(data, offset, attr_space=attr_space) 2564e4480e8SJakub Kicinski offset += msg.nl_len 2574e4480e8SJakub Kicinski self.msgs.append(msg) 2584e4480e8SJakub Kicinski 2594e4480e8SJakub Kicinski def __iter__(self): 2604e4480e8SJakub Kicinski yield from self.msgs 2614e4480e8SJakub Kicinski 2624e4480e8SJakub Kicinski 2634e4480e8SJakub Kicinskigenl_family_name_to_id = None 2644e4480e8SJakub Kicinski 2654e4480e8SJakub Kicinski 2664e4480e8SJakub Kicinskidef _genl_msg(nl_type, nl_flags, genl_cmd, genl_version, seq=None): 2674e4480e8SJakub Kicinski # we prepend length in _genl_msg_finalize() 2684e4480e8SJakub Kicinski if seq is None: 2694e4480e8SJakub Kicinski seq = random.randint(1, 1024) 2704e4480e8SJakub Kicinski nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 271758d29fbSDonald Hunter genlmsg = struct.pack("BBH", genl_cmd, genl_version, 0) 2724e4480e8SJakub Kicinski return nlmsg + genlmsg 2734e4480e8SJakub Kicinski 2744e4480e8SJakub Kicinski 2754e4480e8SJakub Kicinskidef _genl_msg_finalize(msg): 2764e4480e8SJakub Kicinski return struct.pack("I", len(msg) + 4) + msg 2774e4480e8SJakub Kicinski 2784e4480e8SJakub Kicinski 2794e4480e8SJakub Kicinskidef _genl_load_families(): 2804e4480e8SJakub Kicinski with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, Netlink.NETLINK_GENERIC) as sock: 2814e4480e8SJakub Kicinski sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 2824e4480e8SJakub Kicinski 2834e4480e8SJakub Kicinski msg = _genl_msg(Netlink.GENL_ID_CTRL, 2844e4480e8SJakub Kicinski Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK | Netlink.NLM_F_DUMP, 2854e4480e8SJakub Kicinski Netlink.CTRL_CMD_GETFAMILY, 1) 2864e4480e8SJakub Kicinski msg = _genl_msg_finalize(msg) 2874e4480e8SJakub Kicinski 2884e4480e8SJakub Kicinski sock.send(msg, 0) 2894e4480e8SJakub Kicinski 2904e4480e8SJakub Kicinski global genl_family_name_to_id 2914e4480e8SJakub Kicinski genl_family_name_to_id = dict() 2924e4480e8SJakub Kicinski 2934e4480e8SJakub Kicinski while True: 2944e4480e8SJakub Kicinski reply = sock.recv(128 * 1024) 2954e4480e8SJakub Kicinski nms = NlMsgs(reply) 2964e4480e8SJakub Kicinski for nl_msg in nms: 2974e4480e8SJakub Kicinski if nl_msg.error: 2984e4480e8SJakub Kicinski print("Netlink error:", nl_msg.error) 2994e4480e8SJakub Kicinski return 3004e4480e8SJakub Kicinski if nl_msg.done: 3014e4480e8SJakub Kicinski return 3024e4480e8SJakub Kicinski 3034e4480e8SJakub Kicinski gm = GenlMsg(nl_msg) 3044e4480e8SJakub Kicinski fam = dict() 305fb0a06d4SDonald Hunter for attr in NlAttrs(gm.raw): 3064e4480e8SJakub Kicinski if attr.type == Netlink.CTRL_ATTR_FAMILY_ID: 3077c2435efSDonald Hunter fam['id'] = attr.as_scalar('u16') 3084e4480e8SJakub Kicinski elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME: 3094e4480e8SJakub Kicinski fam['name'] = attr.as_strz() 3104e4480e8SJakub Kicinski elif attr.type == Netlink.CTRL_ATTR_MAXATTR: 3117c2435efSDonald Hunter fam['maxattr'] = attr.as_scalar('u32') 3124e4480e8SJakub Kicinski elif attr.type == Netlink.CTRL_ATTR_MCAST_GROUPS: 3134e4480e8SJakub Kicinski fam['mcast'] = dict() 3144e4480e8SJakub Kicinski for entry in NlAttrs(attr.raw): 3154e4480e8SJakub Kicinski mcast_name = None 3164e4480e8SJakub Kicinski mcast_id = None 3174e4480e8SJakub Kicinski for entry_attr in NlAttrs(entry.raw): 3184e4480e8SJakub Kicinski if entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_NAME: 3194e4480e8SJakub Kicinski mcast_name = entry_attr.as_strz() 3204e4480e8SJakub Kicinski elif entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_ID: 3217c2435efSDonald Hunter mcast_id = entry_attr.as_scalar('u32') 3224e4480e8SJakub Kicinski if mcast_name and mcast_id is not None: 3234e4480e8SJakub Kicinski fam['mcast'][mcast_name] = mcast_id 3244e4480e8SJakub Kicinski if 'name' in fam and 'id' in fam: 3254e4480e8SJakub Kicinski genl_family_name_to_id[fam['name']] = fam 3264e4480e8SJakub Kicinski 3274e4480e8SJakub Kicinski 3284e4480e8SJakub Kicinskiclass GenlMsg: 329fb0a06d4SDonald Hunter def __init__(self, nl_msg): 3304e4480e8SJakub Kicinski self.nl = nl_msg 331fb0a06d4SDonald Hunter self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0) 332fb0a06d4SDonald Hunter self.raw = nl_msg.raw[4:] 3334e4480e8SJakub Kicinski 334e46dd903SDonald Hunter def cmd(self): 335e46dd903SDonald Hunter return self.genl_cmd 336e46dd903SDonald Hunter 3374e4480e8SJakub Kicinski def __repr__(self): 3384e4480e8SJakub Kicinski msg = repr(self.nl) 3394e4480e8SJakub Kicinski msg += f"\tgenl_cmd = {self.genl_cmd} genl_ver = {self.genl_version}\n" 3404e4480e8SJakub Kicinski for a in self.raw_attrs: 3414e4480e8SJakub Kicinski msg += '\t\t' + repr(a) + '\n' 3424e4480e8SJakub Kicinski return msg 3434e4480e8SJakub Kicinski 3444e4480e8SJakub Kicinski 345e46dd903SDonald Hunterclass NetlinkProtocol: 346e46dd903SDonald Hunter def __init__(self, family_name, proto_num): 3474e4480e8SJakub Kicinski self.family_name = family_name 348e46dd903SDonald Hunter self.proto_num = proto_num 349e46dd903SDonald Hunter 350e46dd903SDonald Hunter def _message(self, nl_type, nl_flags, seq=None): 351e46dd903SDonald Hunter if seq is None: 352e46dd903SDonald Hunter seq = random.randint(1, 1024) 353e46dd903SDonald Hunter nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 354e46dd903SDonald Hunter return nlmsg 355e46dd903SDonald Hunter 356e46dd903SDonald Hunter def message(self, flags, command, version, seq=None): 357e46dd903SDonald Hunter return self._message(command, flags, seq) 358e46dd903SDonald Hunter 359e46dd903SDonald Hunter def _decode(self, nl_msg): 360e46dd903SDonald Hunter return nl_msg 361e46dd903SDonald Hunter 362e46dd903SDonald Hunter def decode(self, ynl, nl_msg): 363e46dd903SDonald Hunter msg = self._decode(nl_msg) 364e46dd903SDonald Hunter fixed_header_size = 0 365e46dd903SDonald Hunter if ynl: 366e46dd903SDonald Hunter op = ynl.rsp_by_value[msg.cmd()] 367e46dd903SDonald Hunter fixed_header_size = ynl._fixed_header_size(op) 368e46dd903SDonald Hunter msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:]) 369e46dd903SDonald Hunter return msg 370e46dd903SDonald Hunter 371e46dd903SDonald Hunter def get_mcast_id(self, mcast_name, mcast_groups): 372e46dd903SDonald Hunter if mcast_name not in mcast_groups: 373e46dd903SDonald Hunter raise Exception(f'Multicast group "{mcast_name}" not present in the spec') 374e46dd903SDonald Hunter return mcast_groups[mcast_name].value 375e46dd903SDonald Hunter 376e46dd903SDonald Hunter 377e46dd903SDonald Hunterclass GenlProtocol(NetlinkProtocol): 378e46dd903SDonald Hunter def __init__(self, family_name): 379e46dd903SDonald Hunter super().__init__(family_name, Netlink.NETLINK_GENERIC) 3804e4480e8SJakub Kicinski 3814e4480e8SJakub Kicinski global genl_family_name_to_id 3824e4480e8SJakub Kicinski if genl_family_name_to_id is None: 3834e4480e8SJakub Kicinski _genl_load_families() 3844e4480e8SJakub Kicinski 3854e4480e8SJakub Kicinski self.genl_family = genl_family_name_to_id[family_name] 3864e4480e8SJakub Kicinski self.family_id = genl_family_name_to_id[family_name]['id'] 3874e4480e8SJakub Kicinski 388e46dd903SDonald Hunter def message(self, flags, command, version, seq=None): 389e46dd903SDonald Hunter nlmsg = self._message(self.family_id, flags, seq) 390e46dd903SDonald Hunter genlmsg = struct.pack("BBH", command, version, 0) 391e46dd903SDonald Hunter return nlmsg + genlmsg 392e46dd903SDonald Hunter 393e46dd903SDonald Hunter def _decode(self, nl_msg): 394e46dd903SDonald Hunter return GenlMsg(nl_msg) 395e46dd903SDonald Hunter 396e46dd903SDonald Hunter def get_mcast_id(self, mcast_name, mcast_groups): 397e46dd903SDonald Hunter if mcast_name not in self.genl_family['mcast']: 398e46dd903SDonald Hunter raise Exception(f'Multicast group "{mcast_name}" not present in the family') 399e46dd903SDonald Hunter return self.genl_family['mcast'][mcast_name] 400e46dd903SDonald Hunter 4014e4480e8SJakub Kicinski 4024e4480e8SJakub Kicinski# 4034e4480e8SJakub Kicinski# YNL implementation details. 4044e4480e8SJakub Kicinski# 4054e4480e8SJakub Kicinski 4064e4480e8SJakub Kicinski 40730a5c6c8SJakub Kicinskiclass YnlFamily(SpecFamily): 4084e4480e8SJakub Kicinski def __init__(self, def_path, schema=None): 40930a5c6c8SJakub Kicinski super().__init__(def_path, schema) 41030a5c6c8SJakub Kicinski 4114e4480e8SJakub Kicinski self.include_raw = False 4124e4480e8SJakub Kicinski 413e46dd903SDonald Hunter try: 414e46dd903SDonald Hunter if self.proto == "netlink-raw": 415e46dd903SDonald Hunter self.nlproto = NetlinkProtocol(self.yaml['name'], 416e46dd903SDonald Hunter self.yaml['protonum']) 417e46dd903SDonald Hunter else: 418e46dd903SDonald Hunter self.nlproto = GenlProtocol(self.yaml['name']) 419e46dd903SDonald Hunter except KeyError: 420e46dd903SDonald Hunter raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel") 421e46dd903SDonald Hunter 422e46dd903SDonald Hunter self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.nlproto.proto_num) 4234e4480e8SJakub Kicinski self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 4244e4480e8SJakub Kicinski self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1) 425e46dd903SDonald Hunter self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_GET_STRICT_CHK, 1) 4264e4480e8SJakub Kicinski 4274e4480e8SJakub Kicinski self.async_msg_ids = set() 4284e4480e8SJakub Kicinski self.async_msg_queue = [] 4294e4480e8SJakub Kicinski 43030a5c6c8SJakub Kicinski for msg in self.msgs.values(): 43130a5c6c8SJakub Kicinski if msg.is_async: 432fd0616d3SJakub Kicinski self.async_msg_ids.add(msg.rsp_value) 4334e4480e8SJakub Kicinski 43430a5c6c8SJakub Kicinski for op_name, op in self.ops.items(): 43530a5c6c8SJakub Kicinski bound_f = functools.partial(self._op, op_name) 43630a5c6c8SJakub Kicinski setattr(self, op.ident_name, bound_f) 4374e4480e8SJakub Kicinski 4384e4480e8SJakub Kicinski 4394e4480e8SJakub Kicinski def ntf_subscribe(self, mcast_name): 440e46dd903SDonald Hunter mcast_id = self.nlproto.get_mcast_id(mcast_name, self.mcast_groups) 4414e4480e8SJakub Kicinski self.sock.bind((0, 0)) 4424e4480e8SJakub Kicinski self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, 443e46dd903SDonald Hunter mcast_id) 4444e4480e8SJakub Kicinski 4454e4480e8SJakub Kicinski def _add_attr(self, space, name, value): 4467582113cSJakub Kicinski try: 44730a5c6c8SJakub Kicinski attr = self.attr_sets[space][name] 4487582113cSJakub Kicinski except KeyError: 4497582113cSJakub Kicinski raise Exception(f"Space '{space}' has no attribute '{name}'") 45030a5c6c8SJakub Kicinski nl_type = attr.value 4514e4480e8SJakub Kicinski if attr["type"] == 'nest': 4524e4480e8SJakub Kicinski nl_type |= Netlink.NLA_F_NESTED 4534e4480e8SJakub Kicinski attr_payload = b'' 4544e4480e8SJakub Kicinski for subname, subvalue in value.items(): 4554e4480e8SJakub Kicinski attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue) 45619b64b48SJakub Kicinski elif attr["type"] == 'flag': 45719b64b48SJakub Kicinski attr_payload = b'' 4584e4480e8SJakub Kicinski elif attr["type"] == 'string': 4594e4480e8SJakub Kicinski attr_payload = str(value).encode('ascii') + b'\x00' 4604e4480e8SJakub Kicinski elif attr["type"] == 'binary': 461649bde90SJakub Kicinski if isinstance(value, bytes): 462649bde90SJakub Kicinski attr_payload = value 463649bde90SJakub Kicinski elif isinstance(value, str): 464d8eea68dSDonald Hunter attr_payload = bytes.fromhex(value) 465649bde90SJakub Kicinski else: 466649bde90SJakub Kicinski raise Exception(f'Unknown type for binary attribute, value: {value}') 4677c2435efSDonald Hunter elif attr['type'] in NlAttr.type_formats: 4687c2435efSDonald Hunter format = NlAttr.get_format(attr['type'], attr.byte_order) 4697c2435efSDonald Hunter attr_payload = format.pack(int(value)) 4704e4480e8SJakub Kicinski else: 4714e4480e8SJakub Kicinski raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}') 4724e4480e8SJakub Kicinski 4734e4480e8SJakub Kicinski pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4) 4744e4480e8SJakub Kicinski return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad 4754e4480e8SJakub Kicinski 476df15c15eSArkadiusz Kubalewski def _decode_enum(self, raw, attr_spec): 477c311aaa7SJakub Kicinski enum = self.consts[attr_spec['enum']] 4784e4480e8SJakub Kicinski if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']: 479d7ddf5f4SArkadiusz Kubalewski i = 0 4804e4480e8SJakub Kicinski value = set() 4814e4480e8SJakub Kicinski while raw: 4824e4480e8SJakub Kicinski if raw & 1: 483c311aaa7SJakub Kicinski value.add(enum.entries_by_val[i].name) 4844e4480e8SJakub Kicinski raw >>= 1 4854e4480e8SJakub Kicinski i += 1 4864e4480e8SJakub Kicinski else: 487d7ddf5f4SArkadiusz Kubalewski value = enum.entries_by_val[raw].name 488df15c15eSArkadiusz Kubalewski return value 4894e4480e8SJakub Kicinski 490b423c3c8SDonald Hunter def _decode_binary(self, attr, attr_spec): 49126071913SDonald Hunter if attr_spec.struct_name: 492313a7a80SDonald Hunter members = self.consts[attr_spec.struct_name] 493313a7a80SDonald Hunter decoded = attr.as_struct(members) 494313a7a80SDonald Hunter for m in members: 495313a7a80SDonald Hunter if m.enum: 496df15c15eSArkadiusz Kubalewski decoded[m.name] = self._decode_enum(decoded[m.name], m) 49726071913SDonald Hunter elif attr_spec.sub_type: 498b423c3c8SDonald Hunter decoded = attr.as_c_array(attr_spec.sub_type) 499b423c3c8SDonald Hunter else: 500b423c3c8SDonald Hunter decoded = attr.as_bin() 501d8eea68dSDonald Hunter if attr_spec.display_hint: 502d8eea68dSDonald Hunter decoded = NlAttr.formatted_string(decoded, attr_spec.display_hint) 503b423c3c8SDonald Hunter return decoded 504b423c3c8SDonald Hunter 5050493e56dSDonald Hunter def _decode_array_nest(self, attr, attr_spec): 5060493e56dSDonald Hunter decoded = [] 5070493e56dSDonald Hunter offset = 0 5080493e56dSDonald Hunter while offset < len(attr.raw): 5090493e56dSDonald Hunter item = NlAttr(attr.raw, offset) 5100493e56dSDonald Hunter offset += item.full_len 5110493e56dSDonald Hunter 5120493e56dSDonald Hunter subattrs = self._decode(NlAttrs(item.raw), attr_spec['nested-attributes']) 5130493e56dSDonald Hunter decoded.append({ item.type: subattrs }) 5140493e56dSDonald Hunter return decoded 5150493e56dSDonald Hunter 5164e4480e8SJakub Kicinski def _decode(self, attrs, space): 51730a5c6c8SJakub Kicinski attr_space = self.attr_sets[space] 5184e4480e8SJakub Kicinski rsp = dict() 5194e4480e8SJakub Kicinski for attr in attrs: 5207582113cSJakub Kicinski try: 52130a5c6c8SJakub Kicinski attr_spec = attr_space.attrs_by_val[attr.type] 5227582113cSJakub Kicinski except KeyError: 5237582113cSJakub Kicinski raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'") 5244e4480e8SJakub Kicinski if attr_spec["type"] == 'nest': 5254e4480e8SJakub Kicinski subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes']) 52690256f3fSJakub Kicinski decoded = subdict 5274e4480e8SJakub Kicinski elif attr_spec["type"] == 'string': 52890256f3fSJakub Kicinski decoded = attr.as_strz() 5294e4480e8SJakub Kicinski elif attr_spec["type"] == 'binary': 530b423c3c8SDonald Hunter decoded = self._decode_binary(attr, attr_spec) 53119b64b48SJakub Kicinski elif attr_spec["type"] == 'flag': 53290256f3fSJakub Kicinski decoded = True 5337c2435efSDonald Hunter elif attr_spec["type"] in NlAttr.type_formats: 5347c2435efSDonald Hunter decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) 5350493e56dSDonald Hunter elif attr_spec["type"] == 'array-nest': 5360493e56dSDonald Hunter decoded = self._decode_array_nest(attr, attr_spec) 5374e4480e8SJakub Kicinski else: 5387c2435efSDonald Hunter raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') 5394e4480e8SJakub Kicinski 540df15c15eSArkadiusz Kubalewski if 'enum' in attr_spec: 541df15c15eSArkadiusz Kubalewski decoded = self._decode_enum(decoded, attr_spec) 542df15c15eSArkadiusz Kubalewski 54390256f3fSJakub Kicinski if not attr_spec.is_multi: 54490256f3fSJakub Kicinski rsp[attr_spec['name']] = decoded 54590256f3fSJakub Kicinski elif attr_spec.name in rsp: 54690256f3fSJakub Kicinski rsp[attr_spec.name].append(decoded) 54790256f3fSJakub Kicinski else: 54890256f3fSJakub Kicinski rsp[attr_spec.name] = [decoded] 54990256f3fSJakub Kicinski 5504e4480e8SJakub Kicinski return rsp 5514e4480e8SJakub Kicinski 5524cd2796fSJakub Kicinski def _decode_extack_path(self, attrs, attr_set, offset, target): 5534cd2796fSJakub Kicinski for attr in attrs: 5547582113cSJakub Kicinski try: 5554cd2796fSJakub Kicinski attr_spec = attr_set.attrs_by_val[attr.type] 5567582113cSJakub Kicinski except KeyError: 5577582113cSJakub Kicinski raise Exception(f"Space '{attr_set.name}' has no attribute with value '{attr.type}'") 5584cd2796fSJakub Kicinski if offset > target: 5594cd2796fSJakub Kicinski break 5604cd2796fSJakub Kicinski if offset == target: 5614cd2796fSJakub Kicinski return '.' + attr_spec.name 5624cd2796fSJakub Kicinski 5634cd2796fSJakub Kicinski if offset + attr.full_len <= target: 5644cd2796fSJakub Kicinski offset += attr.full_len 5654cd2796fSJakub Kicinski continue 5664cd2796fSJakub Kicinski if attr_spec['type'] != 'nest': 5674cd2796fSJakub Kicinski raise Exception(f"Can't dive into {attr.type} ({attr_spec['name']}) for extack") 5684cd2796fSJakub Kicinski offset += 4 5694cd2796fSJakub Kicinski subpath = self._decode_extack_path(NlAttrs(attr.raw), 5704cd2796fSJakub Kicinski self.attr_sets[attr_spec['nested-attributes']], 5714cd2796fSJakub Kicinski offset, target) 5724cd2796fSJakub Kicinski if subpath is None: 5734cd2796fSJakub Kicinski return None 5744cd2796fSJakub Kicinski return '.' + attr_spec.name + subpath 5754cd2796fSJakub Kicinski 5764cd2796fSJakub Kicinski return None 5774cd2796fSJakub Kicinski 578fb0a06d4SDonald Hunter def _decode_extack(self, request, op, extack): 5794cd2796fSJakub Kicinski if 'bad-attr-offs' not in extack: 5804cd2796fSJakub Kicinski return 5814cd2796fSJakub Kicinski 582e46dd903SDonald Hunter msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) 583e46dd903SDonald Hunter offset = 20 + self._fixed_header_size(op) 584e46dd903SDonald Hunter path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, 585fb0a06d4SDonald Hunter extack['bad-attr-offs']) 5864cd2796fSJakub Kicinski if path: 5874cd2796fSJakub Kicinski del extack['bad-attr-offs'] 5884cd2796fSJakub Kicinski extack['bad-attr'] = path 5894cd2796fSJakub Kicinski 590fb0a06d4SDonald Hunter def _fixed_header_size(self, op): 591fb0a06d4SDonald Hunter if op.fixed_header: 592fb0a06d4SDonald Hunter fixed_header_members = self.consts[op.fixed_header].members 593fb0a06d4SDonald Hunter size = 0 594fb0a06d4SDonald Hunter for m in fixed_header_members: 595fb0a06d4SDonald Hunter format = NlAttr.get_format(m.type, m.byte_order) 596fb0a06d4SDonald Hunter size += format.size 597fb0a06d4SDonald Hunter return size 598fb0a06d4SDonald Hunter else: 599fb0a06d4SDonald Hunter return 0 600fb0a06d4SDonald Hunter 601fb0a06d4SDonald Hunter def _decode_fixed_header(self, msg, name): 602fb0a06d4SDonald Hunter fixed_header_members = self.consts[name].members 603fb0a06d4SDonald Hunter fixed_header_attrs = dict() 604fb0a06d4SDonald Hunter offset = 0 605fb0a06d4SDonald Hunter for m in fixed_header_members: 606fb0a06d4SDonald Hunter format = NlAttr.get_format(m.type, m.byte_order) 607fb0a06d4SDonald Hunter [ value ] = format.unpack_from(msg.raw, offset) 608fb0a06d4SDonald Hunter offset += format.size 609fb0a06d4SDonald Hunter if m.enum: 610fb0a06d4SDonald Hunter value = self._decode_enum(value, m) 611fb0a06d4SDonald Hunter fixed_header_attrs[m.name] = value 612fb0a06d4SDonald Hunter return fixed_header_attrs 613fb0a06d4SDonald Hunter 614e46dd903SDonald Hunter def handle_ntf(self, decoded): 6154e4480e8SJakub Kicinski msg = dict() 6164e4480e8SJakub Kicinski if self.include_raw: 617e46dd903SDonald Hunter msg['raw'] = decoded 618e46dd903SDonald Hunter op = self.rsp_by_value[decoded.cmd()] 619e46dd903SDonald Hunter attrs = self._decode(decoded.raw_attrs, op.attr_set.name) 620e46dd903SDonald Hunter if op.fixed_header: 621e46dd903SDonald Hunter attrs.update(self._decode_fixed_header(decoded, op.fixed_header)) 622e46dd903SDonald Hunter 6234e4480e8SJakub Kicinski msg['name'] = op['name'] 624e46dd903SDonald Hunter msg['msg'] = attrs 6254e4480e8SJakub Kicinski self.async_msg_queue.append(msg) 6264e4480e8SJakub Kicinski 6274e4480e8SJakub Kicinski def check_ntf(self): 6284e4480e8SJakub Kicinski while True: 6294e4480e8SJakub Kicinski try: 6304e4480e8SJakub Kicinski reply = self.sock.recv(128 * 1024, socket.MSG_DONTWAIT) 6314e4480e8SJakub Kicinski except BlockingIOError: 6324e4480e8SJakub Kicinski return 6334e4480e8SJakub Kicinski 6344e4480e8SJakub Kicinski nms = NlMsgs(reply) 6354e4480e8SJakub Kicinski for nl_msg in nms: 6364e4480e8SJakub Kicinski if nl_msg.error: 6374e4480e8SJakub Kicinski print("Netlink error in ntf!?", os.strerror(-nl_msg.error)) 6384e4480e8SJakub Kicinski print(nl_msg) 6394e4480e8SJakub Kicinski continue 6404e4480e8SJakub Kicinski if nl_msg.done: 6414e4480e8SJakub Kicinski print("Netlink done while checking for ntf!?") 6424e4480e8SJakub Kicinski continue 6434e4480e8SJakub Kicinski 644e46dd903SDonald Hunter decoded = self.nlproto.decode(self, nl_msg) 645e46dd903SDonald Hunter if decoded.cmd() not in self.async_msg_ids: 646e46dd903SDonald Hunter print("Unexpected msg id done while checking for ntf", decoded) 6474e4480e8SJakub Kicinski continue 6484e4480e8SJakub Kicinski 649e46dd903SDonald Hunter self.handle_ntf(decoded) 6504e4480e8SJakub Kicinski 651f3d07b02SStanislav Fomichev def operation_do_attributes(self, name): 652f3d07b02SStanislav Fomichev """ 653f3d07b02SStanislav Fomichev For a given operation name, find and return a supported 654f3d07b02SStanislav Fomichev set of attributes (as a dict). 655f3d07b02SStanislav Fomichev """ 656f3d07b02SStanislav Fomichev op = self.find_operation(name) 657f3d07b02SStanislav Fomichev if not op: 658f3d07b02SStanislav Fomichev return None 659f3d07b02SStanislav Fomichev 660f3d07b02SStanislav Fomichev return op['do']['request']['attributes'].copy() 661f3d07b02SStanislav Fomichev 6621768d8a7SDonald Hunter def _op(self, method, vals, flags, dump=False): 66330a5c6c8SJakub Kicinski op = self.ops[method] 6644e4480e8SJakub Kicinski 6654e4480e8SJakub Kicinski nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK 6661768d8a7SDonald Hunter for flag in flags or []: 6671768d8a7SDonald Hunter nl_flags |= flag 6684e4480e8SJakub Kicinski if dump: 6694e4480e8SJakub Kicinski nl_flags |= Netlink.NLM_F_DUMP 6704e4480e8SJakub Kicinski 6714e4480e8SJakub Kicinski req_seq = random.randint(1024, 65535) 672e46dd903SDonald Hunter msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) 673f036d936SDonald Hunter fixed_header_members = [] 674f036d936SDonald Hunter if op.fixed_header: 675f036d936SDonald Hunter fixed_header_members = self.consts[op.fixed_header].members 676f036d936SDonald Hunter for m in fixed_header_members: 6775ac18889SDonald Hunter value = vals.pop(m.name) if m.name in vals else 0 678bddd2e56SDonald Hunter format = NlAttr.get_format(m.type, m.byte_order) 6797c2435efSDonald Hunter msg += format.pack(value) 6804e4480e8SJakub Kicinski for name, value in vals.items(): 68130a5c6c8SJakub Kicinski msg += self._add_attr(op.attr_set.name, name, value) 6824e4480e8SJakub Kicinski msg = _genl_msg_finalize(msg) 6834e4480e8SJakub Kicinski 6844e4480e8SJakub Kicinski self.sock.send(msg, 0) 6854e4480e8SJakub Kicinski 6864e4480e8SJakub Kicinski done = False 6874e4480e8SJakub Kicinski rsp = [] 6884e4480e8SJakub Kicinski while not done: 6894e4480e8SJakub Kicinski reply = self.sock.recv(128 * 1024) 69030a5c6c8SJakub Kicinski nms = NlMsgs(reply, attr_space=op.attr_set) 6914e4480e8SJakub Kicinski for nl_msg in nms: 6924cd2796fSJakub Kicinski if nl_msg.extack: 693fb0a06d4SDonald Hunter self._decode_extack(msg, op, nl_msg.extack) 6944cd2796fSJakub Kicinski 6954e4480e8SJakub Kicinski if nl_msg.error: 69648993e22SStanislav Fomichev raise NlError(nl_msg) 6974e4480e8SJakub Kicinski if nl_msg.done: 6984cd2796fSJakub Kicinski if nl_msg.extack: 6994cd2796fSJakub Kicinski print("Netlink warning:") 7004cd2796fSJakub Kicinski print(nl_msg) 7014e4480e8SJakub Kicinski done = True 7024e4480e8SJakub Kicinski break 7034e4480e8SJakub Kicinski 704e46dd903SDonald Hunter decoded = self.nlproto.decode(self, nl_msg) 705e46dd903SDonald Hunter 7064e4480e8SJakub Kicinski # Check if this is a reply to our request 707e46dd903SDonald Hunter if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value: 708e46dd903SDonald Hunter if decoded.cmd() in self.async_msg_ids: 709e46dd903SDonald Hunter self.handle_ntf(decoded) 7104e4480e8SJakub Kicinski continue 7114e4480e8SJakub Kicinski else: 712e46dd903SDonald Hunter print('Unexpected message: ' + repr(decoded)) 7134e4480e8SJakub Kicinski continue 7144e4480e8SJakub Kicinski 715e46dd903SDonald Hunter rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name) 716fb0a06d4SDonald Hunter if op.fixed_header: 717e46dd903SDonald Hunter rsp_msg.update(self._decode_fixed_header(decoded, op.fixed_header)) 718081e8df6SJakub Kicinski rsp.append(rsp_msg) 7194e4480e8SJakub Kicinski 7204e4480e8SJakub Kicinski if not rsp: 7214e4480e8SJakub Kicinski return None 7224e4480e8SJakub Kicinski if not dump and len(rsp) == 1: 7234e4480e8SJakub Kicinski return rsp[0] 7244e4480e8SJakub Kicinski return rsp 7258dfec0a8SJakub Kicinski 7261768d8a7SDonald Hunter def do(self, method, vals, flags): 7271768d8a7SDonald Hunter return self._op(method, vals, flags) 7288dfec0a8SJakub Kicinski 7298dfec0a8SJakub Kicinski def dump(self, method, vals): 7301768d8a7SDonald Hunter return self._op(method, vals, [], dump=True) 731