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
|
|
|
|
2023-05-23 17:37:47 +08:00
|
|
|
from collections import namedtuple
|
2024-03-28 23:56:36 +08:00
|
|
|
from enum import Enum
|
2023-01-21 01:50:41 +08:00
|
|
|
import functools
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import socket
|
|
|
|
import struct
|
2023-05-23 17:37:47 +08:00
|
|
|
from struct import Struct
|
2024-03-05 13:33:09 +08:00
|
|
|
import sys
|
2023-01-21 01:50:41 +08:00
|
|
|
import yaml
|
2023-06-24 04:19:27 +08:00
|
|
|
import ipaddress
|
|
|
|
import uuid
|
2023-01-21 01:50:41 +08:00
|
|
|
|
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
|
2023-08-25 20:27:50 +08:00
|
|
|
NETLINK_GET_STRICT_CHK = 12
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
# Netlink message
|
|
|
|
NLMSG_ERROR = 2
|
|
|
|
NLMSG_DONE = 3
|
|
|
|
|
|
|
|
NLM_F_REQUEST = 1
|
|
|
|
NLM_F_ACK = 4
|
|
|
|
NLM_F_ROOT = 0x100
|
|
|
|
NLM_F_MATCH = 0x200
|
2023-08-25 20:27:52 +08:00
|
|
|
|
|
|
|
NLM_F_REPLACE = 0x100
|
|
|
|
NLM_F_EXCL = 0x200
|
|
|
|
NLM_F_CREATE = 0x400
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
2024-03-28 23:56:36 +08:00
|
|
|
# Policy types
|
|
|
|
NL_POLICY_TYPE_ATTR_TYPE = 1
|
|
|
|
NL_POLICY_TYPE_ATTR_MIN_VALUE_S = 2
|
|
|
|
NL_POLICY_TYPE_ATTR_MAX_VALUE_S = 3
|
|
|
|
NL_POLICY_TYPE_ATTR_MIN_VALUE_U = 4
|
|
|
|
NL_POLICY_TYPE_ATTR_MAX_VALUE_U = 5
|
|
|
|
NL_POLICY_TYPE_ATTR_MIN_LENGTH = 6
|
|
|
|
NL_POLICY_TYPE_ATTR_MAX_LENGTH = 7
|
|
|
|
NL_POLICY_TYPE_ATTR_POLICY_IDX = 8
|
|
|
|
NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE = 9
|
|
|
|
NL_POLICY_TYPE_ATTR_BITFIELD32_MASK = 10
|
|
|
|
NL_POLICY_TYPE_ATTR_PAD = 11
|
|
|
|
NL_POLICY_TYPE_ATTR_MASK = 12
|
|
|
|
|
|
|
|
AttrType = Enum('AttrType', ['flag', 'u8', 'u16', 'u32', 'u64',
|
|
|
|
's8', 's16', 's32', 's64',
|
|
|
|
'binary', 'string', 'nul-string',
|
|
|
|
'nested', 'nested-array',
|
|
|
|
'bitfield32', 'sint', 'uint'])
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-03-30 06:16:54 +08:00
|
|
|
class NlError(Exception):
|
|
|
|
def __init__(self, nl_msg):
|
|
|
|
self.nl_msg = nl_msg
|
2024-04-03 10:34:21 +08:00
|
|
|
self.error = -nl_msg.error
|
2023-03-30 06:16:54 +08:00
|
|
|
|
|
|
|
def __str__(self):
|
2024-04-03 10:34:21 +08:00
|
|
|
return f"Netlink error: {os.strerror(self.error)}\n{self.nl_msg}"
|
2023-03-30 06:16:54 +08:00
|
|
|
|
|
|
|
|
2024-03-05 13:33:08 +08:00
|
|
|
class ConfigError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
class NlAttr:
|
2023-05-23 17:37:47 +08:00
|
|
|
ScalarFormat = namedtuple('ScalarFormat', ['native', 'big', 'little'])
|
|
|
|
type_formats = {
|
|
|
|
'u8' : ScalarFormat(Struct('B'), Struct("B"), Struct("B")),
|
|
|
|
's8' : ScalarFormat(Struct('b'), Struct("b"), Struct("b")),
|
|
|
|
'u16': ScalarFormat(Struct('H'), Struct(">H"), Struct("<H")),
|
|
|
|
's16': ScalarFormat(Struct('h'), Struct(">h"), Struct("<h")),
|
|
|
|
'u32': ScalarFormat(Struct('I'), Struct(">I"), Struct("<I")),
|
|
|
|
's32': ScalarFormat(Struct('i'), Struct(">i"), Struct("<i")),
|
|
|
|
'u64': ScalarFormat(Struct('Q'), Struct(">Q"), Struct("<Q")),
|
|
|
|
's64': ScalarFormat(Struct('q'), Struct(">q"), Struct("<q"))
|
|
|
|
}
|
2023-03-27 16:31:33 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def __init__(self, raw, offset):
|
2023-12-15 17:37:08 +08:00
|
|
|
self._len, self._type = struct.unpack("HH", raw[offset : offset + 4])
|
2023-01-21 01:50:41 +08:00
|
|
|
self.type = self._type & ~Netlink.NLA_TYPE_MASK
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
self.is_nest = self._type & Netlink.NLA_F_NESTED
|
2023-01-21 01:50:41 +08:00
|
|
|
self.payload_len = self._len
|
|
|
|
self.full_len = (self.payload_len + 3) & ~3
|
2023-12-15 17:37:08 +08:00
|
|
|
self.raw = raw[offset + 4 : offset + self.payload_len]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-05-23 17:37:47 +08:00
|
|
|
@classmethod
|
|
|
|
def get_format(cls, attr_type, byte_order=None):
|
|
|
|
format = cls.type_formats[attr_type]
|
2023-03-30 06:16:52 +08:00
|
|
|
if byte_order:
|
2023-05-23 17:37:47 +08:00
|
|
|
return format.big if byte_order == "big-endian" \
|
|
|
|
else format.little
|
|
|
|
return format.native
|
2023-03-30 06:16:52 +08:00
|
|
|
|
2023-05-23 17:37:47 +08:00
|
|
|
def as_scalar(self, attr_type, byte_order=None):
|
|
|
|
format = self.get_format(attr_type, byte_order)
|
|
|
|
return format.unpack(self.raw)[0]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-10-19 05:39:21 +08:00
|
|
|
def as_auto_scalar(self, attr_type, byte_order=None):
|
|
|
|
if len(self.raw) != 4 and len(self.raw) != 8:
|
|
|
|
raise Exception(f"Auto-scalar len payload be 4 or 8 bytes, got {len(self.raw)}")
|
|
|
|
real_type = attr_type[0] + str(len(self.raw) * 8)
|
|
|
|
format = self.get_format(real_type, byte_order)
|
|
|
|
return format.unpack(self.raw)[0]
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
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):
|
2023-05-23 17:37:47 +08:00
|
|
|
format = self.get_format(type)
|
|
|
|
return [ x[0] for x in format.iter_unpack(self.raw) ]
|
2023-03-27 16:31:33 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return f"[type:{self.type} len:{self._len}] {self.raw}"
|
|
|
|
|
|
|
|
|
|
|
|
class NlAttrs:
|
2023-12-15 17:37:11 +08:00
|
|
|
def __init__(self, msg, offset=0):
|
2023-01-21 01:50:41 +08:00
|
|
|
self.attrs = []
|
|
|
|
|
|
|
|
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):
|
2023-12-15 17:37:08 +08:00
|
|
|
self.hdr = msg[offset : offset + 16]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \
|
|
|
|
struct.unpack("IHHII", self.hdr)
|
|
|
|
|
2023-12-15 17:37:08 +08:00
|
|
|
self.raw = msg[offset + 16 : offset + self.nl_len]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
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:
|
2023-05-23 17:37:47 +08:00
|
|
|
self.extack['miss-type'] = extack.as_scalar('u32')
|
2023-01-21 01:50:41 +08:00
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST:
|
2023-05-23 17:37:47 +08:00
|
|
|
self.extack['miss-nest'] = extack.as_scalar('u32')
|
2023-01-21 01:50:41 +08:00
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_OFFS:
|
2023-05-23 17:37:47 +08:00
|
|
|
self.extack['bad-attr-offs'] = extack.as_scalar('u32')
|
2024-03-28 23:56:36 +08:00
|
|
|
elif extack.type == Netlink.NLMSGERR_ATTR_POLICY:
|
|
|
|
self.extack['policy'] = self._decode_policy(extack.raw)
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
2024-03-28 23:56:36 +08:00
|
|
|
def _decode_policy(self, raw):
|
|
|
|
policy = {}
|
|
|
|
for attr in NlAttrs(raw):
|
|
|
|
if attr.type == Netlink.NL_POLICY_TYPE_ATTR_TYPE:
|
|
|
|
type = attr.as_scalar('u32')
|
|
|
|
policy['type'] = Netlink.AttrType(type).name
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_VALUE_S:
|
|
|
|
policy['min-value'] = attr.as_scalar('s64')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_VALUE_S:
|
|
|
|
policy['max-value'] = attr.as_scalar('s64')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_VALUE_U:
|
|
|
|
policy['min-value'] = attr.as_scalar('u64')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_VALUE_U:
|
|
|
|
policy['max-value'] = attr.as_scalar('u64')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MIN_LENGTH:
|
|
|
|
policy['min-length'] = attr.as_scalar('u32')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MAX_LENGTH:
|
|
|
|
policy['max-length'] = attr.as_scalar('u32')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_BITFIELD32_MASK:
|
|
|
|
policy['bitfield32-mask'] = attr.as_scalar('u32')
|
|
|
|
elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MASK:
|
|
|
|
policy['mask'] = attr.as_scalar('u64')
|
|
|
|
return policy
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
def cmd(self):
|
|
|
|
return self.nl_type
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
def __repr__(self):
|
2024-03-05 13:33:07 +08:00
|
|
|
msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}"
|
2023-01-21 01:50:41 +08:00
|
|
|
if self.error:
|
2024-03-05 13:33:07 +08:00
|
|
|
msg += '\n\terror: ' + str(self.error)
|
2023-01-21 01:50:41 +08:00
|
|
|
if self.extack:
|
2024-03-05 13:33:07 +08:00
|
|
|
msg += '\n\textack: ' + repr(self.extack)
|
2023-01-21 01:50:41 +08:00
|
|
|
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()
|
2023-08-25 20:27:49 +08:00
|
|
|
for attr in NlAttrs(gm.raw):
|
2023-01-21 01:50:41 +08:00
|
|
|
if attr.type == Netlink.CTRL_ATTR_FAMILY_ID:
|
2023-05-23 17:37:47 +08:00
|
|
|
fam['id'] = attr.as_scalar('u16')
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME:
|
|
|
|
fam['name'] = attr.as_strz()
|
|
|
|
elif attr.type == Netlink.CTRL_ATTR_MAXATTR:
|
2023-05-23 17:37:47 +08:00
|
|
|
fam['maxattr'] = attr.as_scalar('u32')
|
2023-01-21 01:50:41 +08:00
|
|
|
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:
|
2023-05-23 17:37:47 +08:00
|
|
|
mcast_id = entry_attr.as_scalar('u32')
|
2023-01-21 01:50:41 +08:00
|
|
|
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:
|
2023-08-25 20:27:49 +08:00
|
|
|
def __init__(self, nl_msg):
|
2023-01-21 01:50:41 +08:00
|
|
|
self.nl = nl_msg
|
2023-08-25 20:27:49 +08:00
|
|
|
self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0)
|
|
|
|
self.raw = nl_msg.raw[4:]
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
def cmd(self):
|
|
|
|
return self.genl_cmd
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
class NetlinkProtocol:
|
|
|
|
def __init__(self, family_name, proto_num):
|
2023-01-21 01:50:41 +08:00
|
|
|
self.family_name = family_name
|
2023-08-25 20:27:50 +08:00
|
|
|
self.proto_num = proto_num
|
|
|
|
|
|
|
|
def _message(self, nl_type, nl_flags, seq=None):
|
|
|
|
if seq is None:
|
|
|
|
seq = random.randint(1, 1024)
|
|
|
|
nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0)
|
|
|
|
return nlmsg
|
|
|
|
|
|
|
|
def message(self, flags, command, version, seq=None):
|
|
|
|
return self._message(command, flags, seq)
|
|
|
|
|
|
|
|
def _decode(self, nl_msg):
|
|
|
|
return nl_msg
|
|
|
|
|
|
|
|
def decode(self, ynl, nl_msg):
|
|
|
|
msg = self._decode(nl_msg)
|
|
|
|
fixed_header_size = 0
|
|
|
|
if ynl:
|
|
|
|
op = ynl.rsp_by_value[msg.cmd()]
|
2024-01-30 06:34:53 +08:00
|
|
|
fixed_header_size = ynl._struct_size(op.fixed_header)
|
2023-12-15 17:37:11 +08:00
|
|
|
msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size)
|
2023-08-25 20:27:50 +08:00
|
|
|
return msg
|
|
|
|
|
|
|
|
def get_mcast_id(self, mcast_name, mcast_groups):
|
|
|
|
if mcast_name not in mcast_groups:
|
|
|
|
raise Exception(f'Multicast group "{mcast_name}" not present in the spec')
|
|
|
|
return mcast_groups[mcast_name].value
|
|
|
|
|
2024-03-07 07:10:41 +08:00
|
|
|
def msghdr_size(self):
|
|
|
|
return 16
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
|
|
|
|
class GenlProtocol(NetlinkProtocol):
|
|
|
|
def __init__(self, family_name):
|
|
|
|
super().__init__(family_name, Netlink.NETLINK_GENERIC)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
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']
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
def message(self, flags, command, version, seq=None):
|
|
|
|
nlmsg = self._message(self.family_id, flags, seq)
|
|
|
|
genlmsg = struct.pack("BBH", command, version, 0)
|
|
|
|
return nlmsg + genlmsg
|
|
|
|
|
|
|
|
def _decode(self, nl_msg):
|
|
|
|
return GenlMsg(nl_msg)
|
|
|
|
|
|
|
|
def get_mcast_id(self, mcast_name, mcast_groups):
|
|
|
|
if mcast_name not in self.genl_family['mcast']:
|
|
|
|
raise Exception(f'Multicast group "{mcast_name}" not present in the family')
|
|
|
|
return self.genl_family['mcast'][mcast_name]
|
|
|
|
|
2024-03-07 07:10:41 +08:00
|
|
|
def msghdr_size(self):
|
|
|
|
return super().msghdr_size() + 4
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2024-01-30 06:34:47 +08:00
|
|
|
|
|
|
|
class SpaceAttrs:
|
|
|
|
SpecValuesPair = namedtuple('SpecValuesPair', ['spec', 'values'])
|
|
|
|
|
|
|
|
def __init__(self, attr_space, attrs, outer = None):
|
|
|
|
outer_scopes = outer.scopes if outer else []
|
|
|
|
inner_scope = self.SpecValuesPair(attr_space, attrs)
|
|
|
|
self.scopes = [inner_scope] + outer_scopes
|
|
|
|
|
|
|
|
def lookup(self, name):
|
|
|
|
for scope in self.scopes:
|
|
|
|
if name in scope.spec:
|
|
|
|
if name in scope.values:
|
|
|
|
return scope.values[name]
|
|
|
|
spec_name = scope.spec.yaml['name']
|
|
|
|
raise Exception(
|
|
|
|
f"No value for '{name}' in attribute space '{spec_name}'")
|
|
|
|
raise Exception(f"Attribute '{name}' not defined in any attribute-set")
|
|
|
|
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
#
|
|
|
|
# YNL implementation details.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
2023-01-31 10:33:44 +08:00
|
|
|
class YnlFamily(SpecFamily):
|
2024-03-05 13:33:08 +08:00
|
|
|
def __init__(self, def_path, schema=None, process_unknown=False,
|
|
|
|
recv_size=0):
|
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
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
self.process_unknown = process_unknown
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
try:
|
|
|
|
if self.proto == "netlink-raw":
|
|
|
|
self.nlproto = NetlinkProtocol(self.yaml['name'],
|
|
|
|
self.yaml['protonum'])
|
|
|
|
else:
|
|
|
|
self.nlproto = GenlProtocol(self.yaml['name'])
|
|
|
|
except KeyError:
|
|
|
|
raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel")
|
|
|
|
|
2024-03-05 13:33:09 +08:00
|
|
|
self._recv_dbg = False
|
2024-03-05 13:33:08 +08:00
|
|
|
# Note that netlink will use conservative (min) message size for
|
|
|
|
# the first dump recv() on the socket, our setting will only matter
|
|
|
|
# from the second recv() on.
|
|
|
|
self._recv_size = recv_size if recv_size else 131072
|
|
|
|
# Netlink will always allocate at least PAGE_SIZE - sizeof(skb_shinfo)
|
|
|
|
# for a message, so smaller receive sizes will lead to truncation.
|
|
|
|
# Note that the min size for other families may be larger than 4k!
|
|
|
|
if self._recv_size < 4000:
|
|
|
|
raise ConfigError()
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.nlproto.proto_num)
|
2023-01-21 01:50:41 +08:00
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
|
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1)
|
2023-08-25 20:27:50 +08:00
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_GET_STRICT_CHK, 1)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
def ntf_subscribe(self, mcast_name):
|
2023-08-25 20:27:50 +08:00
|
|
|
mcast_id = self.nlproto.get_mcast_id(mcast_name, self.mcast_groups)
|
2023-01-21 01:50:41 +08:00
|
|
|
self.sock.bind((0, 0))
|
|
|
|
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP,
|
2023-08-25 20:27:50 +08:00
|
|
|
mcast_id)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2024-03-05 13:33:09 +08:00
|
|
|
def set_recv_dbg(self, enabled):
|
|
|
|
self._recv_dbg = enabled
|
|
|
|
|
|
|
|
def _recv_dbg_print(self, reply, nl_msgs):
|
|
|
|
if not self._recv_dbg:
|
|
|
|
return
|
|
|
|
print("Recv: read", len(reply), "bytes,",
|
|
|
|
len(nl_msgs.msgs), "messages", file=sys.stderr)
|
|
|
|
for nl_msg in nl_msgs:
|
|
|
|
print(" ", nl_msg, file=sys.stderr)
|
|
|
|
|
tools: ynl: allow user to pass enum string instead of scalar value
During decoding of messages coming from kernel, attribute values are
converted to enum names in case the attribute type is enum of bitfield32.
However, when user constructs json message, he has to pass plain scalar
values. See "state" "selector" and "value" attributes in following
examples:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": 1}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": 1, "value": 1 }}}'
Allow user to pass strings containing enum names, convert them to scalar
values to be encoded into Netlink message:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": "connected"}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": ["roce-bit"], "value": ["roce-bit"] }}}'
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240222134351.224704-4-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-02-22 21:43:51 +08:00
|
|
|
def _encode_enum(self, attr_spec, value):
|
|
|
|
enum = self.consts[attr_spec['enum']]
|
|
|
|
if enum.type == 'flags' or attr_spec.get('enum-as-flags', False):
|
|
|
|
scalar = 0
|
|
|
|
if isinstance(value, str):
|
|
|
|
value = [value]
|
|
|
|
for single_value in value:
|
|
|
|
scalar += enum.entries[single_value].user_value(as_flags = True)
|
|
|
|
return scalar
|
|
|
|
else:
|
|
|
|
return enum.entries[value].user_value()
|
|
|
|
|
|
|
|
def _get_scalar(self, attr_spec, value):
|
|
|
|
try:
|
|
|
|
return int(value)
|
|
|
|
except (ValueError, TypeError) as e:
|
|
|
|
if 'enum' not in attr_spec:
|
|
|
|
raise e
|
2024-03-09 03:25:55 +08:00
|
|
|
return self._encode_enum(attr_spec, value)
|
tools: ynl: allow user to pass enum string instead of scalar value
During decoding of messages coming from kernel, attribute values are
converted to enum names in case the attribute type is enum of bitfield32.
However, when user constructs json message, he has to pass plain scalar
values. See "state" "selector" and "value" attributes in following
examples:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": 1}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": 1, "value": 1 }}}'
Allow user to pass strings containing enum names, convert them to scalar
values to be encoded into Netlink message:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": "connected"}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": ["roce-bit"], "value": ["roce-bit"] }}}'
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240222134351.224704-4-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-02-22 21:43:51 +08:00
|
|
|
|
2024-01-30 06:34:50 +08:00
|
|
|
def _add_attr(self, space, name, value, search_attrs):
|
2023-08-15 04:56:27 +08:00
|
|
|
try:
|
|
|
|
attr = self.attr_sets[space][name]
|
|
|
|
except KeyError:
|
|
|
|
raise Exception(f"Space '{space}' has no attribute '{name}'")
|
2023-01-31 10:33:44 +08:00
|
|
|
nl_type = attr.value
|
2024-02-03 21:16:53 +08:00
|
|
|
|
|
|
|
if attr.is_multi and isinstance(value, list):
|
|
|
|
attr_payload = b''
|
|
|
|
for subvalue in value:
|
|
|
|
attr_payload += self._add_attr(space, name, subvalue, search_attrs)
|
|
|
|
return attr_payload
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
if attr["type"] == 'nest':
|
|
|
|
nl_type |= Netlink.NLA_F_NESTED
|
|
|
|
attr_payload = b''
|
2024-01-30 06:34:50 +08:00
|
|
|
sub_attrs = SpaceAttrs(self.attr_sets[space], value, search_attrs)
|
2023-01-21 01:50:41 +08:00
|
|
|
for subname, subvalue in value.items():
|
2024-01-30 06:34:50 +08:00
|
|
|
attr_payload += self._add_attr(attr['nested-attributes'],
|
|
|
|
subname, subvalue, sub_attrs)
|
2023-01-31 10:33:45 +08:00
|
|
|
elif attr["type"] == 'flag':
|
2024-02-22 21:43:49 +08:00
|
|
|
if not value:
|
|
|
|
# If value is absent or false then skip attribute creation.
|
|
|
|
return b''
|
2023-01-31 10:33:45 +08:00
|
|
|
attr_payload = b''
|
2023-01-21 01:50:41 +08:00
|
|
|
elif attr["type"] == 'string':
|
|
|
|
attr_payload = str(value).encode('ascii') + b'\x00'
|
|
|
|
elif attr["type"] == 'binary':
|
2023-08-24 08:30:52 +08:00
|
|
|
if isinstance(value, bytes):
|
|
|
|
attr_payload = value
|
|
|
|
elif isinstance(value, str):
|
|
|
|
attr_payload = bytes.fromhex(value)
|
2024-01-30 06:34:50 +08:00
|
|
|
elif isinstance(value, dict) and attr.struct_name:
|
|
|
|
attr_payload = self._encode_struct(attr.struct_name, value)
|
2023-08-24 08:30:52 +08:00
|
|
|
else:
|
|
|
|
raise Exception(f'Unknown type for binary attribute, value: {value}')
|
2024-02-22 21:43:50 +08:00
|
|
|
elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar:
|
tools: ynl: allow user to pass enum string instead of scalar value
During decoding of messages coming from kernel, attribute values are
converted to enum names in case the attribute type is enum of bitfield32.
However, when user constructs json message, he has to pass plain scalar
values. See "state" "selector" and "value" attributes in following
examples:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": 1}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": 1, "value": 1 }}}'
Allow user to pass strings containing enum names, convert them to scalar
values to be encoded into Netlink message:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": "connected"}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": ["roce-bit"], "value": ["roce-bit"] }}}'
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240222134351.224704-4-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-02-22 21:43:51 +08:00
|
|
|
scalar = self._get_scalar(attr, value)
|
2024-02-22 21:43:50 +08:00
|
|
|
if attr.is_auto_scalar:
|
|
|
|
attr_type = attr["type"][0] + ('32' if scalar.bit_length() <= 32 else '64')
|
|
|
|
else:
|
|
|
|
attr_type = attr["type"]
|
|
|
|
format = NlAttr.get_format(attr_type, attr.byte_order)
|
|
|
|
attr_payload = format.pack(scalar)
|
2023-10-21 19:27:03 +08:00
|
|
|
elif attr['type'] in "bitfield32":
|
tools: ynl: allow user to pass enum string instead of scalar value
During decoding of messages coming from kernel, attribute values are
converted to enum names in case the attribute type is enum of bitfield32.
However, when user constructs json message, he has to pass plain scalar
values. See "state" "selector" and "value" attributes in following
examples:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": 1}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": 1, "value": 1 }}}'
Allow user to pass strings containing enum names, convert them to scalar
values to be encoded into Netlink message:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-set --json '{"id": 0, "parent-device": {"parent-id": 0, "state": "connected"}}'
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do port-set --json '{"bus-name": "pci", "dev-name": "0000:08:00.1", "port-index": 98304, "port-function": {"caps": {"selector": ["roce-bit"], "value": ["roce-bit"] }}}'
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20240222134351.224704-4-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-02-22 21:43:51 +08:00
|
|
|
scalar_value = self._get_scalar(attr, value["value"])
|
|
|
|
scalar_selector = self._get_scalar(attr, value["selector"])
|
|
|
|
attr_payload = struct.pack("II", scalar_value, scalar_selector)
|
2024-01-30 06:34:50 +08:00
|
|
|
elif attr['type'] == 'sub-message':
|
|
|
|
msg_format = self._resolve_selector(attr, search_attrs)
|
|
|
|
attr_payload = b''
|
|
|
|
if msg_format.fixed_header:
|
|
|
|
attr_payload += self._encode_struct(msg_format.fixed_header, value)
|
|
|
|
if msg_format.attr_set:
|
|
|
|
if msg_format.attr_set in self.attr_sets:
|
|
|
|
nl_type |= Netlink.NLA_F_NESTED
|
|
|
|
sub_attrs = SpaceAttrs(msg_format.attr_set, value, search_attrs)
|
|
|
|
for subname, subvalue in value.items():
|
|
|
|
attr_payload += self._add_attr(msg_format.attr_set,
|
|
|
|
subname, subvalue, sub_attrs)
|
|
|
|
else:
|
|
|
|
raise Exception(f"Unknown attribute-set '{msg_format.attr_set}'")
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
tools: ynl-gen: fix parse multi-attr enum attribute
When attribute is enum type and marked as multi-attr, the netlink
respond is not parsed, fails with stack trace:
Traceback (most recent call last):
File "/net-next/tools/net/ynl/./test.py", line 520, in <module>
main()
File "/net-next/tools/net/ynl/./test.py", line 488, in main
dplls=dplls_get(282574471561216)
File "/net-next/tools/net/ynl/./test.py", line 48, in dplls_get
reply=act(args)
File "/net-next/tools/net/ynl/./test.py", line 41, in act
reply = ynl.dump(args.dump, attrs)
File "/net-next/tools/net/ynl/lib/ynl.py", line 598, in dump
return self._op(method, vals, dump=True)
File "/net-next/tools/net/ynl/lib/ynl.py", line 584, in _op
rsp_msg = self._decode(gm.raw_attrs, op.attr_set.name)
File "/net-next/tools/net/ynl/lib/ynl.py", line 451, in _decode
self._decode_enum(rsp, attr_spec)
File "/net-next/tools/net/ynl/lib/ynl.py", line 408, in _decode_enum
value = enum.entries_by_val[raw].name
TypeError: unhashable type: 'list'
error: 1
Redesign _decode_enum(..) to take a enum int value and translate
it to either a bitmask or enum name as expected.
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20230725101642.267248-3-arkadiusz.kubalewski@intel.com
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-07-25 18:16:42 +08:00
|
|
|
def _decode_enum(self, raw, attr_spec):
|
2023-03-08 08:39:23 +08:00
|
|
|
enum = self.consts[attr_spec['enum']]
|
2023-10-17 05:39:37 +08:00
|
|
|
if enum.type == 'flags' or attr_spec.get('enum-as-flags', False):
|
2023-07-25 18:16:41 +08:00
|
|
|
i = 0
|
2023-01-21 01:50:41 +08:00
|
|
|
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-07-25 18:16:41 +08:00
|
|
|
value = enum.entries_by_val[raw].name
|
tools: ynl-gen: fix parse multi-attr enum attribute
When attribute is enum type and marked as multi-attr, the netlink
respond is not parsed, fails with stack trace:
Traceback (most recent call last):
File "/net-next/tools/net/ynl/./test.py", line 520, in <module>
main()
File "/net-next/tools/net/ynl/./test.py", line 488, in main
dplls=dplls_get(282574471561216)
File "/net-next/tools/net/ynl/./test.py", line 48, in dplls_get
reply=act(args)
File "/net-next/tools/net/ynl/./test.py", line 41, in act
reply = ynl.dump(args.dump, attrs)
File "/net-next/tools/net/ynl/lib/ynl.py", line 598, in dump
return self._op(method, vals, dump=True)
File "/net-next/tools/net/ynl/lib/ynl.py", line 584, in _op
rsp_msg = self._decode(gm.raw_attrs, op.attr_set.name)
File "/net-next/tools/net/ynl/lib/ynl.py", line 451, in _decode
self._decode_enum(rsp, attr_spec)
File "/net-next/tools/net/ynl/lib/ynl.py", line 408, in _decode_enum
value = enum.entries_by_val[raw].name
TypeError: unhashable type: 'list'
error: 1
Redesign _decode_enum(..) to take a enum int value and translate
it to either a bitmask or enum name as expected.
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20230725101642.267248-3-arkadiusz.kubalewski@intel.com
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-07-25 18:16:42 +08:00
|
|
|
return value
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-03-27 16:31:33 +08:00
|
|
|
def _decode_binary(self, attr, attr_spec):
|
2023-03-27 16:31:34 +08:00
|
|
|
if attr_spec.struct_name:
|
2024-01-30 06:34:52 +08:00
|
|
|
decoded = self._decode_struct(attr.raw, attr_spec.struct_name)
|
2023-03-27 16:31:34 +08:00
|
|
|
elif attr_spec.sub_type:
|
2023-03-27 16:31:33 +08:00
|
|
|
decoded = attr.as_c_array(attr_spec.sub_type)
|
|
|
|
else:
|
|
|
|
decoded = attr.as_bin()
|
2023-06-24 04:19:27 +08:00
|
|
|
if attr_spec.display_hint:
|
2024-01-30 06:34:54 +08:00
|
|
|
decoded = self._formatted_string(decoded, attr_spec.display_hint)
|
2023-03-27 16:31:33 +08:00
|
|
|
return decoded
|
|
|
|
|
2024-04-04 14:31:12 +08:00
|
|
|
def _decode_array_attr(self, attr, attr_spec):
|
2023-08-25 20:27:51 +08:00
|
|
|
decoded = []
|
|
|
|
offset = 0
|
|
|
|
while offset < len(attr.raw):
|
|
|
|
item = NlAttr(attr.raw, offset)
|
|
|
|
offset += item.full_len
|
|
|
|
|
2024-04-04 14:31:12 +08:00
|
|
|
if attr_spec["sub-type"] == 'nest':
|
|
|
|
subattrs = self._decode(NlAttrs(item.raw), attr_spec['nested-attributes'])
|
|
|
|
decoded.append({ item.type: subattrs })
|
2024-04-04 14:31:13 +08:00
|
|
|
elif attr_spec["sub-type"] == 'binary':
|
|
|
|
subattrs = item.as_bin()
|
|
|
|
if attr_spec.display_hint:
|
|
|
|
subattrs = self._formatted_string(subattrs, attr_spec.display_hint)
|
|
|
|
decoded.append(subattrs)
|
|
|
|
elif attr_spec["sub-type"] in NlAttr.type_formats:
|
|
|
|
subattrs = item.as_scalar(attr_spec['sub-type'], attr_spec.byte_order)
|
|
|
|
if attr_spec.display_hint:
|
|
|
|
subattrs = self._formatted_string(subattrs, attr_spec.display_hint)
|
|
|
|
decoded.append(subattrs)
|
2024-04-04 14:31:12 +08:00
|
|
|
else:
|
|
|
|
raise Exception(f'Unknown {attr_spec["sub-type"]} with name {attr_spec["name"]}')
|
2023-08-25 20:27:51 +08:00
|
|
|
return decoded
|
|
|
|
|
2024-03-07 07:10:44 +08:00
|
|
|
def _decode_nest_type_value(self, attr, attr_spec):
|
|
|
|
decoded = {}
|
|
|
|
value = attr
|
|
|
|
for name in attr_spec['type-value']:
|
|
|
|
value = NlAttr(value.raw, 0)
|
|
|
|
decoded[name] = value.type
|
|
|
|
subattrs = self._decode(NlAttrs(value.raw), attr_spec['nested-attributes'])
|
|
|
|
decoded.update(subattrs)
|
|
|
|
return decoded
|
|
|
|
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
def _decode_unknown(self, attr):
|
|
|
|
if attr.is_nest:
|
|
|
|
return self._decode(NlAttrs(attr.raw), None)
|
|
|
|
else:
|
|
|
|
return attr.as_bin()
|
|
|
|
|
|
|
|
def _rsp_add(self, rsp, name, is_multi, decoded):
|
|
|
|
if is_multi == None:
|
|
|
|
if name in rsp and type(rsp[name]) is not list:
|
|
|
|
rsp[name] = [rsp[name]]
|
|
|
|
is_multi = True
|
|
|
|
else:
|
|
|
|
is_multi = False
|
|
|
|
|
|
|
|
if not is_multi:
|
|
|
|
rsp[name] = decoded
|
|
|
|
elif name in rsp:
|
|
|
|
rsp[name].append(decoded)
|
|
|
|
else:
|
|
|
|
rsp[name] = [decoded]
|
|
|
|
|
2024-01-30 06:34:47 +08:00
|
|
|
def _resolve_selector(self, attr_spec, search_attrs):
|
2023-12-15 17:37:11 +08:00
|
|
|
sub_msg = attr_spec.sub_message
|
|
|
|
if sub_msg not in self.sub_msgs:
|
|
|
|
raise Exception(f"No sub-message spec named {sub_msg} for {attr_spec.name}")
|
|
|
|
sub_msg_spec = self.sub_msgs[sub_msg]
|
|
|
|
|
|
|
|
selector = attr_spec.selector
|
2024-01-30 06:34:47 +08:00
|
|
|
value = search_attrs.lookup(selector)
|
2023-12-15 17:37:11 +08:00
|
|
|
if value not in sub_msg_spec.formats:
|
|
|
|
raise Exception(f"No message format for '{value}' in sub-message spec '{sub_msg}'")
|
|
|
|
|
|
|
|
spec = sub_msg_spec.formats[value]
|
|
|
|
return spec
|
|
|
|
|
2024-01-30 06:34:47 +08:00
|
|
|
def _decode_sub_msg(self, attr, attr_spec, search_attrs):
|
|
|
|
msg_format = self._resolve_selector(attr_spec, search_attrs)
|
2023-12-15 17:37:11 +08:00
|
|
|
decoded = {}
|
|
|
|
offset = 0
|
|
|
|
if msg_format.fixed_header:
|
2024-01-30 06:34:52 +08:00
|
|
|
decoded.update(self._decode_struct(attr.raw, msg_format.fixed_header));
|
2024-01-30 06:34:53 +08:00
|
|
|
offset = self._struct_size(msg_format.fixed_header)
|
2023-12-15 17:37:11 +08:00
|
|
|
if msg_format.attr_set:
|
|
|
|
if msg_format.attr_set in self.attr_sets:
|
|
|
|
subdict = self._decode(NlAttrs(attr.raw, offset), msg_format.attr_set)
|
|
|
|
decoded.update(subdict)
|
|
|
|
else:
|
|
|
|
raise Exception(f"Unknown attribute-set '{attr_space}' when decoding '{attr_spec.name}'")
|
|
|
|
return decoded
|
|
|
|
|
2024-01-30 06:34:47 +08:00
|
|
|
def _decode(self, attrs, space, outer_attrs = None):
|
tools: ynl: don't access uninitialized attr_space variable
If message contains unknown attribute and user passes
"--process-unknown" command line option, _decode() gets called with space
arg set to None. In that case, attr_space variable is not initialized
used which leads to following trace:
Traceback (most recent call last):
File "./tools/net/ynl/cli.py", line 77, in <module>
main()
File "./tools/net/ynl/cli.py", line 68, in main
reply = ynl.dump(args.dump, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 909, in dump
return self._op(method, vals, [], dump=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 894, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 639, in _decode
self._rsp_add(rsp, attr_name, None, self._decode_unknown(attr))
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 569, in _decode_unknown
return self._decode(NlAttrs(attr.raw), None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 630, in _decode
search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs)
^^^^^^^^^^
UnboundLocalError: cannot access local variable 'attr_space' where it is not associated with a value
Fix this by moving search_attrs assignment under the if statement
above it to make sure attr_space is initialized.
Fixes: bf8b832374fb ("tools/net/ynl: Support sub-messages in nested attribute spaces")
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2024-02-15 20:27:26 +08:00
|
|
|
rsp = dict()
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
if space:
|
|
|
|
attr_space = self.attr_sets[space]
|
tools: ynl: don't access uninitialized attr_space variable
If message contains unknown attribute and user passes
"--process-unknown" command line option, _decode() gets called with space
arg set to None. In that case, attr_space variable is not initialized
used which leads to following trace:
Traceback (most recent call last):
File "./tools/net/ynl/cli.py", line 77, in <module>
main()
File "./tools/net/ynl/cli.py", line 68, in main
reply = ynl.dump(args.dump, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 909, in dump
return self._op(method, vals, [], dump=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 894, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 639, in _decode
self._rsp_add(rsp, attr_name, None, self._decode_unknown(attr))
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 569, in _decode_unknown
return self._decode(NlAttrs(attr.raw), None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "tools/net/ynl/lib/ynl.py", line 630, in _decode
search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs)
^^^^^^^^^^
UnboundLocalError: cannot access local variable 'attr_space' where it is not associated with a value
Fix this by moving search_attrs assignment under the if statement
above it to make sure attr_space is initialized.
Fixes: bf8b832374fb ("tools/net/ynl: Support sub-messages in nested attribute spaces")
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2024-02-15 20:27:26 +08:00
|
|
|
search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs)
|
2024-01-30 06:34:47 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
for attr in attrs:
|
2023-08-15 04:56:27 +08:00
|
|
|
try:
|
|
|
|
attr_spec = attr_space.attrs_by_val[attr.type]
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
except (KeyError, UnboundLocalError):
|
|
|
|
if not self.process_unknown:
|
|
|
|
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
|
|
|
|
attr_name = f"UnknownAttr({attr.type})"
|
|
|
|
self._rsp_add(rsp, attr_name, None, self._decode_unknown(attr))
|
|
|
|
continue
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
if attr_spec["type"] == 'nest':
|
2024-01-30 06:34:47 +08:00
|
|
|
subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes'], search_attrs)
|
2023-01-31 10:33:47 +08:00
|
|
|
decoded = subdict
|
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-10-19 05:39:21 +08:00
|
|
|
elif attr_spec.is_auto_scalar:
|
|
|
|
decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order)
|
2023-05-23 17:37:47 +08:00
|
|
|
elif attr_spec["type"] in NlAttr.type_formats:
|
|
|
|
decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order)
|
2023-10-21 19:27:03 +08:00
|
|
|
if 'enum' in attr_spec:
|
|
|
|
decoded = self._decode_enum(decoded, attr_spec)
|
2024-04-04 14:31:12 +08:00
|
|
|
elif attr_spec["type"] == 'indexed-array':
|
|
|
|
decoded = self._decode_array_attr(attr, attr_spec)
|
2023-10-21 19:27:03 +08:00
|
|
|
elif attr_spec["type"] == 'bitfield32':
|
|
|
|
value, selector = struct.unpack("II", attr.raw)
|
|
|
|
if 'enum' in attr_spec:
|
|
|
|
value = self._decode_enum(value, attr_spec)
|
|
|
|
selector = self._decode_enum(selector, attr_spec)
|
|
|
|
decoded = {"value": value, "selector": selector}
|
2023-12-15 17:37:11 +08:00
|
|
|
elif attr_spec["type"] == 'sub-message':
|
2024-01-30 06:34:50 +08:00
|
|
|
decoded = self._decode_sub_msg(attr, attr_spec, search_attrs)
|
2024-03-07 07:10:44 +08:00
|
|
|
elif attr_spec["type"] == 'nest-type-value':
|
|
|
|
decoded = self._decode_nest_type_value(attr, attr_spec)
|
2023-01-21 01:50:41 +08:00
|
|
|
else:
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
if not self.process_unknown:
|
|
|
|
raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
|
|
|
|
decoded = self._decode_unknown(attr)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
tools: ynl: introduce option to process unknown attributes or types
In case the kernel sends message back containing attribute not defined
in family spec, following exception is raised to the user:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}'
Traceback (most recent call last):
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 521, in _decode
attr_spec = attr_space.attrs_by_val[attr.type]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 132
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 61, in <module>
main()
File "/home/jiri/work/linux/./tools/net/ynl/cli.py", line 49, in main
reply = ynl.do(args.do, attrs, args.flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 731, in do
return self._op(method, vals, flags)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 719, in _op
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jiri/work/linux/tools/net/ynl/lib/ynl.py", line 525, in _decode
raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
Exception: Space 'devlink' has no attribute with value '132'
Introduce a command line option "process-unknown" and pass it down to
YnlFamily class constructor to allow user to process unknown
attributes and types and print them as binaries.
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/devlink.yaml --do trap-get --json '{"bus-name": "netdevsim", "dev-name": "netdevsim1", "trap-name": "source_mac_is_multicast"}' --process-unknown
{'UnknownAttr(129)': {'UnknownAttr(0)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(1)': b'\x00\x00\x00\x00\x00\x00\x00\x00',
'UnknownAttr(2)': b'\x0e\x00\x00\x00\x00\x00\x00\x00'},
'UnknownAttr(132)': b'\x00',
'UnknownAttr(133)': b'',
'UnknownAttr(134)': {'UnknownAttr(0)': b''},
'bus-name': 'netdevsim',
'dev-name': 'netdevsim1',
'trap-action': 'drop',
'trap-group-name': 'l2_drops',
'trap-name': 'source_mac_is_multicast'}
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20231027092525.956172-1-jiri@resnulli.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-10-27 17:25:25 +08:00
|
|
|
self._rsp_add(rsp, attr_spec["name"], attr_spec.is_multi, decoded)
|
2023-01-31 10:33:47 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
return rsp
|
|
|
|
|
2023-01-31 10:33:48 +08:00
|
|
|
def _decode_extack_path(self, attrs, attr_set, offset, target):
|
|
|
|
for attr in attrs:
|
2023-08-15 04:56:27 +08:00
|
|
|
try:
|
|
|
|
attr_spec = attr_set.attrs_by_val[attr.type]
|
|
|
|
except KeyError:
|
|
|
|
raise Exception(f"Space '{attr_set.name}' has no attribute with value '{attr.type}'")
|
2023-01-31 10:33:48 +08:00
|
|
|
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
|
|
|
|
|
2023-08-25 20:27:49 +08:00
|
|
|
def _decode_extack(self, request, op, extack):
|
2023-01-31 10:33:48 +08:00
|
|
|
if 'bad-attr-offs' not in extack:
|
|
|
|
return
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set))
|
2024-03-07 07:10:41 +08:00
|
|
|
offset = self.nlproto.msghdr_size() + self._struct_size(op.fixed_header)
|
2023-08-25 20:27:50 +08:00
|
|
|
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
|
2023-08-25 20:27:49 +08:00
|
|
|
extack['bad-attr-offs'])
|
2023-01-31 10:33:48 +08:00
|
|
|
if path:
|
|
|
|
del extack['bad-attr-offs']
|
|
|
|
extack['bad-attr'] = path
|
|
|
|
|
2024-01-30 06:34:53 +08:00
|
|
|
def _struct_size(self, name):
|
2023-12-15 17:37:11 +08:00
|
|
|
if name:
|
2024-01-30 06:34:53 +08:00
|
|
|
members = self.consts[name].members
|
2023-08-25 20:27:49 +08:00
|
|
|
size = 0
|
2024-01-30 06:34:53 +08:00
|
|
|
for m in members:
|
2023-12-15 17:37:12 +08:00
|
|
|
if m.type in ['pad', 'binary']:
|
2024-01-30 06:34:55 +08:00
|
|
|
if m.struct:
|
|
|
|
size += self._struct_size(m.struct)
|
|
|
|
else:
|
|
|
|
size += m.len
|
2023-12-15 17:37:12 +08:00
|
|
|
else:
|
|
|
|
format = NlAttr.get_format(m.type, m.byte_order)
|
|
|
|
size += format.size
|
2023-08-25 20:27:49 +08:00
|
|
|
return size
|
|
|
|
else:
|
|
|
|
return 0
|
|
|
|
|
2024-01-30 06:34:52 +08:00
|
|
|
def _decode_struct(self, data, name):
|
|
|
|
members = self.consts[name].members
|
|
|
|
attrs = dict()
|
2023-08-25 20:27:49 +08:00
|
|
|
offset = 0
|
2024-01-30 06:34:52 +08:00
|
|
|
for m in members:
|
2023-12-15 17:37:12 +08:00
|
|
|
value = None
|
|
|
|
if m.type == 'pad':
|
|
|
|
offset += m.len
|
|
|
|
elif m.type == 'binary':
|
2024-01-30 06:34:55 +08:00
|
|
|
if m.struct:
|
|
|
|
len = self._struct_size(m.struct)
|
|
|
|
value = self._decode_struct(data[offset : offset + len],
|
|
|
|
m.struct)
|
|
|
|
offset += len
|
|
|
|
else:
|
|
|
|
value = data[offset : offset + m.len]
|
|
|
|
offset += m.len
|
2023-12-15 17:37:12 +08:00
|
|
|
else:
|
|
|
|
format = NlAttr.get_format(m.type, m.byte_order)
|
2024-01-30 06:34:52 +08:00
|
|
|
[ value ] = format.unpack_from(data, offset)
|
2023-12-15 17:37:12 +08:00
|
|
|
offset += format.size
|
|
|
|
if value is not None:
|
|
|
|
if m.enum:
|
|
|
|
value = self._decode_enum(value, m)
|
2024-01-30 06:34:52 +08:00
|
|
|
elif m.display_hint:
|
2024-01-30 06:34:54 +08:00
|
|
|
value = self._formatted_string(value, m.display_hint)
|
2024-01-30 06:34:52 +08:00
|
|
|
attrs[m.name] = value
|
|
|
|
return attrs
|
2023-08-25 20:27:49 +08:00
|
|
|
|
2024-01-30 06:34:49 +08:00
|
|
|
def _encode_struct(self, name, vals):
|
|
|
|
members = self.consts[name].members
|
|
|
|
attr_payload = b''
|
|
|
|
for m in members:
|
2024-01-30 06:34:51 +08:00
|
|
|
value = vals.pop(m.name) if m.name in vals else None
|
2024-01-30 06:34:49 +08:00
|
|
|
if m.type == 'pad':
|
|
|
|
attr_payload += bytearray(m.len)
|
|
|
|
elif m.type == 'binary':
|
2024-01-30 06:34:55 +08:00
|
|
|
if m.struct:
|
|
|
|
if value is None:
|
|
|
|
value = dict()
|
|
|
|
attr_payload += self._encode_struct(m.struct, value)
|
2024-01-30 06:34:51 +08:00
|
|
|
else:
|
2024-01-30 06:34:55 +08:00
|
|
|
if value is None:
|
|
|
|
attr_payload += bytearray(m.len)
|
|
|
|
else:
|
|
|
|
attr_payload += bytes.fromhex(value)
|
2024-01-30 06:34:49 +08:00
|
|
|
else:
|
2024-01-30 06:34:51 +08:00
|
|
|
if value is None:
|
|
|
|
value = 0
|
2024-01-30 06:34:49 +08:00
|
|
|
format = NlAttr.get_format(m.type, m.byte_order)
|
|
|
|
attr_payload += format.pack(value)
|
|
|
|
return attr_payload
|
|
|
|
|
2024-01-30 06:34:54 +08:00
|
|
|
def _formatted_string(self, raw, display_hint):
|
|
|
|
if display_hint == 'mac':
|
|
|
|
formatted = ':'.join('%02x' % b for b in raw)
|
|
|
|
elif display_hint == 'hex':
|
2024-03-27 20:31:28 +08:00
|
|
|
if isinstance(raw, int):
|
|
|
|
formatted = hex(raw)
|
|
|
|
else:
|
|
|
|
formatted = bytes.hex(raw, ' ')
|
2024-01-30 06:34:54 +08:00
|
|
|
elif display_hint in [ 'ipv4', 'ipv6' ]:
|
|
|
|
formatted = format(ipaddress.ip_address(raw))
|
|
|
|
elif display_hint == 'uuid':
|
|
|
|
formatted = str(uuid.UUID(bytes=raw))
|
|
|
|
else:
|
|
|
|
formatted = raw
|
|
|
|
return formatted
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
def handle_ntf(self, decoded):
|
2023-01-21 01:50:41 +08:00
|
|
|
msg = dict()
|
|
|
|
if self.include_raw:
|
2023-08-25 20:27:50 +08:00
|
|
|
msg['raw'] = decoded
|
|
|
|
op = self.rsp_by_value[decoded.cmd()]
|
|
|
|
attrs = self._decode(decoded.raw_attrs, op.attr_set.name)
|
|
|
|
if op.fixed_header:
|
2024-01-30 06:34:52 +08:00
|
|
|
attrs.update(self._decode_struct(decoded.raw, op.fixed_header))
|
2023-08-25 20:27:50 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
msg['name'] = op['name']
|
2023-08-25 20:27:50 +08:00
|
|
|
msg['msg'] = attrs
|
2023-01-21 01:50:41 +08:00
|
|
|
self.async_msg_queue.append(msg)
|
|
|
|
|
|
|
|
def check_ntf(self):
|
|
|
|
while True:
|
|
|
|
try:
|
2024-03-05 13:33:08 +08:00
|
|
|
reply = self.sock.recv(self._recv_size, socket.MSG_DONTWAIT)
|
2023-01-21 01:50:41 +08:00
|
|
|
except BlockingIOError:
|
|
|
|
return
|
|
|
|
|
|
|
|
nms = NlMsgs(reply)
|
2024-03-05 13:33:09 +08:00
|
|
|
self._recv_dbg_print(reply, nms)
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
decoded = self.nlproto.decode(self, nl_msg)
|
|
|
|
if decoded.cmd() not in self.async_msg_ids:
|
|
|
|
print("Unexpected msg id done while checking for ntf", decoded)
|
2023-01-21 01:50:41 +08:00
|
|
|
continue
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
self.handle_ntf(decoded)
|
2023-01-21 01:50:41 +08:00
|
|
|
|
2023-03-30 06:16:55 +08:00
|
|
|
def operation_do_attributes(self, name):
|
|
|
|
"""
|
|
|
|
For a given operation name, find and return a supported
|
|
|
|
set of attributes (as a dict).
|
|
|
|
"""
|
|
|
|
op = self.find_operation(name)
|
|
|
|
if not op:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return op['do']['request']['attributes'].copy()
|
|
|
|
|
2023-12-03 05:10:05 +08:00
|
|
|
def _op(self, method, vals, flags=None, 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
|
2023-08-25 20:27:52 +08:00
|
|
|
for flag in flags or []:
|
|
|
|
nl_flags |= flag
|
2023-01-21 01:50:41 +08:00
|
|
|
if dump:
|
|
|
|
nl_flags |= Netlink.NLM_F_DUMP
|
|
|
|
|
|
|
|
req_seq = random.randint(1024, 65535)
|
2023-08-25 20:27:50 +08:00
|
|
|
msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq)
|
2023-03-27 16:31:35 +08:00
|
|
|
if op.fixed_header:
|
2024-01-30 06:34:49 +08:00
|
|
|
msg += self._encode_struct(op.fixed_header, vals)
|
2024-01-30 06:34:50 +08:00
|
|
|
search_attrs = SpaceAttrs(op.attr_set, vals)
|
2023-01-21 01:50:41 +08:00
|
|
|
for name, value in vals.items():
|
2024-01-30 06:34:50 +08:00
|
|
|
msg += self._add_attr(op.attr_set.name, name, value, search_attrs)
|
2023-01-21 01:50:41 +08:00
|
|
|
msg = _genl_msg_finalize(msg)
|
|
|
|
|
|
|
|
self.sock.send(msg, 0)
|
|
|
|
|
|
|
|
done = False
|
|
|
|
rsp = []
|
|
|
|
while not done:
|
2024-03-05 13:33:08 +08:00
|
|
|
reply = self.sock.recv(self._recv_size)
|
2023-01-31 10:33:44 +08:00
|
|
|
nms = NlMsgs(reply, attr_space=op.attr_set)
|
2024-03-05 13:33:09 +08:00
|
|
|
self._recv_dbg_print(reply, nms)
|
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:
|
2023-08-25 20:27:49 +08:00
|
|
|
self._decode_extack(msg, op, nl_msg.extack)
|
2023-01-31 10:33:48 +08:00
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
if nl_msg.error:
|
2023-03-30 06:16:54 +08:00
|
|
|
raise NlError(nl_msg)
|
2023-01-21 01:50:41 +08:00
|
|
|
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
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
decoded = self.nlproto.decode(self, nl_msg)
|
|
|
|
|
2023-01-21 01:50:41 +08:00
|
|
|
# Check if this is a reply to our request
|
2023-08-25 20:27:50 +08:00
|
|
|
if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value:
|
|
|
|
if decoded.cmd() in self.async_msg_ids:
|
|
|
|
self.handle_ntf(decoded)
|
2023-01-21 01:50:41 +08:00
|
|
|
continue
|
|
|
|
else:
|
2023-08-25 20:27:50 +08:00
|
|
|
print('Unexpected message: ' + repr(decoded))
|
2023-01-21 01:50:41 +08:00
|
|
|
continue
|
|
|
|
|
2023-08-25 20:27:50 +08:00
|
|
|
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
|
2023-08-25 20:27:49 +08:00
|
|
|
if op.fixed_header:
|
2024-01-30 06:34:52 +08:00
|
|
|
rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header))
|
2023-05-25 01:07:12 +08:00
|
|
|
rsp.append(rsp_msg)
|
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
|
|
|
|
2023-12-03 05:10:05 +08:00
|
|
|
def do(self, method, vals, flags=None):
|
2023-08-25 20:27:52 +08:00
|
|
|
return self._op(method, vals, flags)
|
2023-01-31 10:33:49 +08:00
|
|
|
|
|
|
|
def dump(self, method, vals):
|
2023-08-25 20:27:52 +08:00
|
|
|
return self._op(method, vals, [], dump=True)
|