* [PATCH net-next 0/5] tools: ynl: policy query support
@ 2026-03-10 0:53 Jakub Kicinski
2026-03-10 0:53 ` [PATCH net-next 1/5] tools: ynl: handle pad type during decode Jakub Kicinski
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw)
To: davem, donald.hunter
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf,
linux-kselftest, Jakub Kicinski
Improve the Netlink policy support in YNL. This series grew out of
improvements to policy checking, when writing selftests I realized
that instead of doing all the policy parsing in the test we're
better off making it part of YNL itself.
Patch 1 adds pad handling, apparently we never hit pad with commonly
used families. nlctrl policy dumps use pad more frequently.
Patch 2 is a trivial refactor.
Patch 3 pays off some technical debt in terms of documentation.
The YnlFamily class is growing in size and it's quite hard to
find its members. So document it a little bit.
Patch 4 is the main dish, the implementation of get_policy(op)
in YnlFamily.
Patch 5 plugs the new functionality into the CLI.
Jakub Kicinski (5):
tools: ynl: handle pad type during decode
tools: ynl: move policy decoding out of NlMsg
tools: ynl: add short doc to class YnlFamily
tools: ynl: add Python API for easier access to policies
tools: ynl: cli: add --policy support
tools/net/ynl/pyynl/cli.py | 12 ++
tools/net/ynl/pyynl/lib/__init__.py | 5 +-
tools/net/ynl/pyynl/lib/ynl.py | 216 +++++++++++++++++++---
tools/testing/selftests/net/lib/py/ynl.py | 6 +-
4 files changed, 208 insertions(+), 31 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH net-next 1/5] tools: ynl: handle pad type during decode 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski @ 2026-03-10 0:53 ` Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 2/5] tools: ynl: move policy decoding out of NlMsg Jakub Kicinski ` (4 subsequent siblings) 5 siblings, 0 replies; 10+ messages in thread From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw) To: davem, donald.hunter Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest, Jakub Kicinski Apparently Python code only handled the 'pad' type in structs until now. Add it to attr decoding. nlctrl policy dumps need it. Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/net/ynl/pyynl/lib/ynl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 9774005e7ad1..8302c19ab55c 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -814,7 +814,9 @@ from .nlspec import SpecFamily continue try: - if attr_spec["type"] == 'nest': + if attr_spec["type"] == 'pad': + continue + elif attr_spec["type"] == 'nest': subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes'], search_attrs) -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 2/5] tools: ynl: move policy decoding out of NlMsg 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 1/5] tools: ynl: handle pad type during decode Jakub Kicinski @ 2026-03-10 0:53 ` Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 3/5] tools: ynl: add short doc to class YnlFamily Jakub Kicinski ` (3 subsequent siblings) 5 siblings, 0 replies; 10+ messages in thread From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw) To: davem, donald.hunter Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest, Jakub Kicinski We'll soon need to decode policies from dump so move _decode_policy() out of class NlMsg. Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/net/ynl/pyynl/lib/ynl.py | 53 ++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 8302c19ab55c..17482c17a976 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -247,7 +247,7 @@ from .nlspec import SpecFamily elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: self.extack['bad-attr-offs'] = extack.as_scalar('u32') elif extack.type == Netlink.NLMSGERR_ATTR_POLICY: - self.extack['policy'] = self._decode_policy(extack.raw) + self.extack['policy'] = _genl_decode_policy(extack.raw) else: if 'unknown' not in self.extack: self.extack['unknown'] = [] @@ -256,30 +256,6 @@ from .nlspec import SpecFamily if attr_space: self.annotate_extack(attr_space) - 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 - def annotate_extack(self, attr_space): """ Make extack more human friendly with attribute information """ @@ -333,6 +309,33 @@ from .nlspec import SpecFamily return struct.pack("I", len(msg) + 4) + msg +def _genl_decode_policy(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_POLICY_IDX: + policy['policy-idx'] = 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 + + # pylint: disable=too-many-nested-blocks def _genl_load_families(): genl_family_name_to_id = {} -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 3/5] tools: ynl: add short doc to class YnlFamily 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 1/5] tools: ynl: handle pad type during decode Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 2/5] tools: ynl: move policy decoding out of NlMsg Jakub Kicinski @ 2026-03-10 0:53 ` Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 4/5] tools: ynl: add Python API for easier access to policies Jakub Kicinski ` (2 subsequent siblings) 5 siblings, 0 replies; 10+ messages in thread From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw) To: davem, donald.hunter Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest, Jakub Kicinski The class is quite long. It's getting hard to find the user-facing methods. Add a short doc at the class level explaining the main API. Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/net/ynl/pyynl/lib/ynl.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 17482c17a976..ec4d95f583fe 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -491,6 +491,32 @@ from .nlspec import SpecFamily class YnlFamily(SpecFamily): + """ + YNL family -- a Netlink interface built from a YAML spec. + + Primary use of the class is to execute Netlink commands: + + ynl.<op_name>(attrs, ...) + + By default this will execute the <op_name> as "do", pass dump=True + to perform a dump operation. + + ynl.<op_name> is a shorthand / convenience wrapper for the following + methods which take the op_name as a string: + + ynl.do(op_name, attrs, flags=None) -- execute a do operation + ynl.dump(op_name, attrs) -- execute a dump operation + ynl.do_multi(ops) -- batch multiple do operations + + The flags argument in ynl.do() allows passing in extra NLM_F_* flags + which may be necessary for old families. + + Notification API: + + ynl.ntf_subscribe(mcast_name) -- join a multicast group + ynl.check_ntf() -- drain pending notifications + ynl.poll_ntf(duration=None) -- yield notifications + """ def __init__(self, def_path, schema=None, process_unknown=False, recv_size=0): super().__init__(def_path, schema) -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 4/5] tools: ynl: add Python API for easier access to policies 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski ` (2 preceding siblings ...) 2026-03-10 0:53 ` [PATCH net-next 3/5] tools: ynl: add short doc to class YnlFamily Jakub Kicinski @ 2026-03-10 0:53 ` Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 5/5] tools: ynl: cli: add --policy support Jakub Kicinski 2026-03-11 2:40 ` [PATCH net-next 0/5] tools: ynl: policy query support patchwork-bot+netdevbpf 5 siblings, 0 replies; 10+ messages in thread From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw) To: davem, donald.hunter Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest, Jakub Kicinski The format of Netlink policy dump is a bit curious with messages in the same dump carrying both attrs and mapping info. Plus each message carries a single piece of the puzzle the caller must then reassemble. I need to do this reassembly for a test, but I think it's generally useful. So let's add proper support to YnlFamily to return more user-friendly representation. See the various docs in the patch for more details. Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/net/ynl/pyynl/lib/__init__.py | 5 +- tools/net/ynl/pyynl/lib/ynl.py | 133 ++++++++++++++++++++++ tools/testing/selftests/net/lib/py/ynl.py | 6 +- 3 files changed, 139 insertions(+), 5 deletions(-) diff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py index 33a96155fb3b..be741985ae4e 100644 --- a/tools/net/ynl/pyynl/lib/__init__.py +++ b/tools/net/ynl/pyynl/lib/__init__.py @@ -5,11 +5,12 @@ from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \ SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat, \ SpecException -from .ynl import YnlFamily, Netlink, NlError, YnlException +from .ynl import YnlFamily, Netlink, NlError, NlPolicy, YnlException from .doc_generator import YnlDocGenerator __all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet", "SpecFamily", "SpecOperation", "SpecSubMessage", "SpecSubMessageFormat", "SpecException", - "YnlFamily", "Netlink", "NlError", "YnlDocGenerator", "YnlException"] + "YnlFamily", "Netlink", "NlError", "NlPolicy", "YnlException", + "YnlDocGenerator"] diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index ec4d95f583fe..e6fcedb597a8 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -77,15 +77,22 @@ from .nlspec import SpecFamily # nlctrl CTRL_CMD_GETFAMILY = 3 + CTRL_CMD_GETPOLICY = 10 CTRL_ATTR_FAMILY_ID = 1 CTRL_ATTR_FAMILY_NAME = 2 CTRL_ATTR_MAXATTR = 5 CTRL_ATTR_MCAST_GROUPS = 7 + CTRL_ATTR_POLICY = 8 + CTRL_ATTR_OP_POLICY = 9 + CTRL_ATTR_OP = 10 CTRL_ATTR_MCAST_GRP_NAME = 1 CTRL_ATTR_MCAST_GRP_ID = 2 + CTRL_ATTR_POLICY_DO = 1 + CTRL_ATTR_POLICY_DUMP = 2 + # Extack types NLMSGERR_ATTR_MSG = 1 NLMSGERR_ATTR_OFFS = 2 @@ -136,6 +143,34 @@ from .nlspec import SpecFamily pass +# pylint: disable=too-few-public-methods +class NlPolicy: + """Kernel policy for one mode (do or dump) of one operation. + + Returned by YnlFamily.get_policy(). Contains a dict of attributes + the kernel accepts, with their validation constraints. + + Attributes: + attrs: dict mapping attribute names to policy dicts, e.g. + page-pool-stats-get do policy:: + + { + 'info': {'type': 'nested', 'policy': { + 'id': {'type': 'uint', 'min-value': 1, + 'max-value': 4294967295}, + 'ifindex': {'type': 'u32', 'min-value': 1, + 'max-value': 2147483647}, + }}, + } + + Each policy dict always contains 'type' (e.g. u32, string, + nested). Optional keys: min-value, max-value, min-length, + max-length, mask, policy. + """ + def __init__(self, attrs): + self.attrs = attrs + + class NlAttr: ScalarFormat = namedtuple('ScalarFormat', ['native', 'big', 'little']) type_formats = { @@ -384,6 +419,52 @@ from .nlspec import SpecFamily genl_family_name_to_id[fam['name']] = fam +# pylint: disable=too-many-nested-blocks +def _genl_policy_dump(family_id, op): + op_policy = {} + policy_table = {} + + 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_GETPOLICY, 1) + msg += struct.pack('HHHxx', 6, Netlink.CTRL_ATTR_FAMILY_ID, family_id) + msg += struct.pack('HHI', 8, Netlink.CTRL_ATTR_OP, op) + msg = _genl_msg_finalize(msg) + + sock.send(msg, 0) + + while True: + reply = sock.recv(128 * 1024) + nms = NlMsgs(reply) + for nl_msg in nms: + if nl_msg.error: + raise YnlException(f"Netlink error: {nl_msg.error}") + if nl_msg.done: + return op_policy, policy_table + + gm = GenlMsg(nl_msg) + for attr in NlAttrs(gm.raw): + if attr.type == Netlink.CTRL_ATTR_OP_POLICY: + for op_attr in NlAttrs(attr.raw): + for method_attr in NlAttrs(op_attr.raw): + if method_attr.type == Netlink.CTRL_ATTR_POLICY_DO: + op_policy['do'] = method_attr.as_scalar('u32') + elif method_attr.type == Netlink.CTRL_ATTR_POLICY_DUMP: + op_policy['dump'] = method_attr.as_scalar('u32') + elif attr.type == Netlink.CTRL_ATTR_POLICY: + for pidx_attr in NlAttrs(attr.raw): + policy_idx = pidx_attr.type + for aid_attr in NlAttrs(pidx_attr.raw): + attr_id = aid_attr.type + decoded = _genl_decode_policy(aid_attr.raw) + if policy_idx not in policy_table: + policy_table[policy_idx] = {} + policy_table[policy_idx][attr_id] = decoded + + class GenlMsg: def __init__(self, nl_msg): self.nl = nl_msg @@ -516,6 +597,11 @@ from .nlspec import SpecFamily ynl.ntf_subscribe(mcast_name) -- join a multicast group ynl.check_ntf() -- drain pending notifications ynl.poll_ntf(duration=None) -- yield notifications + + Policy introspection allows querying validation criteria from the running + kernel. Allows checking whether kernel supports a given attribute or value. + + ynl.get_policy(op_name, mode) -- query kernel policy for an op """ def __init__(self, def_path, schema=None, process_unknown=False, recv_size=0): @@ -1221,3 +1307,50 @@ from .nlspec import SpecFamily def do_multi(self, ops): return self._ops(ops) + + def _resolve_policy(self, policy_idx, policy_table, attr_set): + attrs = {} + if policy_idx not in policy_table: + return attrs + for attr_id, decoded in policy_table[policy_idx].items(): + if attr_set and attr_id in attr_set.attrs_by_val: + spec = attr_set.attrs_by_val[attr_id] + name = spec['name'] + else: + spec = None + name = f'attr-{attr_id}' + if 'policy-idx' in decoded: + sub_set = None + if spec and 'nested-attributes' in spec.yaml: + sub_set = self.attr_sets[spec.yaml['nested-attributes']] + nested = self._resolve_policy(decoded['policy-idx'], + policy_table, sub_set) + del decoded['policy-idx'] + decoded['policy'] = nested + attrs[name] = decoded + return attrs + + def get_policy(self, op_name, mode): + """Query running kernel for the Netlink policy of an operation. + + Allows checking whether kernel supports a given attribute or value. + This method consults the running kernel, not the YAML spec. + + Args: + op_name: operation name as it appears in the YAML spec + mode: 'do' or 'dump' + + Returns: + NlPolicy with an attrs dict mapping attribute names to + their policy properties (type, min/max, nested, etc.), + or None if the operation has no policy for the given mode. + No policy usually implies that the operation rejects all attributes. + """ + op = self.ops[op_name] + op_policy, policy_table = _genl_policy_dump(self.nlproto.family_id, + op.req_value) + if mode not in op_policy: + return None + policy_idx = op_policy[mode] + attrs = self._resolve_policy(policy_idx, policy_table, op.attr_set) + return NlPolicy(attrs) diff --git a/tools/testing/selftests/net/lib/py/ynl.py b/tools/testing/selftests/net/lib/py/ynl.py index b42986bf39e3..e8aa97936f6c 100644 --- a/tools/testing/selftests/net/lib/py/ynl.py +++ b/tools/testing/selftests/net/lib/py/ynl.py @@ -13,14 +13,14 @@ from .ksft import ksft_pr, ktap_result SPEC_PATH = KSFT_DIR / "net/lib/specs" sys.path.append(tools_full_path.as_posix()) - from net.lib.ynl.pyynl.lib import YnlFamily, NlError, Netlink + from net.lib.ynl.pyynl.lib import YnlFamily, NlError, NlPolicy, Netlink else: # Running in tree tools_full_path = KSRC / "tools" SPEC_PATH = KSRC / "Documentation/netlink/specs" sys.path.append(tools_full_path.as_posix()) - from net.ynl.pyynl.lib import YnlFamily, NlError, Netlink + from net.ynl.pyynl.lib import YnlFamily, NlError, NlPolicy, Netlink except ModuleNotFoundError as e: ksft_pr("Failed importing `ynl` library from kernel sources") ksft_pr(str(e)) @@ -28,7 +28,7 @@ from .ksft import ksft_pr, ktap_result sys.exit(4) __all__ = [ - "NlError", "Netlink", "YnlFamily", "SPEC_PATH", + "NlError", "NlPolicy", "Netlink", "YnlFamily", "SPEC_PATH", "EthtoolFamily", "RtnlFamily", "RtnlAddrFamily", "NetdevFamily", "NetshaperFamily", "DevlinkFamily", "PSPFamily", ] -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 5/5] tools: ynl: cli: add --policy support 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski ` (3 preceding siblings ...) 2026-03-10 0:53 ` [PATCH net-next 4/5] tools: ynl: add Python API for easier access to policies Jakub Kicinski @ 2026-03-10 0:53 ` Jakub Kicinski 2026-03-11 2:40 ` [PATCH net-next 0/5] tools: ynl: policy query support patchwork-bot+netdevbpf 5 siblings, 0 replies; 10+ messages in thread From: Jakub Kicinski @ 2026-03-10 0:53 UTC (permalink / raw) To: davem, donald.hunter Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest, Jakub Kicinski Add --policy flag which can be combined with --do or --dump to query the kernel's netlink policy for an operation instead of executing it. Examples: $ ynl --family netdev --do dev-get --policy {'ifindex': {'max-value': 4294967295, 'min-value': 1, 'type': 'u32'}} $ ynl --family ethtool --do channels-get --policy --output-json {"header": {"type": "nested", "policy": {"dev-index": ...}}} $ ynl --family netdev --dump dev-get --policy None Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/net/ynl/pyynl/cli.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py index b452d4fb9434..fc9e84754e4b 100755 --- a/tools/net/ynl/pyynl/cli.py +++ b/tools/net/ynl/pyynl/cli.py @@ -256,6 +256,8 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' schema_group.add_argument('--no-schema', action='store_true') dbg_group = parser.add_argument_group('Debug options') + io_group.add_argument('--policy', action='store_true', + help='Query kernel policy for the operation instead of executing it') dbg_group.add_argument('--dbg-small-recv', default=0, const=4000, action='store', nargs='?', type=int, metavar='INT', help="Length of buffers used for recv()") @@ -308,6 +310,16 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' if args.dbg_small_recv: ynl.set_recv_dbg(True) + if args.policy: + if args.do: + pol = ynl.get_policy(args.do, 'do') + output(pol.attrs if pol else None) + args.do = None + if args.dump: + pol = ynl.get_policy(args.dump, 'dump') + output(pol.attrs if pol else None) + args.dump = None + if args.ntf: ynl.ntf_subscribe(args.ntf) -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/5] tools: ynl: policy query support 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski ` (4 preceding siblings ...) 2026-03-10 0:53 ` [PATCH net-next 5/5] tools: ynl: cli: add --policy support Jakub Kicinski @ 2026-03-11 2:40 ` patchwork-bot+netdevbpf 2026-03-11 11:30 ` Donald Hunter 5 siblings, 1 reply; 10+ messages in thread From: patchwork-bot+netdevbpf @ 2026-03-11 2:40 UTC (permalink / raw) To: Jakub Kicinski Cc: davem, donald.hunter, netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest Hello: This series was applied to netdev/net-next.git (main) by Jakub Kicinski <kuba@kernel.org>: On Mon, 9 Mar 2026 17:53:32 -0700 you wrote: > Improve the Netlink policy support in YNL. This series grew out of > improvements to policy checking, when writing selftests I realized > that instead of doing all the policy parsing in the test we're > better off making it part of YNL itself. > > Patch 1 adds pad handling, apparently we never hit pad with commonly > used families. nlctrl policy dumps use pad more frequently. > Patch 2 is a trivial refactor. > Patch 3 pays off some technical debt in terms of documentation. > The YnlFamily class is growing in size and it's quite hard to > find its members. So document it a little bit. > Patch 4 is the main dish, the implementation of get_policy(op) > in YnlFamily. > Patch 5 plugs the new functionality into the CLI. > > [...] Here is the summary with links: - [net-next,1/5] tools: ynl: handle pad type during decode https://git.kernel.org/netdev/net-next/c/7b1309c33927 - [net-next,2/5] tools: ynl: move policy decoding out of NlMsg https://git.kernel.org/netdev/net-next/c/c26fda6212b8 - [net-next,3/5] tools: ynl: add short doc to class YnlFamily https://git.kernel.org/netdev/net-next/c/8bbcfce5db97 - [net-next,4/5] tools: ynl: add Python API for easier access to policies https://git.kernel.org/netdev/net-next/c/77a6401a8722 - [net-next,5/5] tools: ynl: cli: add --policy support https://git.kernel.org/netdev/net-next/c/d6df5e9b2a56 You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/5] tools: ynl: policy query support 2026-03-11 2:40 ` [PATCH net-next 0/5] tools: ynl: policy query support patchwork-bot+netdevbpf @ 2026-03-11 11:30 ` Donald Hunter 2026-03-11 18:35 ` Jakub Kicinski 0 siblings, 1 reply; 10+ messages in thread From: Donald Hunter @ 2026-03-11 11:30 UTC (permalink / raw) To: Jakub Kicinski Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest patchwork-bot+netdevbpf@kernel.org writes: > Hello: > > This series was applied to netdev/net-next.git (main) > by Jakub Kicinski <kuba@kernel.org>: > > On Mon, 9 Mar 2026 17:53:32 -0700 you wrote: >> Improve the Netlink policy support in YNL. This series grew out of >> improvements to policy checking, when writing selftests I realized >> that instead of doing all the policy parsing in the test we're >> better off making it part of YNL itself. >> >> Patch 1 adds pad handling, apparently we never hit pad with commonly >> used families. nlctrl policy dumps use pad more frequently. >> Patch 2 is a trivial refactor. >> Patch 3 pays off some technical debt in terms of documentation. >> The YnlFamily class is growing in size and it's quite hard to >> find its members. So document it a little bit. >> Patch 4 is the main dish, the implementation of get_policy(op) >> in YnlFamily. >> Patch 5 plugs the new functionality into the CLI. I was mid review, looking at a couple of issues: - It would be good to fail more gracefully for netlink-raw families - I get "RecursionError: maximum recursion depth exceeded" for nl80211 Happy to address these in a followup. Cheers, Donald. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/5] tools: ynl: policy query support 2026-03-11 11:30 ` Donald Hunter @ 2026-03-11 18:35 ` Jakub Kicinski 2026-03-12 17:17 ` Donald Hunter 0 siblings, 1 reply; 10+ messages in thread From: Jakub Kicinski @ 2026-03-11 18:35 UTC (permalink / raw) To: Donald Hunter Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest On Wed, 11 Mar 2026 11:30:59 +0000 Donald Hunter wrote: > > On Mon, 9 Mar 2026 17:53:32 -0700 you wrote: > >> Improve the Netlink policy support in YNL. This series grew out of > >> improvements to policy checking, when writing selftests I realized > >> that instead of doing all the policy parsing in the test we're > >> better off making it part of YNL itself. > >> > >> Patch 1 adds pad handling, apparently we never hit pad with commonly > >> used families. nlctrl policy dumps use pad more frequently. > >> Patch 2 is a trivial refactor. > >> Patch 3 pays off some technical debt in terms of documentation. > >> The YnlFamily class is growing in size and it's quite hard to > >> find its members. So document it a little bit. > >> Patch 4 is the main dish, the implementation of get_policy(op) > >> in YnlFamily. > >> Patch 5 plugs the new functionality into the CLI. > > I was mid review Sorry about that! :( I didn't see any reply to the one liner for a few days I thought you may be AFK :) > looking at a couple of issues: > > - It would be good to fail more gracefully for netlink-raw families This one I saw, but I figured the message that gets printed is... reasonable. For low level functionality like policies we should assume user is relatively advanced? The policies are spotty even in genetlink, a lot of families don't link them up properly :( So explaining all of this is a bit of a rabbit hole. > - I get "RecursionError: maximum recursion depth exceeded" for nl80211 :o Missed this one completely! My feeling is that we should lean into the NlPolicy class more, and avoid rendering the full structure upfront. WDYT about the following? diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py index fc9e84754e4b..ff0cfbdc82e4 100755 --- a/tools/net/ynl/pyynl/cli.py +++ b/tools/net/ynl/pyynl/cli.py @@ -16,7 +16,8 @@ import textwrap # pylint: disable=no-name-in-module,wrong-import-position sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix()) -from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException +from lib import SpecFamily, SpecException +from lib import YnlFamily, Netlink, NlError, NlPolicy, YnlException SYS_SCHEMA_DIR='/usr/share/ynl' RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' @@ -79,6 +80,8 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' return bytes.hex(o) if isinstance(o, set): return sorted(o) + if isinstance(o, NlPolicy): + return o.to_dict() return json.JSONEncoder.default(self, o) @@ -313,11 +316,11 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' if args.policy: if args.do: pol = ynl.get_policy(args.do, 'do') - output(pol.attrs if pol else None) + output(pol if pol else None) args.do = None if args.dump: pol = ynl.get_policy(args.dump, 'dump') - output(pol.attrs if pol else None) + output(pol if pol else None) args.dump = None if args.ntf: diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 0eedeee465d8..4411f1902ae4 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -143,32 +143,113 @@ from .nlspec import SpecFamily pass -# pylint: disable=too-few-public-methods class NlPolicy: """Kernel policy for one mode (do or dump) of one operation. - Returned by YnlFamily.get_policy(). Contains a dict of attributes - the kernel accepts, with their validation constraints. + Returned by YnlFamily.get_policy(). Attributes of the policy + are accessible as attributes of the object. Nested policies + can be accessed indexing the object like a dictionary:: - Attributes: - attrs: dict mapping attribute names to policy dicts, e.g. - page-pool-stats-get do policy:: + pol = ynl.get_policy('page-pool-stats-get', 'do') + pol['info'].type # 'nested' + pol['info']['id'].type # 'uint' + pol['info']['id'].min_value # 1 - { - 'info': {'type': 'nested', 'policy': { - 'id': {'type': 'uint', 'min-value': 1, - 'max-value': 4294967295}, - 'ifindex': {'type': 'u32', 'min-value': 1, - 'max-value': 2147483647}, - }}, - } + Each policy entry always has a 'type' attribute (e.g. u32, string, + nested). Optional attributes depending on the 'type': min-value, + max-value, min-length, max-length, mask. - Each policy dict always contains 'type' (e.g. u32, string, - nested). Optional keys: min-value, max-value, min-length, - max-length, mask, policy. + Policies can form infinite nesting loops. These loops are trimmed + when policy is converted to a dict with pol.to_dict(). """ - def __init__(self, attrs): - self.attrs = attrs + def __init__(self, ynl, policy_idx, policy_table, attr_set, props=None): + self._policy_idx = policy_idx + self._policy_table = policy_table + self._ynl = ynl + self._props = props or {} + self._entries = {} + if policy_idx is not None and policy_idx in policy_table: + for attr_id, decoded in policy_table[policy_idx].items(): + if attr_set and attr_id in attr_set.attrs_by_val: + spec = attr_set.attrs_by_val[attr_id] + name = spec['name'] + else: + spec = None + name = f'attr-{attr_id}' + self._entries[name] = (spec, decoded) + + def __getitem__(self, name): + """Descend into a nested policy by attribute name.""" + spec, decoded = self._entries[name] + props = dict(decoded) + child_idx = None + child_set = None + if 'policy-idx' in props: + child_idx = props.pop('policy-idx') + if spec and 'nested-attributes' in spec.yaml: + child_set = self._ynl.attr_sets[spec.yaml['nested-attributes']] + return NlPolicy(self._ynl, child_idx, self._policy_table, + child_set, props) + + def __getattr__(self, name): + """Access this policy entry's own properties (type, min-value, etc.). + + Underscores in the name are converted to dashes, so that + pol.min_value looks up "min-value". + """ + key = name.replace('_', '-') + try: + # Hack for level-0 which we still want to have .type but we don't + # want type to pointlessly show up in the dict / JSON form. + if not self._props and name == "type": + return "nested" + return self._props[key] + except KeyError: + raise AttributeError(name) + + def get(self, name, default=None): + """Look up a child policy entry by attribute name, with a default.""" + try: + return self[name] + except KeyError: + return default + + def __contains__(self, name): + return name in self._entries + + def __len__(self): + return len(self._entries) + + def __iter__(self): + return iter(self._entries) + + def keys(self): + """Return attribute names accepted by this policy.""" + return self._entries.keys() + + def to_dict(self, seen=None): + """Convert to a plain dict, suitable for JSON serialization. + + Nested NlPolicy objects are expanded recursively. Cyclic + references are trimmed (resolved to just {"type": "nested"}). + """ + if seen is None: + seen = set() + result = dict(self._props) + if self._policy_idx is not None: + if self._policy_idx not in seen: + seen = seen | {self._policy_idx} + children = {} + for name in self: + children[name] = self[name].to_dict(seen) + if self._props: + result['policy'] = children + else: + result = children + return result + + def __repr__(self): + return repr(self.to_dict()) class NlAttr: @@ -1308,28 +1389,6 @@ from .nlspec import SpecFamily def do_multi(self, ops): return self._ops(ops) - def _resolve_policy(self, policy_idx, policy_table, attr_set): - attrs = {} - if policy_idx not in policy_table: - return attrs - for attr_id, decoded in policy_table[policy_idx].items(): - if attr_set and attr_id in attr_set.attrs_by_val: - spec = attr_set.attrs_by_val[attr_id] - name = spec['name'] - else: - spec = None - name = f'attr-{attr_id}' - if 'policy-idx' in decoded: - sub_set = None - if spec and 'nested-attributes' in spec.yaml: - sub_set = self.attr_sets[spec.yaml['nested-attributes']] - nested = self._resolve_policy(decoded['policy-idx'], - policy_table, sub_set) - del decoded['policy-idx'] - decoded['policy'] = nested - attrs[name] = decoded - return attrs - def get_policy(self, op_name, mode): """Query running kernel for the Netlink policy of an operation. @@ -1341,8 +1400,8 @@ from .nlspec import SpecFamily mode: 'do' or 'dump' Returns: - NlPolicy with an attrs dict mapping attribute names to - their policy properties (type, min/max, nested, etc.), + NlPolicy acting as a read-only dict mapping attribute names + to their policy properties (type, min/max, nested, etc.), or None if the operation has no policy for the given mode. Empty policy usually implies that the operation rejects all attributes. @@ -1353,5 +1412,4 @@ from .nlspec import SpecFamily if mode not in op_policy: return None policy_idx = op_policy[mode] - attrs = self._resolve_policy(policy_idx, policy_table, op.attr_set) - return NlPolicy(attrs) + return NlPolicy(self, policy_idx, policy_table, op.attr_set) ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 0/5] tools: ynl: policy query support 2026-03-11 18:35 ` Jakub Kicinski @ 2026-03-12 17:17 ` Donald Hunter 0 siblings, 0 replies; 10+ messages in thread From: Donald Hunter @ 2026-03-12 17:17 UTC (permalink / raw) To: Jakub Kicinski Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah, sdf, linux-kselftest Jakub Kicinski <kuba@kernel.org> writes: > On Wed, 11 Mar 2026 11:30:59 +0000 Donald Hunter wrote: >> > On Mon, 9 Mar 2026 17:53:32 -0700 you wrote: >> >> Improve the Netlink policy support in YNL. This series grew out of >> >> improvements to policy checking, when writing selftests I realized >> >> that instead of doing all the policy parsing in the test we're >> >> better off making it part of YNL itself. >> >> >> >> Patch 1 adds pad handling, apparently we never hit pad with commonly >> >> used families. nlctrl policy dumps use pad more frequently. >> >> Patch 2 is a trivial refactor. >> >> Patch 3 pays off some technical debt in terms of documentation. >> >> The YnlFamily class is growing in size and it's quite hard to >> >> find its members. So document it a little bit. >> >> Patch 4 is the main dish, the implementation of get_policy(op) >> >> in YnlFamily. >> >> Patch 5 plugs the new functionality into the CLI. >> >> I was mid review > > Sorry about that! :( I didn't see any reply to the one liner for a few > days I thought you may be AFK :) > >> looking at a couple of issues: >> >> - It would be good to fail more gracefully for netlink-raw families > > This one I saw, but I figured the message that gets printed is... > reasonable. For low level functionality like policies we should > assume user is relatively advanced? The policies are spotty > even in genetlink, a lot of families don't link them up properly :( > So explaining all of this is a bit of a rabbit hole. That's fair. I had it in mind to work on avoiding emitting stack traces for "normal usage". If I get to that then I can see about handling this case as well. >> - I get "RecursionError: maximum recursion depth exceeded" for nl80211 > > :o Missed this one completely! My feeling is that we should lean into > the NlPolicy class more, and avoid rendering the full structure upfront. > WDYT about the following? I agree with the approach. The code below looks reasonable, though I haven't been thorough - I'll wait for the patch. > diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py > index fc9e84754e4b..ff0cfbdc82e4 100755 > --- a/tools/net/ynl/pyynl/cli.py > +++ b/tools/net/ynl/pyynl/cli.py > @@ -16,7 +16,8 @@ import textwrap > > # pylint: disable=no-name-in-module,wrong-import-position > sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix()) > -from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException > +from lib import SpecFamily, SpecException > +from lib import YnlFamily, Netlink, NlError, NlPolicy, YnlException > > SYS_SCHEMA_DIR='/usr/share/ynl' > RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' > @@ -79,6 +80,8 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' > return bytes.hex(o) > if isinstance(o, set): > return sorted(o) > + if isinstance(o, NlPolicy): > + return o.to_dict() > return json.JSONEncoder.default(self, o) > > > @@ -313,11 +316,11 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink' > if args.policy: > if args.do: > pol = ynl.get_policy(args.do, 'do') > - output(pol.attrs if pol else None) > + output(pol if pol else None) > args.do = None > if args.dump: > pol = ynl.get_policy(args.dump, 'dump') > - output(pol.attrs if pol else None) > + output(pol if pol else None) > args.dump = None > > if args.ntf: > diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py > index 0eedeee465d8..4411f1902ae4 100644 > --- a/tools/net/ynl/pyynl/lib/ynl.py > +++ b/tools/net/ynl/pyynl/lib/ynl.py > @@ -143,32 +143,113 @@ from .nlspec import SpecFamily > pass > > > -# pylint: disable=too-few-public-methods > class NlPolicy: > """Kernel policy for one mode (do or dump) of one operation. > > - Returned by YnlFamily.get_policy(). Contains a dict of attributes > - the kernel accepts, with their validation constraints. > + Returned by YnlFamily.get_policy(). Attributes of the policy > + are accessible as attributes of the object. Nested policies > + can be accessed indexing the object like a dictionary:: > > - Attributes: > - attrs: dict mapping attribute names to policy dicts, e.g. > - page-pool-stats-get do policy:: > + pol = ynl.get_policy('page-pool-stats-get', 'do') > + pol['info'].type # 'nested' > + pol['info']['id'].type # 'uint' > + pol['info']['id'].min_value # 1 > > - { > - 'info': {'type': 'nested', 'policy': { > - 'id': {'type': 'uint', 'min-value': 1, > - 'max-value': 4294967295}, > - 'ifindex': {'type': 'u32', 'min-value': 1, > - 'max-value': 2147483647}, > - }}, > - } > + Each policy entry always has a 'type' attribute (e.g. u32, string, > + nested). Optional attributes depending on the 'type': min-value, > + max-value, min-length, max-length, mask. > > - Each policy dict always contains 'type' (e.g. u32, string, > - nested). Optional keys: min-value, max-value, min-length, > - max-length, mask, policy. > + Policies can form infinite nesting loops. These loops are trimmed > + when policy is converted to a dict with pol.to_dict(). > """ > - def __init__(self, attrs): > - self.attrs = attrs > + def __init__(self, ynl, policy_idx, policy_table, attr_set, props=None): > + self._policy_idx = policy_idx > + self._policy_table = policy_table > + self._ynl = ynl > + self._props = props or {} > + self._entries = {} > + if policy_idx is not None and policy_idx in policy_table: > + for attr_id, decoded in policy_table[policy_idx].items(): > + if attr_set and attr_id in attr_set.attrs_by_val: > + spec = attr_set.attrs_by_val[attr_id] > + name = spec['name'] > + else: > + spec = None > + name = f'attr-{attr_id}' > + self._entries[name] = (spec, decoded) > + > + def __getitem__(self, name): > + """Descend into a nested policy by attribute name.""" > + spec, decoded = self._entries[name] > + props = dict(decoded) > + child_idx = None > + child_set = None > + if 'policy-idx' in props: > + child_idx = props.pop('policy-idx') > + if spec and 'nested-attributes' in spec.yaml: > + child_set = self._ynl.attr_sets[spec.yaml['nested-attributes']] > + return NlPolicy(self._ynl, child_idx, self._policy_table, > + child_set, props) > + > + def __getattr__(self, name): > + """Access this policy entry's own properties (type, min-value, etc.). > + > + Underscores in the name are converted to dashes, so that > + pol.min_value looks up "min-value". > + """ > + key = name.replace('_', '-') > + try: > + # Hack for level-0 which we still want to have .type but we don't > + # want type to pointlessly show up in the dict / JSON form. > + if not self._props and name == "type": > + return "nested" > + return self._props[key] > + except KeyError: > + raise AttributeError(name) > + > + def get(self, name, default=None): > + """Look up a child policy entry by attribute name, with a default.""" > + try: > + return self[name] > + except KeyError: > + return default > + > + def __contains__(self, name): > + return name in self._entries > + > + def __len__(self): > + return len(self._entries) > + > + def __iter__(self): > + return iter(self._entries) > + > + def keys(self): > + """Return attribute names accepted by this policy.""" > + return self._entries.keys() > + > + def to_dict(self, seen=None): > + """Convert to a plain dict, suitable for JSON serialization. > + > + Nested NlPolicy objects are expanded recursively. Cyclic > + references are trimmed (resolved to just {"type": "nested"}). > + """ > + if seen is None: > + seen = set() > + result = dict(self._props) > + if self._policy_idx is not None: > + if self._policy_idx not in seen: > + seen = seen | {self._policy_idx} > + children = {} > + for name in self: > + children[name] = self[name].to_dict(seen) > + if self._props: > + result['policy'] = children > + else: > + result = children > + return result > + > + def __repr__(self): > + return repr(self.to_dict()) > > > class NlAttr: > @@ -1308,28 +1389,6 @@ from .nlspec import SpecFamily > def do_multi(self, ops): > return self._ops(ops) > > - def _resolve_policy(self, policy_idx, policy_table, attr_set): > - attrs = {} > - if policy_idx not in policy_table: > - return attrs > - for attr_id, decoded in policy_table[policy_idx].items(): > - if attr_set and attr_id in attr_set.attrs_by_val: > - spec = attr_set.attrs_by_val[attr_id] > - name = spec['name'] > - else: > - spec = None > - name = f'attr-{attr_id}' > - if 'policy-idx' in decoded: > - sub_set = None > - if spec and 'nested-attributes' in spec.yaml: > - sub_set = self.attr_sets[spec.yaml['nested-attributes']] > - nested = self._resolve_policy(decoded['policy-idx'], > - policy_table, sub_set) > - del decoded['policy-idx'] > - decoded['policy'] = nested > - attrs[name] = decoded > - return attrs > - > def get_policy(self, op_name, mode): > """Query running kernel for the Netlink policy of an operation. > > @@ -1341,8 +1400,8 @@ from .nlspec import SpecFamily > mode: 'do' or 'dump' > > Returns: > - NlPolicy with an attrs dict mapping attribute names to > - their policy properties (type, min/max, nested, etc.), > + NlPolicy acting as a read-only dict mapping attribute names > + to their policy properties (type, min/max, nested, etc.), > or None if the operation has no policy for the given mode. > Empty policy usually implies that the operation rejects > all attributes. > @@ -1353,5 +1412,4 @@ from .nlspec import SpecFamily > if mode not in op_policy: > return None > policy_idx = op_policy[mode] > - attrs = self._resolve_policy(policy_idx, policy_table, op.attr_set) > - return NlPolicy(attrs) > + return NlPolicy(self, policy_idx, policy_table, op.attr_set) ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-03-12 17:18 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-10 0:53 [PATCH net-next 0/5] tools: ynl: policy query support Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 1/5] tools: ynl: handle pad type during decode Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 2/5] tools: ynl: move policy decoding out of NlMsg Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 3/5] tools: ynl: add short doc to class YnlFamily Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 4/5] tools: ynl: add Python API for easier access to policies Jakub Kicinski 2026-03-10 0:53 ` [PATCH net-next 5/5] tools: ynl: cli: add --policy support Jakub Kicinski 2026-03-11 2:40 ` [PATCH net-next 0/5] tools: ynl: policy query support patchwork-bot+netdevbpf 2026-03-11 11:30 ` Donald Hunter 2026-03-11 18:35 ` Jakub Kicinski 2026-03-12 17:17 ` Donald Hunter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox