2023-03-07 04:04:57 +08:00
|
|
|
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
import functools
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import socket
|
|
|
|
import struct
|
|
|
|
import yaml
|
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
from .nlspec import SpecFamily
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
#
|
|
|
|
# Generic Netlink code which should really be in some library, but I can't quickly find one.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
class Netlink:
|
|
|
|
# Netlink socket
|
|
|
|
SOL_NETLINK = 270
|
|
|
|
|
|
|
|
NETLINK_ADD_MEMBERSHIP = 1
|
|
|
|
NETLINK_CAP_ACK = 10
|
|
|
|
NETLINK_EXT_ACK = 11
|
|
|
|
|
|
|
|
# Netlink message
|
|
|
|
NLMSG_ERROR = 2
|
|
|
|
NLMSG_DONE = 3
|
|
|
|
|
|
|
|
NLM_F_REQUEST = 1
|
|
|
|
NLM_F_ACK = 4
|
|
|
|
NLM_F_ROOT = 0x100
|
|
|
|
NLM_F_MATCH = 0x200
|
|
|
|
NLM_F_APPEND = 0x800
|
|
|
|
|
|
|
|
NLM_F_CAPPED = 0x100
|
|
|
|
NLM_F_ACK_TLVS = 0x200
|
|
|
|
|
|
|
|
NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
|
|
|
|
|
|
|
|
NLA_F_NESTED = 0x8000
|
|
|
|
NLA_F_NET_BYTEORDER = 0x4000
|
|
|
|
|
|
|
|
NLA_TYPE_MASK = NLA_F_NESTED | NLA_F_NET_BYTEORDER
|
|
|
|
|
|
|
|
# Genetlink defines
|
|
|
|
NETLINK_GENERIC = 16
|
|
|
|
|
|
|
|
GENL_ID_CTRL = 0x10
|
|
|
|
|
|
|
|
# nlctrl
|
|
|
|
CTRL_CMD_GETFAMILY = 3
|
|
|
|
|
|
|
|
CTRL_ATTR_FAMILY_ID = 1
|
|
|
|
CTRL_ATTR_FAMILY_NAME = 2
|
|
|
|
CTRL_ATTR_MAXATTR = 5
|
|
|
|
CTRL_ATTR_MCAST_GROUPS = 7
|
|
|
|
|
|
|
|
CTRL_ATTR_MCAST_GRP_NAME = 1
|
|
|
|
CTRL_ATTR_MCAST_GRP_ID = 2
|
|
|
|
|
|
|
|
# Extack types
|
|
|
|
NLMSGERR_ATTR_MSG = 1
|
|
|
|
NLMSGERR_ATTR_OFFS = 2
|
|
|
|
NLMSGERR_ATTR_COOKIE = 3
|
|
|
|
NLMSGERR_ATTR_POLICY = 4
|
|
|
|
NLMSGERR_ATTR_MISS_TYPE = 5
|
|
|
|
NLMSGERR_ATTR_MISS_NEST = 6
|
|
|
|
|
|
|
|
|
|
|
|
class NlAttr:
|
2023-03-27 16:31:33 +08:00
|
|
|
type_formats = { 'u8' : ('B', 1), 's8' : ('b', 1),
|
|
|
|
'u16': ('H', 2), 's16': ('h', 2),
|
|
|
|
'u32': ('I', 4), 's32': ('i', 4),
|
|
|
|
'u64': ('Q', 8), 's64': ('q', 8) }
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def __init__(self, raw, offset):
|
|
|
|
self._len, self._type = struct.unpack("HH", raw[offset:offset + 4])
|
|
|
|
self.type = self._type & ~Netlink.NLA_TYPE_MASK
|
|
|
|
self.payload_len = self._len
|
|
|
|
self.full_len = (self.payload_len + 3) & ~3
|
|
|
|
self.raw = raw[offset + 4:offset + self.payload_len]
|
|
|
|
|
2023-01-31 10:33:45 +08:00
|
|
|
def as_u8(self):
|
|
|
|
return struct.unpack("B", self.raw)[0]
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def as_u16(self):
|
|
|
|
return struct.unpack("H", self.raw)[0]
|
|
|
|
|
|
|
|
def as_u32(self):
|
|
|
|
return struct.unpack("I", self.raw)[0]
|
|
|
|
|
|
|
|
def as_u64(self):
|
|
|
|
return struct.unpack("Q", self.raw)[0]
|
|
|
|
|
|
|
|
def as_strz(self):
|
|
|
|
return self.raw.decode('ascii')[:-1]
|
|
|
|
|
|
|
|
def as_bin(self):
|
|
|
|
return self.raw
|
|
|
|
|
2023-03-27 16:31:33 +08:00
|
|
|
def as_c_array(self, type):
|
|
|
|
format, _ = self.type_formats[type]
|
|
|
|
return list({ x[0] for x in struct.iter_unpack(format, self.raw) })
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return f"[type:{self.type} len:{self._len}] {self.raw}"
|
|
|
|
|
|
|
|
|
|
|
|
class NlAttrs:
|
|
|
|
def __init__(self, msg):
|
|
|
|
self.attrs = []
|
|
|
|
|
|
|
|
offset = 0
|
|
|
|
while offset < len(msg):
|
|
|
|
attr = NlAttr(msg, offset)
|
|
|
|
offset += attr.full_len
|
|
|
|
self.attrs.append(attr)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
yield from self.attrs
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
msg = ''
|
|
|
|
for a in self.attrs:
|
|
|
|
if msg:
|
|
|
|
msg += '\n'
|
|
|
|
msg += repr(a)
|
|
|
|
return msg
|
|
|
|
|
|
|
|
|
|
|
|
class NlMsg:
|
|
|
|
def __init__(self, msg, offset, attr_space=None):
|
|
|
|
self.hdr = msg[offset:offset + 16]
|
|
|
|
|
|
|
|
self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \
|
|
|
|
struct.unpack("IHHII", self.hdr)
|
|
|
|
|
|
|
|
self.raw = msg[offset + 16:offset + self.nl_len]
|
|
|
|
|
|
|
|
self.error = 0
|
|
|
|
self.done = 0
|
|
|
|
|
|
|
|
extack_off = None
|
|
|
|
if self.nl_type == Netlink.NLMSG_ERROR:
|
|
|
|
self.error = struct.unpack("i", self.raw[0:4])[0]
|
|
|
|
self.done = 1
|
|
|
|
extack_off = 20
|
|
|
|
elif self.nl_type == Netlink.NLMSG_DONE:
|
|
|
|
self.done = 1
|
|
|
|
extack_off = 4
|
|
|
|
|
|
|
|
self.extack = None
|
|
|
|
if self.nl_flags & Netlink.NLM_F_ACK_TLVS and extack_off:
|
|
|
|
self.extack = dict()
|
|
|
|
extack_attrs = NlAttrs(self.raw[extack_off:])
|
|
|
|
for extack in extack_attrs:
|
|
|
|
if extack.type == Netlink.NLMSGERR_ATTR_MSG:
|
|
|
|
self.extack['msg'] = extack.as_strz()
|
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_MISS_TYPE:
|
|
|
|
self.extack['miss-type'] = extack.as_u32()
|
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST:
|
|
|
|
self.extack['miss-nest'] = extack.as_u32()
|
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_OFFS:
|
|
|
|
self.extack['bad-attr-offs'] = extack.as_u32()
|
|
|
|
else:
|
|
|
|
if 'unknown' not in self.extack:
|
|
|
|
self.extack['unknown'] = []
|
|
|
|
self.extack['unknown'].append(extack)
|
|
|
|
|
|
|
|
if attr_space:
|
|
|
|
# We don't have the ability to parse nests yet, so only do global
|
|
|
|
if 'miss-type' in self.extack and 'miss-nest' not in self.extack:
|
|
|
|
miss_type = self.extack['miss-type']
|
2023-01-31 10:33:44 +08:00
|
|
|
if miss_type in attr_space.attrs_by_val:
|
|
|
|
spec = attr_space.attrs_by_val[miss_type]
|
2023-01-21 01:50:41 +08:00
|
|
|
desc = spec['name']
|
|
|
|
if 'doc' in spec:
|
|
|
|
desc += f" ({spec['doc']})"
|
|
|
|
self.extack['miss-type'] = desc
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n"
|
|
|
|
if self.error:
|
|
|
|
msg += '\terror: ' + str(self.error)
|
|
|
|
if self.extack:
|
|
|
|
msg += '\textack: ' + repr(self.extack)
|
|
|
|
return msg
|
|
|
|
|
|
|
|
|
|
|
|
class NlMsgs:
|
|
|
|
def __init__(self, data, attr_space=None):
|
|
|
|
self.msgs = []
|
|
|
|
|
|
|
|
offset = 0
|
|
|
|
while offset < len(data):
|
|
|
|
msg = NlMsg(data, offset, attr_space=attr_space)
|
|
|
|
offset += msg.nl_len
|
|
|
|
self.msgs.append(msg)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
yield from self.msgs
|
|
|
|
|
|
|
|
|
|
|
|
genl_family_name_to_id = None
|
|
|
|
|
|
|
|
|
|
|
|
def _genl_msg(nl_type, nl_flags, genl_cmd, genl_version, seq=None):
|
|
|
|
# we prepend length in _genl_msg_finalize()
|
|
|
|
if seq is None:
|
|
|
|
seq = random.randint(1, 1024)
|
|
|
|
nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0)
|
2023-03-20 03:37:58 +08:00
|
|
|
genlmsg = struct.pack("BBH", genl_cmd, genl_version, 0)
|
2023-01-21 01:50:41 +08:00
|
|
|
return nlmsg + genlmsg
|
|
|
|
|
|
|
|
|
|
|
|
def _genl_msg_finalize(msg):
|
|
|
|
return struct.pack("I", len(msg) + 4) + msg
|
|
|
|
|
|
|
|
|
|
|
|
def _genl_load_families():
|
|
|
|
with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, Netlink.NETLINK_GENERIC) as sock:
|
|
|
|
sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
|
|
|
|
|
|
|
|
msg = _genl_msg(Netlink.GENL_ID_CTRL,
|
|
|
|
Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK | Netlink.NLM_F_DUMP,
|
|
|
|
Netlink.CTRL_CMD_GETFAMILY, 1)
|
|
|
|
msg = _genl_msg_finalize(msg)
|
|
|
|
|
|
|
|
sock.send(msg, 0)
|
|
|
|
|
|
|
|
global genl_family_name_to_id
|
|
|
|
genl_family_name_to_id = dict()
|
|
|
|
|
|
|
|
while True:
|
|
|
|
reply = sock.recv(128 * 1024)
|
|
|
|
nms = NlMsgs(reply)
|
|
|
|
for nl_msg in nms:
|
|
|
|
if nl_msg.error:
|
|
|
|
print("Netlink error:", nl_msg.error)
|
|
|
|
return
|
|
|
|
if nl_msg.done:
|
|
|
|
return
|
|
|
|
|
|
|
|
gm = GenlMsg(nl_msg)
|
|
|
|
fam = dict()
|
|
|
|
for attr in gm.raw_attrs:
|
|
|
|
if attr.type == Netlink.CTRL_ATTR_FAMILY_ID:
|
|
|
|
fam['id'] = attr.as_u16()
|
|
|
|
elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME:
|
|
|
|
fam['name'] = attr.as_strz()
|
|
|
|
elif attr.type == Netlink.CTRL_ATTR_MAXATTR:
|
|
|
|
fam['maxattr'] = attr.as_u32()
|
|
|
|
elif attr.type == Netlink.CTRL_ATTR_MCAST_GROUPS:
|
|
|
|
fam['mcast'] = dict()
|
|
|
|
for entry in NlAttrs(attr.raw):
|
|
|
|
mcast_name = None
|
|
|
|
mcast_id = None
|
|
|
|
for entry_attr in NlAttrs(entry.raw):
|
|
|
|
if entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_NAME:
|
|
|
|
mcast_name = entry_attr.as_strz()
|
|
|
|
elif entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_ID:
|
|
|
|
mcast_id = entry_attr.as_u32()
|
|
|
|
if mcast_name and mcast_id is not None:
|
|
|
|
fam['mcast'][mcast_name] = mcast_id
|
|
|
|
if 'name' in fam and 'id' in fam:
|
|
|
|
genl_family_name_to_id[fam['name']] = fam
|
|
|
|
|
|
|
|
|
|
|
|
class GenlMsg:
|
|
|
|
def __init__(self, nl_msg):
|
|
|
|
self.nl = nl_msg
|
|
|
|
|
|
|
|
self.hdr = nl_msg.raw[0:4]
|
|
|
|
self.raw = nl_msg.raw[4:]
|
|
|
|
|
2023-03-20 03:37:58 +08:00
|
|
|
self.genl_cmd, self.genl_version, _ = struct.unpack("BBH", self.hdr)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
self.raw_attrs = NlAttrs(self.raw)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
msg = repr(self.nl)
|
|
|
|
msg += f"\tgenl_cmd = {self.genl_cmd} genl_ver = {self.genl_version}\n"
|
|
|
|
for a in self.raw_attrs:
|
|
|
|
msg += '\t\t' + repr(a) + '\n'
|
|
|
|
return msg
|
|
|
|
|
|
|
|
|
|
|
|
class GenlFamily:
|
|
|
|
def __init__(self, family_name):
|
|
|
|
self.family_name = family_name
|
|
|
|
|
|
|
|
global genl_family_name_to_id
|
|
|
|
if genl_family_name_to_id is None:
|
|
|
|
_genl_load_families()
|
|
|
|
|
|
|
|
self.genl_family = genl_family_name_to_id[family_name]
|
|
|
|
self.family_id = genl_family_name_to_id[family_name]['id']
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# YNL implementation details.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
class YnlFamily(SpecFamily):
|
2023-01-21 01:50:41 +08:00
|
|
|
def __init__(self, def_path, schema=None):
|
2023-01-31 10:33:44 +08:00
|
|
|
super().__init__(def_path, schema)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
self.include_raw = False
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, Netlink.NETLINK_GENERIC)
|
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
|
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1)
|
|
|
|
|
|
|
|
self.async_msg_ids = set()
|
|
|
|
self.async_msg_queue = []
|
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
for msg in self.msgs.values():
|
|
|
|
if msg.is_async:
|
2023-01-31 10:33:46 +08:00
|
|
|
self.async_msg_ids.add(msg.rsp_value)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
for op_name, op in self.ops.items():
|
|
|
|
bound_f = functools.partial(self._op, op_name)
|
|
|
|
setattr(self, op.ident_name, bound_f)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
self.family = GenlFamily(self.yaml['name'])
|
|
|
|
|
|
|
|
def ntf_subscribe(self, mcast_name):
|
|
|
|
if mcast_name not in self.family.genl_family['mcast']:
|
|
|
|
raise Exception(f'Multicast group "{mcast_name}" not present in the family')
|
|
|
|
|
|
|
|
self.sock.bind((0, 0))
|
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP,
|
|
|
|
self.family.genl_family['mcast'][mcast_name])
|
|
|
|
|
|
|
|
def _add_attr(self, space, name, value):
|
2023-01-31 10:33:44 +08:00
|
|
|
attr = self.attr_sets[space][name]
|
|
|
|
nl_type = attr.value
|
2023-01-21 01:50:41 +08:00
|
|
|
if attr["type"] == 'nest':
|
|
|
|
nl_type |= Netlink.NLA_F_NESTED
|
|
|
|
attr_payload = b''
|
|
|
|
for subname, subvalue in value.items():
|
|
|
|
attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue)
|
2023-01-31 10:33:45 +08:00
|
|
|
elif attr["type"] == 'flag':
|
|
|
|
attr_payload = b''
|
ynl: allow to encode u8 attr
Playing with dpll netlink, I came across following issue:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "pin-idx": 1, "pin-state": 1}'
Traceback (most recent call last):
File "tools/net/ynl/cli.py", line 52, in <module>
main()
File "tools/net/ynl/cli.py", line 40, in main
reply = ynl.do(args.do, attrs)
File "tools/net/ynl/lib/ynl.py", line 520, in do
return self._op(method, vals)
File "tools/net/ynl/lib/ynl.py", line 476, in _op
msg += self._add_attr(op.attr_set.name, name, value)
File "tools/net/ynl/lib/ynl.py", line 344, in _add_attr
raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}')
Exception: Unknown type at dpll pin-state 1 u8
I'm not that familiar with ynl code, but from a quick peek, I suspect
that couple other types are missing for both encoding and decoding.
Ignoring those here as I'm scratching my local itch only.
Fix the issue by adding u8 attr packing.
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20230322154242.1739136-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-03-22 23:42:42 +08:00
|
|
|
elif attr["type"] == 'u8':
|
|
|
|
attr_payload = struct.pack("B", int(value))
|
2023-03-25 01:52:58 +08:00
|
|
|
elif attr["type"] == 'u16':
|
|
|
|
attr_payload = struct.pack("H", int(value))
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr["type"] == 'u32':
|
|
|
|
attr_payload = struct.pack("I", int(value))
|
2023-03-25 01:52:58 +08:00
|
|
|
elif attr["type"] == 'u64':
|
|
|
|
attr_payload = struct.pack("Q", int(value))
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr["type"] == 'string':
|
|
|
|
attr_payload = str(value).encode('ascii') + b'\x00'
|
|
|
|
elif attr["type"] == 'binary':
|
|
|
|
attr_payload = value
|
|
|
|
else:
|
|
|
|
raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}')
|
|
|
|
|
|
|
|
pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4)
|
|
|
|
return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad
|
|
|
|
|
|
|
|
def _decode_enum(self, rsp, attr_spec):
|
|
|
|
raw = rsp[attr_spec['name']]
|
2023-03-08 08:39:23 +08:00
|
|
|
enum = self.consts[attr_spec['enum']]
|
2023-01-21 01:50:41 +08:00
|
|
|
i = attr_spec.get('value-start', 0)
|
|
|
|
if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
|
|
|
|
value = set()
|
|
|
|
while raw:
|
|
|
|
if raw & 1:
|
2023-03-08 08:39:23 +08:00
|
|
|
value.add(enum.entries_by_val[i].name)
|
2023-01-21 01:50:41 +08:00
|
|
|
raw >>= 1
|
|
|
|
i += 1
|
|
|
|
else:
|
2023-03-20 03:37:58 +08:00
|
|
|
value = enum.entries_by_val[raw - i].name
|
2023-01-21 01:50:41 +08:00
|
|
|
rsp[attr_spec['name']] = value
|
|
|
|
|
2023-03-27 16:31:33 +08:00
|
|
|
def _decode_binary(self, attr, attr_spec):
|
|
|
|
if attr_spec.sub_type:
|
|
|
|
decoded = attr.as_c_array(attr_spec.sub_type)
|
|
|
|
else:
|
|
|
|
decoded = attr.as_bin()
|
|
|
|
return decoded
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def _decode(self, attrs, space):
|
2023-01-31 10:33:44 +08:00
|
|
|
attr_space = self.attr_sets[space]
|
2023-01-21 01:50:41 +08:00
|
|
|
rsp = dict()
|
|
|
|
for attr in attrs:
|
2023-01-31 10:33:44 +08:00
|
|
|
attr_spec = attr_space.attrs_by_val[attr.type]
|
2023-01-21 01:50:41 +08:00
|
|
|
if attr_spec["type"] == 'nest':
|
|
|
|
subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes'])
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = subdict
|
2023-01-31 10:33:45 +08:00
|
|
|
elif attr_spec['type'] == 'u8':
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = attr.as_u8()
|
2023-03-25 01:52:58 +08:00
|
|
|
elif attr_spec['type'] == 'u16':
|
|
|
|
decoded = attr.as_u16()
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr_spec['type'] == 'u32':
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = attr.as_u32()
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr_spec['type'] == 'u64':
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = attr.as_u64()
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr_spec["type"] == 'string':
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = attr.as_strz()
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr_spec["type"] == 'binary':
|
2023-03-27 16:31:33 +08:00
|
|
|
decoded = self._decode_binary(attr, attr_spec)
|
2023-01-31 10:33:45 +08:00
|
|
|
elif attr_spec["type"] == 'flag':
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = True
|
2023-01-21 01:50:41 +08:00
|
|
|
else:
|
|
|
|
raise Exception(f'Unknown {attr.type} {attr_spec["name"]} {attr_spec["type"]}')
|
|
|
|
|
2023-01-31 10:33:47 +08:00
|
|
|
if not attr_spec.is_multi:
|
|
|
|
rsp[attr_spec['name']] = decoded
|
|
|
|
elif attr_spec.name in rsp:
|
|
|
|
rsp[attr_spec.name].append(decoded)
|
|
|
|
else:
|
|
|
|
rsp[attr_spec.name] = [decoded]
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
if 'enum' in attr_spec:
|
|
|
|
self._decode_enum(rsp, attr_spec)
|
|
|
|
return rsp
|
|
|
|
|
2023-01-31 10:33:48 +08:00
|
|
|
def _decode_extack_path(self, attrs, attr_set, offset, target):
|
|
|
|
for attr in attrs:
|
|
|
|
attr_spec = attr_set.attrs_by_val[attr.type]
|
|
|
|
if offset > target:
|
|
|
|
break
|
|
|
|
if offset == target:
|
|
|
|
return '.' + attr_spec.name
|
|
|
|
|
|
|
|
if offset + attr.full_len <= target:
|
|
|
|
offset += attr.full_len
|
|
|
|
continue
|
|
|
|
if attr_spec['type'] != 'nest':
|
|
|
|
raise Exception(f"Can't dive into {attr.type} ({attr_spec['name']}) for extack")
|
|
|
|
offset += 4
|
|
|
|
subpath = self._decode_extack_path(NlAttrs(attr.raw),
|
|
|
|
self.attr_sets[attr_spec['nested-attributes']],
|
|
|
|
offset, target)
|
|
|
|
if subpath is None:
|
|
|
|
return None
|
|
|
|
return '.' + attr_spec.name + subpath
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
def _decode_extack(self, request, attr_space, extack):
|
|
|
|
if 'bad-attr-offs' not in extack:
|
|
|
|
return
|
|
|
|
|
|
|
|
genl_req = GenlMsg(NlMsg(request, 0, attr_space=attr_space))
|
|
|
|
path = self._decode_extack_path(genl_req.raw_attrs, attr_space,
|
|
|
|
20, extack['bad-attr-offs'])
|
|
|
|
if path:
|
|
|
|
del extack['bad-attr-offs']
|
|
|
|
extack['bad-attr'] = path
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def handle_ntf(self, nl_msg, genl_msg):
|
|
|
|
msg = dict()
|
|
|
|
if self.include_raw:
|
|
|
|
msg['nlmsg'] = nl_msg
|
|
|
|
msg['genlmsg'] = genl_msg
|
2023-01-31 10:33:46 +08:00
|
|
|
op = self.rsp_by_value[genl_msg.genl_cmd]
|
2023-01-21 01:50:41 +08:00
|
|
|
msg['name'] = op['name']
|
2023-01-31 10:33:44 +08:00
|
|
|
msg['msg'] = self._decode(genl_msg.raw_attrs, op.attr_set.name)
|
2023-01-21 01:50:41 +08:00
|
|
|
self.async_msg_queue.append(msg)
|
|
|
|
|
|
|
|
def check_ntf(self):
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
reply = self.sock.recv(128 * 1024, socket.MSG_DONTWAIT)
|
|
|
|
except BlockingIOError:
|
|
|
|
return
|
|
|
|
|
|
|
|
nms = NlMsgs(reply)
|
|
|
|
for nl_msg in nms:
|
|
|
|
if nl_msg.error:
|
|
|
|
print("Netlink error in ntf!?", os.strerror(-nl_msg.error))
|
|
|
|
print(nl_msg)
|
|
|
|
continue
|
|
|
|
if nl_msg.done:
|
|
|
|
print("Netlink done while checking for ntf!?")
|
|
|
|
continue
|
|
|
|
|
|
|
|
gm = GenlMsg(nl_msg)
|
|
|
|
if gm.genl_cmd not in self.async_msg_ids:
|
|
|
|
print("Unexpected msg id done while checking for ntf", gm)
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.handle_ntf(nl_msg, gm)
|
|
|
|
|
|
|
|
def _op(self, method, vals, dump=False):
|
2023-01-31 10:33:44 +08:00
|
|
|
op = self.ops[method]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK
|
|
|
|
if dump:
|
|
|
|
nl_flags |= Netlink.NLM_F_DUMP
|
|
|
|
|
|
|
|
req_seq = random.randint(1024, 65535)
|
2023-01-31 10:33:46 +08:00
|
|
|
msg = _genl_msg(self.family.family_id, nl_flags, op.req_value, 1, req_seq)
|
2023-01-21 01:50:41 +08:00
|
|
|
for name, value in vals.items():
|
2023-01-31 10:33:44 +08:00
|
|
|
msg += self._add_attr(op.attr_set.name, name, value)
|
2023-01-21 01:50:41 +08:00
|
|
|
msg = _genl_msg_finalize(msg)
|
|
|
|
|
|
|
|
self.sock.send(msg, 0)
|
|
|
|
|
|
|
|
done = False
|
|
|
|
rsp = []
|
|
|
|
while not done:
|
|
|
|
reply = self.sock.recv(128 * 1024)
|
2023-01-31 10:33:44 +08:00
|
|
|
nms = NlMsgs(reply, attr_space=op.attr_set)
|
2023-01-21 01:50:41 +08:00
|
|
|
for nl_msg in nms:
|
2023-01-31 10:33:48 +08:00
|
|
|
if nl_msg.extack:
|
|
|
|
self._decode_extack(msg, op.attr_set, nl_msg.extack)
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
if nl_msg.error:
|
|
|
|
print("Netlink error:", os.strerror(-nl_msg.error))
|
|
|
|
print(nl_msg)
|
|
|
|
return
|
|
|
|
if nl_msg.done:
|
2023-01-31 10:33:48 +08:00
|
|
|
if nl_msg.extack:
|
|
|
|
print("Netlink warning:")
|
|
|
|
print(nl_msg)
|
2023-01-21 01:50:41 +08:00
|
|
|
done = True
|
|
|
|
break
|
|
|
|
|
|
|
|
gm = GenlMsg(nl_msg)
|
|
|
|
# Check if this is a reply to our request
|
2023-01-31 10:33:46 +08:00
|
|
|
if nl_msg.nl_seq != req_seq or gm.genl_cmd != op.rsp_value:
|
2023-01-21 01:50:41 +08:00
|
|
|
if gm.genl_cmd in self.async_msg_ids:
|
|
|
|
self.handle_ntf(nl_msg, gm)
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
print('Unexpected message: ' + repr(gm))
|
|
|
|
continue
|
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
rsp.append(self._decode(gm.raw_attrs, op.attr_set.name))
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
if not rsp:
|
|
|
|
return None
|
|
|
|
if not dump and len(rsp) == 1:
|
|
|
|
return rsp[0]
|
|
|
|
return rsp
|
2023-01-31 10:33:49 +08:00
|
|
|
|
|
|
|
def do(self, method, vals):
|
|
|
|
return self._op(method, vals)
|
|
|
|
|
|
|
|
def dump(self, method, vals):
|
|
|
|
return self._op(method, vals, dump=True)
|