From bf8b832374fb85931acda478920e3a270f53bf17 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:47 +0000 Subject: tools/net/ynl: Support sub-messages in nested attribute spaces Sub-message selectors could only be resolved using values from the current nest level. Enable value lookup in outer scopes by using collections.ChainMap to implement an ordered lookup from nested to outer scopes. Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-3-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 1e10512b2117..f24581759acf 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -405,6 +405,26 @@ class GenlProtocol(NetlinkProtocol): return self.genl_family['mcast'][mcast_name] + +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") + + # # YNL implementation details. # @@ -548,24 +568,22 @@ class YnlFamily(SpecFamily): else: rsp[name] = [decoded] - def _resolve_selector(self, attr_spec, vals): + def _resolve_selector(self, attr_spec, search_attrs): 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 - if selector not in vals: - raise Exception(f"There is no value for {selector} to resolve '{attr_spec.name}'") - value = vals[selector] + value = search_attrs.lookup(selector) 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 - def _decode_sub_msg(self, attr, attr_spec, rsp): - msg_format = self._resolve_selector(attr_spec, rsp) + def _decode_sub_msg(self, attr, attr_spec, search_attrs): + msg_format = self._resolve_selector(attr_spec, search_attrs) decoded = {} offset = 0 if msg_format.fixed_header: @@ -579,10 +597,12 @@ class YnlFamily(SpecFamily): raise Exception(f"Unknown attribute-set '{attr_space}' when decoding '{attr_spec.name}'") return decoded - def _decode(self, attrs, space): + def _decode(self, attrs, space, outer_attrs = None): if space: attr_space = self.attr_sets[space] rsp = dict() + search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs) + for attr in attrs: try: attr_spec = attr_space.attrs_by_val[attr.type] @@ -594,7 +614,7 @@ class YnlFamily(SpecFamily): continue if attr_spec["type"] == 'nest': - subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes']) + subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes'], search_attrs) decoded = subdict elif attr_spec["type"] == 'string': decoded = attr.as_strz() @@ -617,7 +637,7 @@ class YnlFamily(SpecFamily): selector = self._decode_enum(selector, attr_spec) decoded = {"value": value, "selector": selector} elif attr_spec["type"] == 'sub-message': - decoded = self._decode_sub_msg(attr, attr_spec, rsp) + decoded = self._decode_sub_msg(attr, attr_spec, vals) else: if not self.process_unknown: raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') -- cgit v1.2.3 From 5f2823c48ad6f63fc033e8ec5e995fcd5cbab409 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:49 +0000 Subject: tools/net/ynl: Refactor fixed header encoding into separate method Refactor the fixed header encoding into a separate _encode_struct method so that it can be reused for fixed headers in sub-messages and for encoding structs. Signed-off-by: Donald Hunter Reviewed-by: Breno Leitao Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-5-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index f24581759acf..b22ddedb801b 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -720,6 +720,20 @@ class YnlFamily(SpecFamily): fixed_header_attrs[m.name] = value return fixed_header_attrs + def _encode_struct(self, name, vals): + members = self.consts[name].members + attr_payload = b'' + for m in members: + value = vals.pop(m.name) if m.name in vals else 0 + if m.type == 'pad': + attr_payload += bytearray(m.len) + elif m.type == 'binary': + attr_payload += bytes.fromhex(value) + else: + format = NlAttr.get_format(m.type, m.byte_order) + attr_payload += format.pack(value) + return attr_payload + def handle_ntf(self, decoded): msg = dict() if self.include_raw: @@ -779,18 +793,8 @@ class YnlFamily(SpecFamily): req_seq = random.randint(1024, 65535) msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) - fixed_header_members = [] if op.fixed_header: - fixed_header_members = self.consts[op.fixed_header].members - for m in fixed_header_members: - value = vals.pop(m.name) if m.name in vals else 0 - if m.type == 'pad': - msg += bytearray(m.len) - elif m.type == 'binary': - msg += bytes.fromhex(value) - else: - format = NlAttr.get_format(m.type, m.byte_order) - msg += format.pack(value) + msg += self._encode_struct(op.fixed_header, vals) for name, value in vals.items(): msg += self._add_attr(op.attr_set.name, name, value) msg = _genl_msg_finalize(msg) -- cgit v1.2.3 From ab463c4342d1d14f25357c0f9f1a9744d7417edd Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:50 +0000 Subject: tools/net/ynl: Add support for encoding sub-messages Add sub-message encoding to ynl. This makes it possible to create tc qdiscs and other polymorphic netlink objects. Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-6-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index b22ddedb801b..b063094e8a4b 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -469,7 +469,7 @@ class YnlFamily(SpecFamily): self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, mcast_id) - def _add_attr(self, space, name, value): + def _add_attr(self, space, name, value, search_attrs): try: attr = self.attr_sets[space][name] except KeyError: @@ -478,8 +478,10 @@ class YnlFamily(SpecFamily): if attr["type"] == 'nest': nl_type |= Netlink.NLA_F_NESTED attr_payload = b'' + sub_attrs = SpaceAttrs(self.attr_sets[space], value, search_attrs) for subname, subvalue in value.items(): - attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue) + attr_payload += self._add_attr(attr['nested-attributes'], + subname, subvalue, sub_attrs) elif attr["type"] == 'flag': attr_payload = b'' elif attr["type"] == 'string': @@ -489,6 +491,8 @@ class YnlFamily(SpecFamily): attr_payload = value elif isinstance(value, str): attr_payload = bytes.fromhex(value) + elif isinstance(value, dict) and attr.struct_name: + attr_payload = self._encode_struct(attr.struct_name, value) else: raise Exception(f'Unknown type for binary attribute, value: {value}') elif attr.is_auto_scalar: @@ -501,6 +505,20 @@ class YnlFamily(SpecFamily): attr_payload = format.pack(int(value)) elif attr['type'] in "bitfield32": attr_payload = struct.pack("II", int(value["value"]), int(value["selector"])) + 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}'") else: raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}') @@ -637,7 +655,7 @@ class YnlFamily(SpecFamily): selector = self._decode_enum(selector, attr_spec) decoded = {"value": value, "selector": selector} elif attr_spec["type"] == 'sub-message': - decoded = self._decode_sub_msg(attr, attr_spec, vals) + decoded = self._decode_sub_msg(attr, attr_spec, search_attrs) else: if not self.process_unknown: raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') @@ -795,8 +813,9 @@ class YnlFamily(SpecFamily): msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) if op.fixed_header: msg += self._encode_struct(op.fixed_header, vals) + search_attrs = SpaceAttrs(op.attr_set, vals) for name, value in vals.items(): - msg += self._add_attr(op.attr_set.name, name, value) + msg += self._add_attr(op.attr_set.name, name, value, search_attrs) msg = _genl_msg_finalize(msg) self.sock.send(msg, 0) -- cgit v1.2.3 From a387a921139e07587b0fb93c9c0fec8b6800776d Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:51 +0000 Subject: tools/net/ynl: Encode default values for binary blobs Add support for defaulting binary byte arrays to all zeros as well as defaulting scalar values to 0 when encoding input parameters. Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-7-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index b063094e8a4b..d04435f26f89 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -742,12 +742,17 @@ class YnlFamily(SpecFamily): members = self.consts[name].members attr_payload = b'' for m in members: - value = vals.pop(m.name) if m.name in vals else 0 + value = vals.pop(m.name) if m.name in vals else None if m.type == 'pad': attr_payload += bytearray(m.len) elif m.type == 'binary': - attr_payload += bytes.fromhex(value) + if value is None: + attr_payload += bytearray(m.len) + else: + attr_payload += bytes.fromhex(value) else: + if value is None: + value = 0 format = NlAttr.get_format(m.type, m.byte_order) attr_payload += format.pack(value) return attr_payload -- cgit v1.2.3 From e45fee0f49fcbafe9f0f883ef22afb890504a5e6 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:52 +0000 Subject: tools/net/ynl: Combine struct decoding logic in ynl _decode_fixed_header() and NlAttr.as_struct() both implemented struct decoding logic. Deduplicate the code into newly named _decode_struct() method. Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-8-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index d04435f26f89..0e7e9f60ab7e 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -148,23 +148,6 @@ class NlAttr: format = self.get_format(type) return [ x[0] for x in format.iter_unpack(self.raw) ] - def as_struct(self, members): - value = dict() - offset = 0 - for m in members: - # TODO: handle non-scalar members - if m.type == 'binary': - decoded = self.raw[offset : offset + m['len']] - offset += m['len'] - elif m.type in NlAttr.type_formats: - format = self.get_format(m.type, m.byte_order) - [ decoded ] = format.unpack_from(self.raw, offset) - offset += format.size - if m.display_hint: - decoded = self.formatted_string(decoded, m.display_hint) - value[m.name] = decoded - return value - def __repr__(self): return f"[type:{self.type} len:{self._len}] {self.raw}" @@ -541,11 +524,7 @@ class YnlFamily(SpecFamily): def _decode_binary(self, attr, attr_spec): if attr_spec.struct_name: - members = self.consts[attr_spec.struct_name] - decoded = attr.as_struct(members) - for m in members: - if m.enum: - decoded[m.name] = self._decode_enum(decoded[m.name], m) + decoded = self._decode_struct(attr.raw, attr_spec.struct_name) elif attr_spec.sub_type: decoded = attr.as_c_array(attr_spec.sub_type) else: @@ -605,7 +584,7 @@ class YnlFamily(SpecFamily): decoded = {} offset = 0 if msg_format.fixed_header: - decoded.update(self._decode_fixed_header(attr, msg_format.fixed_header)); + decoded.update(self._decode_struct(attr.raw, msg_format.fixed_header)); offset = self._fixed_header_size(msg_format.fixed_header) if msg_format.attr_set: if msg_format.attr_set in self.attr_sets: @@ -717,26 +696,28 @@ class YnlFamily(SpecFamily): else: return 0 - def _decode_fixed_header(self, msg, name): - fixed_header_members = self.consts[name].members - fixed_header_attrs = dict() + def _decode_struct(self, data, name): + members = self.consts[name].members + attrs = dict() offset = 0 - for m in fixed_header_members: + for m in members: value = None if m.type == 'pad': offset += m.len elif m.type == 'binary': - value = msg.raw[offset : offset + m.len] + value = data[offset : offset + m.len] offset += m.len else: format = NlAttr.get_format(m.type, m.byte_order) - [ value ] = format.unpack_from(msg.raw, offset) + [ value ] = format.unpack_from(data, offset) offset += format.size if value is not None: if m.enum: value = self._decode_enum(value, m) - fixed_header_attrs[m.name] = value - return fixed_header_attrs + elif m.display_hint: + value = NlAttr.formatted_string(value, m.display_hint) + attrs[m.name] = value + return attrs def _encode_struct(self, name, vals): members = self.consts[name].members @@ -764,7 +745,7 @@ class YnlFamily(SpecFamily): op = self.rsp_by_value[decoded.cmd()] attrs = self._decode(decoded.raw_attrs, op.attr_set.name) if op.fixed_header: - attrs.update(self._decode_fixed_header(decoded, op.fixed_header)) + attrs.update(self._decode_struct(decoded.raw, op.fixed_header)) msg['name'] = op['name'] msg['msg'] = attrs @@ -856,7 +837,7 @@ class YnlFamily(SpecFamily): rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name) if op.fixed_header: - rsp_msg.update(self._decode_fixed_header(decoded, op.fixed_header)) + rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header)) rsp.append(rsp_msg) if not rsp: -- cgit v1.2.3 From 886365cf40b2b53864f1ef0655fe7999c2477418 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:53 +0000 Subject: tools/net/ynl: Rename _fixed_header_size() to _struct_size() Refactor the _fixed_header_size() method to be _struct_size() so that naming is consistent with _encode_struct() and _decode_struct(). Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-9-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 0e7e9f60ab7e..173ef4489e38 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -353,7 +353,7 @@ class NetlinkProtocol: fixed_header_size = 0 if ynl: op = ynl.rsp_by_value[msg.cmd()] - fixed_header_size = ynl._fixed_header_size(op.fixed_header) + fixed_header_size = ynl._struct_size(op.fixed_header) msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size) return msg @@ -585,7 +585,7 @@ class YnlFamily(SpecFamily): offset = 0 if msg_format.fixed_header: decoded.update(self._decode_struct(attr.raw, msg_format.fixed_header)); - offset = self._fixed_header_size(msg_format.fixed_header) + offset = self._struct_size(msg_format.fixed_header) 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) @@ -675,18 +675,18 @@ class YnlFamily(SpecFamily): return msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) - offset = 20 + self._fixed_header_size(op.fixed_header) + offset = 20 + self._struct_size(op.fixed_header) path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, extack['bad-attr-offs']) if path: del extack['bad-attr-offs'] extack['bad-attr'] = path - def _fixed_header_size(self, name): + def _struct_size(self, name): if name: - fixed_header_members = self.consts[name].members + members = self.consts[name].members size = 0 - for m in fixed_header_members: + for m in members: if m.type in ['pad', 'binary']: size += m.len else: -- cgit v1.2.3 From 971c3eeaf668ed312d032d12becb106982d2b2bd Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:54 +0000 Subject: tools/net/ynl: Move formatted_string method out of NlAttr The formatted_string() class method was in NlAttr so that it could be accessed by NlAttr.as_struct(). Now that as_struct() has been removed, move formatted_string() to YnlFamily as an internal helper method. Signed-off-by: Donald Hunter Reviewed-by: Breno Leitao Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-10-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 173ef4489e38..2b0ca61deaf8 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -113,20 +113,6 @@ class NlAttr: else format.little return format.native - @classmethod - def formatted_string(cls, raw, display_hint): - if display_hint == 'mac': - formatted = ':'.join('%02x' % b for b in raw) - elif display_hint == 'hex': - formatted = bytes.hex(raw, ' ') - 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 - def as_scalar(self, attr_type, byte_order=None): format = self.get_format(attr_type, byte_order) return format.unpack(self.raw)[0] @@ -530,7 +516,7 @@ class YnlFamily(SpecFamily): else: decoded = attr.as_bin() if attr_spec.display_hint: - decoded = NlAttr.formatted_string(decoded, attr_spec.display_hint) + decoded = self._formatted_string(decoded, attr_spec.display_hint) return decoded def _decode_array_nest(self, attr, attr_spec): @@ -715,7 +701,7 @@ class YnlFamily(SpecFamily): if m.enum: value = self._decode_enum(value, m) elif m.display_hint: - value = NlAttr.formatted_string(value, m.display_hint) + value = self._formatted_string(value, m.display_hint) attrs[m.name] = value return attrs @@ -738,6 +724,19 @@ class YnlFamily(SpecFamily): attr_payload += format.pack(value) return attr_payload + def _formatted_string(self, raw, display_hint): + if display_hint == 'mac': + formatted = ':'.join('%02x' % b for b in raw) + elif display_hint == 'hex': + formatted = bytes.hex(raw, ' ') + 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 + def handle_ntf(self, decoded): msg = dict() if self.include_raw: -- cgit v1.2.3 From bf08f32c8cedb12a23efcdc2c9584601d7030e16 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 29 Jan 2024 22:34:55 +0000 Subject: tools/net/ynl: Add support for nested structs Make it possible for struct definitions to reference other struct definitions ofr binary members. For example, the tbf qdisc uses this struct definition for its parms attribute: - name: tc-tbf-qopt type: struct members: - name: rate type: binary struct: tc-ratespec - name: peakrate type: binary struct: tc-ratespec - name: limit type: u32 - name: buffer type: u32 - name: mtu type: u32 This adds the necessary schema changes and adds nested struct encoding and decoding to ynl. Signed-off-by: Donald Hunter Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240129223458.52046-11-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/netlink-raw.yaml | 15 ++++++++++++--- tools/net/ynl/lib/nlspec.py | 2 ++ tools/net/ynl/lib/ynl.py | 26 ++++++++++++++++++++------ 3 files changed, 34 insertions(+), 9 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml index 04b92f1a5cd6..ac4e05415f2f 100644 --- a/Documentation/netlink/netlink-raw.yaml +++ b/Documentation/netlink/netlink-raw.yaml @@ -152,14 +152,23 @@ properties: the right formatting mechanism when displaying values of this type. enum: [ hex, mac, fddi, ipv4, ipv6, uuid ] + struct: + description: Name of the nested struct type. + type: string if: properties: type: - oneOf: - - const: binary - - const: pad + const: pad then: required: [ len ] + if: + properties: + type: + const: binary + then: + oneOf: + - required: [ len ] + - required: [ struct ] # End genetlink-legacy attribute-sets: diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index 44f13e383e8a..5d197a12ab8d 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -248,6 +248,7 @@ class SpecStructMember(SpecElement): len integer, optional byte length of binary types display_hint string, hint to help choose format specifier when displaying the value + struct string, name of nested struct type """ def __init__(self, family, yaml): super().__init__(family, yaml) @@ -256,6 +257,7 @@ class SpecStructMember(SpecElement): self.enum = yaml.get('enum') self.len = yaml.get('len') self.display_hint = yaml.get('display-hint') + self.struct = yaml.get('struct') class SpecStruct(SpecElement): diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 2b0ca61deaf8..0f4193cc2e3b 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -674,7 +674,10 @@ class YnlFamily(SpecFamily): size = 0 for m in members: if m.type in ['pad', 'binary']: - size += m.len + if m.struct: + size += self._struct_size(m.struct) + else: + size += m.len else: format = NlAttr.get_format(m.type, m.byte_order) size += format.size @@ -691,8 +694,14 @@ class YnlFamily(SpecFamily): if m.type == 'pad': offset += m.len elif m.type == 'binary': - value = data[offset : offset + m.len] - offset += m.len + 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 else: format = NlAttr.get_format(m.type, m.byte_order) [ value ] = format.unpack_from(data, offset) @@ -713,10 +722,15 @@ class YnlFamily(SpecFamily): if m.type == 'pad': attr_payload += bytearray(m.len) elif m.type == 'binary': - if value is None: - attr_payload += bytearray(m.len) + if m.struct: + if value is None: + value = dict() + attr_payload += self._encode_struct(m.struct, value) else: - attr_payload += bytes.fromhex(value) + if value is None: + attr_payload += bytearray(m.len) + else: + attr_payload += bytes.fromhex(value) else: if value is None: value = 0 -- cgit v1.2.3 From b9bcfc3bc978f7b81104d2c01d4fe29a6f45e17a Mon Sep 17 00:00:00 2001 From: Alessandro Marcolini Date: Sat, 3 Feb 2024 14:16:53 +0100 Subject: tools: ynl: add support for encoding multi-attr Multi-attr elements could not be encoded because of missing logic in the ynl code. Enable encoding of these attributes by checking if the attribute is a multi-attr and if the value to be processed is a list. This has been tested both with the taprio and ets qdisc which contain this kind of attributes. Signed-off-by: Alessandro Marcolini Reviewed-by: Donald Hunter Reviewed-by: Jakub Kicinski Link: https://lore.kernel.org/r/c5bc9f5797168dbf7a4379c42f38d5de8ac7f38a.1706962013.git.alessandromarcolini99@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 0f4193cc2e3b..03c7ca6aaae9 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -444,6 +444,13 @@ class YnlFamily(SpecFamily): except KeyError: raise Exception(f"Space '{space}' has no attribute '{name}'") nl_type = attr.value + + 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 + if attr["type"] == 'nest': nl_type |= Netlink.NLA_F_NESTED attr_payload = b'' -- cgit v1.2.3 From d0bcc15cbae806cad7d1d90003c82ecb5833b533 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 15 Feb 2024 13:27:26 +0100 Subject: 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 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 Signed-off-by: David S. Miller --- tools/net/ynl/lib/ynl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 03c7ca6aaae9..f45ee5f29bed 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -588,10 +588,10 @@ class YnlFamily(SpecFamily): return decoded def _decode(self, attrs, space, outer_attrs = None): + rsp = dict() if space: attr_space = self.attr_sets[space] - rsp = dict() - search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs) + search_attrs = SpaceAttrs(attr_space, rsp, outer_attrs) for attr in attrs: try: -- cgit v1.2.3 From ac95b1fca034d50d42a058a9476e0fe555653d0d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 22 Feb 2024 14:43:49 +0100 Subject: tools: ynl: allow user to specify flag attr with bool values The flag attr presence in Netlink message indicates value "true", if it is missing in the message it means "false". Allow user to specify attrname with value "true"/"false" in json for flag attrs, treat "false" value properly. Signed-off-by: Jiri Pirko Reviewed-by: Donald Hunter Link: https://lore.kernel.org/r/20240222134351.224704-2-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index f45ee5f29bed..1c5c7662dc9a 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -459,6 +459,9 @@ class YnlFamily(SpecFamily): attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue, sub_attrs) elif attr["type"] == 'flag': + if not value: + # If value is absent or false then skip attribute creation. + return b'' attr_payload = b'' elif attr["type"] == 'string': attr_payload = str(value).encode('ascii') + b'\x00' -- cgit v1.2.3 From ffe10a4546feaae085a269f6e99680b9eda7d5a6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 22 Feb 2024 14:43:50 +0100 Subject: tools: ynl: process all scalar types encoding in single elif statement As a preparation to handle enums for scalar values, unify the processing of all scalar types in a single elif statement. Signed-off-by: Jiri Pirko Reviewed-by: Donald Hunter Reviewed-by: Jacob Keller Link: https://lore.kernel.org/r/20240222134351.224704-3-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 1c5c7662dc9a..e459a130170b 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -474,14 +474,14 @@ class YnlFamily(SpecFamily): attr_payload = self._encode_struct(attr.struct_name, value) else: raise Exception(f'Unknown type for binary attribute, value: {value}') - elif attr.is_auto_scalar: + elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar: scalar = int(value) - real_type = attr["type"][0] + ('32' if scalar.bit_length() <= 32 else '64') - format = NlAttr.get_format(real_type, attr.byte_order) - attr_payload = format.pack(int(value)) - elif attr['type'] in NlAttr.type_formats: - format = NlAttr.get_format(attr['type'], attr.byte_order) - attr_payload = format.pack(int(value)) + 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) elif attr['type'] in "bitfield32": attr_payload = struct.pack("II", int(value["value"]), int(value["selector"])) elif attr['type'] == 'sub-message': -- cgit v1.2.3 From e8a6c515ff5f52e1ee24397116a05cd4fc1bae99 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 22 Feb 2024 14:43:51 +0100 Subject: 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 Reviewed-by: Donald Hunter Link: https://lore.kernel.org/r/20240222134351.224704-4-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index e459a130170b..ac55aa5a3083 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -438,6 +438,26 @@ class YnlFamily(SpecFamily): self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, mcast_id) + 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 + return self._encode_enum(attr_spec, value); + def _add_attr(self, space, name, value, search_attrs): try: attr = self.attr_sets[space][name] @@ -475,7 +495,7 @@ class YnlFamily(SpecFamily): else: raise Exception(f'Unknown type for binary attribute, value: {value}') elif attr['type'] in NlAttr.type_formats or attr.is_auto_scalar: - scalar = int(value) + scalar = self._get_scalar(attr, value) if attr.is_auto_scalar: attr_type = attr["type"][0] + ('32' if scalar.bit_length() <= 32 else '64') else: @@ -483,7 +503,9 @@ class YnlFamily(SpecFamily): format = NlAttr.get_format(attr_type, attr.byte_order) attr_payload = format.pack(scalar) elif attr['type'] in "bitfield32": - attr_payload = struct.pack("II", int(value["value"]), int(value["selector"])) + 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) elif attr['type'] == 'sub-message': msg_format = self._resolve_selector(attr, search_attrs) attr_payload = b'' -- cgit v1.2.3 From 7df7231d6a6b68b4b68fb4e38a4639997a208fd2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 4 Mar 2024 21:33:07 -0800 Subject: tools: ynl: move the new line in NlMsg __repr__ We add the new line even if message has no error or extack, which leads to print(nl_msg) ending with two new lines. Reviewed-by: Donald Hunter Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/net/ynl/lib/ynl.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index ac55aa5a3083..92ade9105f31 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -213,11 +213,11 @@ class NlMsg: return self.nl_type def __repr__(self): - msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n" + msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}" if self.error: - msg += '\terror: ' + str(self.error) + msg += '\n\terror: ' + str(self.error) if self.extack: - msg += '\textack: ' + repr(self.extack) + msg += '\n\textack: ' + repr(self.extack) return msg -- cgit v1.2.3 From 7c93a88785dae6b61dc736b46594d088989e484b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 4 Mar 2024 21:33:08 -0800 Subject: tools: ynl: allow setting recv() size Make the size of the buffer we use for recv() configurable. The details of the buffer sizing in netlink are somewhat arcane, we could spend a lot of time polishing this API. Let's just leave some hopefully helpful comments for now. This is a for-developers-only feature, anyway. Signed-off-by: Jakub Kicinski Reviewed-by: Donald Hunter Signed-off-by: David S. Miller --- tools/net/ynl/lib/ynl.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 92ade9105f31..c3ff5be33e4e 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -84,6 +84,10 @@ class NlError(Exception): return f"Netlink error: {os.strerror(-self.nl_msg.error)}\n{self.nl_msg}" +class ConfigError(Exception): + pass + + class NlAttr: ScalarFormat = namedtuple('ScalarFormat', ['native', 'big', 'little']) type_formats = { @@ -400,7 +404,8 @@ class SpaceAttrs: class YnlFamily(SpecFamily): - def __init__(self, def_path, schema=None, process_unknown=False): + def __init__(self, def_path, schema=None, process_unknown=False, + recv_size=0): super().__init__(def_path, schema) self.include_raw = False @@ -415,6 +420,16 @@ class YnlFamily(SpecFamily): except KeyError: raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel") + # 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() + self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.nlproto.proto_num) self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1) @@ -799,7 +814,7 @@ class YnlFamily(SpecFamily): def check_ntf(self): while True: try: - reply = self.sock.recv(128 * 1024, socket.MSG_DONTWAIT) + reply = self.sock.recv(self._recv_size, socket.MSG_DONTWAIT) except BlockingIOError: return @@ -854,7 +869,7 @@ class YnlFamily(SpecFamily): done = False rsp = [] while not done: - reply = self.sock.recv(128 * 1024) + reply = self.sock.recv(self._recv_size) nms = NlMsgs(reply, attr_space=op.attr_set) for nl_msg in nms: if nl_msg.extack: -- cgit v1.2.3 From a6a41521f95e5263a52ac79c02167a59ccc7183b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 4 Mar 2024 21:33:09 -0800 Subject: tools: ynl: support debug printing messages For manual debug, allow printing the netlink level messages to stderr. Reviewed-by: Donald Hunter Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/net/ynl/lib/ynl.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index c3ff5be33e4e..239e22b7a85f 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -7,6 +7,7 @@ import random import socket import struct from struct import Struct +import sys import yaml import ipaddress import uuid @@ -420,6 +421,7 @@ class YnlFamily(SpecFamily): except KeyError: raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel") + self._recv_dbg = False # 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. @@ -453,6 +455,17 @@ class YnlFamily(SpecFamily): self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, mcast_id) + 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) + 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): @@ -819,6 +832,7 @@ class YnlFamily(SpecFamily): return nms = NlMsgs(reply) + self._recv_dbg_print(reply, nms) for nl_msg in nms: if nl_msg.error: print("Netlink error in ntf!?", os.strerror(-nl_msg.error)) @@ -871,6 +885,7 @@ class YnlFamily(SpecFamily): while not done: reply = self.sock.recv(self._recv_size) nms = NlMsgs(reply, attr_space=op.attr_set) + self._recv_dbg_print(reply, nms) for nl_msg in nms: if nl_msg.extack: self._decode_extack(msg, op, nl_msg.extack) -- cgit v1.2.3 From cecbc52c46e235bd651f7b3ebbf481764846a2d9 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Wed, 6 Mar 2024 23:10:41 +0000 Subject: tools/net/ynl: Fix extack decoding for netlink-raw Extack decoding was using a hard-coded msg header size of 20 but netlink-raw has a header size of 16. Use a protocol specific msghdr_size() when decoding the attr offssets. Signed-off-by: Donald Hunter Link: https://lore.kernel.org/r/20240306231046.97158-2-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 239e22b7a85f..b810a478a304 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -353,6 +353,9 @@ class NetlinkProtocol: raise Exception(f'Multicast group "{mcast_name}" not present in the spec') return mcast_groups[mcast_name].value + def msghdr_size(self): + return 16 + class GenlProtocol(NetlinkProtocol): def __init__(self, family_name): @@ -378,6 +381,8 @@ class GenlProtocol(NetlinkProtocol): raise Exception(f'Multicast group "{mcast_name}" not present in the family') return self.genl_family['mcast'][mcast_name] + def msghdr_size(self): + return super().msghdr_size() + 4 class SpaceAttrs: @@ -721,7 +726,7 @@ class YnlFamily(SpecFamily): return msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) - offset = 20 + self._struct_size(op.fixed_header) + offset = self.nlproto.msghdr_size() + self._struct_size(op.fixed_header) path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, extack['bad-attr-offs']) if path: -- cgit v1.2.3 From b6e6a76dec330dc0c3ed3149acc2669f3ebfb3cb Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Wed, 6 Mar 2024 23:10:44 +0000 Subject: tools/net/ynl: Add nest-type-value decoding The nlctrl genetlink-legacy family uses nest-type-value encoding as described in Documentation/userspace-api/netlink/genetlink-legacy.rst Add nest-type-value decoding to ynl. Signed-off-by: Donald Hunter Link: https://lore.kernel.org/r/20240306231046.97158-5-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index b810a478a304..2d7fdd903d9e 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -595,6 +595,16 @@ class YnlFamily(SpecFamily): decoded.append({ item.type: subattrs }) return decoded + 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 + def _decode_unknown(self, attr): if attr.is_nest: return self._decode(NlAttrs(attr.raw), None) @@ -686,6 +696,8 @@ class YnlFamily(SpecFamily): decoded = {"value": value, "selector": selector} elif attr_spec["type"] == 'sub-message': decoded = self._decode_sub_msg(attr, attr_spec, search_attrs) + elif attr_spec["type"] == 'nest-type-value': + decoded = self._decode_nest_type_value(attr, attr_spec) else: if not self.process_unknown: raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') -- cgit v1.2.3 From a0d942960d9b0ba352b2400e1a3b8fb02d911143 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 8 Mar 2024 11:25:55 -0800 Subject: tools: ynl: remove trailing semicolon Commit e8a6c515ff5f ("tools: ynl: allow user to pass enum string instead of scalar value") added a semicolon at the end of a line. Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20240308192555.2550253-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/net/ynl/lib/ynl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/net/ynl/lib/ynl.py') diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 2d7fdd903d9e..5fa7957f6e0f 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -489,7 +489,7 @@ class YnlFamily(SpecFamily): except (ValueError, TypeError) as e: if 'enum' not in attr_spec: raise e - return self._encode_enum(attr_spec, value); + return self._encode_enum(attr_spec, value) def _add_attr(self, space, name, value, search_attrs): try: -- cgit v1.2.3