From 7c2435ef76e5f2d9fac44b241e4a54113f6eafbf Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 23 May 2023 10:37:47 +0100 Subject: [PATCH 1/2] tools: ynl: Use dict of predefined Structs to decode scalar types Use a dict of predefined Struct() objects to decode scalar types in native, big or little endian format. This removes the repetitive code for the scalar variants and ensures all the signed variants are supported. Signed-off-by: Donald Hunter Signed-off-by: David S. Miller --- tools/net/ynl/lib/ynl.py | 101 +++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 57 deletions(-) diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index aa77bcae4807e..6185ba27f2e7f 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -1,10 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +from collections import namedtuple import functools import os import random import socket import struct +from struct import Struct import yaml from .nlspec import SpecFamily @@ -76,10 +78,17 @@ def __str__(self): class NlAttr: - type_formats = { 'u8' : ('B', 1), 's8' : ('b', 1), - 'u16': ('H', 2), 's16': ('h', 2), - 'u32': ('I', 4), 's32': ('i', 4), - 'u64': ('Q', 8), 's64': ('q', 8) } + 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"), Struct("I"), Struct("i"), Struct("Q"), Struct("q"), Struct("" if byte_order == "big-endian" else "<" - return "" + return format.big if byte_order == "big-endian" \ + else format.little + return format.native - def as_u8(self): - return struct.unpack("B", self.raw)[0] - - def as_u16(self, byte_order=None): - endian = NlAttr.format_byte_order(byte_order) - return struct.unpack(f"{endian}H", self.raw)[0] - - def as_u32(self, byte_order=None): - endian = NlAttr.format_byte_order(byte_order) - return struct.unpack(f"{endian}I", self.raw)[0] - - def as_u64(self, byte_order=None): - endian = NlAttr.format_byte_order(byte_order) - return struct.unpack(f"{endian}Q", self.raw)[0] + def as_scalar(self, attr_type, byte_order=None): + format = self.get_format(attr_type, byte_order) + return format.unpack(self.raw)[0] def as_strz(self): return self.raw.decode('ascii')[:-1] @@ -115,17 +116,17 @@ def as_bin(self): return self.raw def as_c_array(self, type): - format, _ = self.type_formats[type] - return list({ x[0] for x in struct.iter_unpack(format, self.raw) }) + 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 - format, size = self.type_formats[m.type] - decoded = struct.unpack_from(format, self.raw, offset) - offset += size + format = self.get_format(m.type) + decoded = format.unpack_from(self.raw, offset) + offset += format.size value[m.name] = decoded[0] return value @@ -184,11 +185,11 @@ def __init__(self, msg, offset, attr_space=None): if extack.type == Netlink.NLMSGERR_ATTR_MSG: self.extack['msg'] = extack.as_strz() elif extack.type == Netlink.NLMSGERR_ATTR_MISS_TYPE: - self.extack['miss-type'] = extack.as_u32() + self.extack['miss-type'] = extack.as_scalar('u32') elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST: - self.extack['miss-nest'] = extack.as_u32() + self.extack['miss-nest'] = extack.as_scalar('u32') elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: - self.extack['bad-attr-offs'] = extack.as_u32() + self.extack['bad-attr-offs'] = extack.as_scalar('u32') else: if 'unknown' not in self.extack: self.extack['unknown'] = [] @@ -272,11 +273,11 @@ def _genl_load_families(): fam = dict() for attr in gm.raw_attrs: if attr.type == Netlink.CTRL_ATTR_FAMILY_ID: - fam['id'] = attr.as_u16() + fam['id'] = attr.as_scalar('u16') elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME: fam['name'] = attr.as_strz() elif attr.type == Netlink.CTRL_ATTR_MAXATTR: - fam['maxattr'] = attr.as_u32() + fam['maxattr'] = attr.as_scalar('u32') elif attr.type == Netlink.CTRL_ATTR_MCAST_GROUPS: fam['mcast'] = dict() for entry in NlAttrs(attr.raw): @@ -286,7 +287,7 @@ def _genl_load_families(): if entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_NAME: mcast_name = entry_attr.as_strz() elif entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_ID: - mcast_id = entry_attr.as_u32() + mcast_id = entry_attr.as_scalar('u32') if mcast_name and mcast_id is not None: fam['mcast'][mcast_name] = mcast_id if 'name' in fam and 'id' in fam: @@ -304,9 +305,9 @@ def __init__(self, nl_msg, fixed_header_members=[]): self.fixed_header_attrs = dict() for m in fixed_header_members: - format, size = NlAttr.type_formats[m.type] - decoded = struct.unpack_from(format, nl_msg.raw, offset) - offset += size + format = NlAttr.get_format(m.type) + decoded = format.unpack_from(nl_msg.raw, offset) + offset += format.size self.fixed_header_attrs[m.name] = decoded[0] self.raw = nl_msg.raw[offset:] @@ -381,21 +382,13 @@ def _add_attr(self, space, name, value): attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue) elif attr["type"] == 'flag': attr_payload = b'' - elif attr["type"] == 'u8': - attr_payload = struct.pack("B", int(value)) - elif attr["type"] == 'u16': - endian = NlAttr.format_byte_order(attr.byte_order) - attr_payload = struct.pack(f"{endian}H", int(value)) - elif attr["type"] == 'u32': - endian = NlAttr.format_byte_order(attr.byte_order) - attr_payload = struct.pack(f"{endian}I", int(value)) - elif attr["type"] == 'u64': - endian = NlAttr.format_byte_order(attr.byte_order) - attr_payload = struct.pack(f"{endian}Q", int(value)) elif attr["type"] == 'string': attr_payload = str(value).encode('ascii') + b'\x00' elif attr["type"] == 'binary': attr_payload = value + elif attr['type'] in NlAttr.type_formats: + format = NlAttr.get_format(attr['type'], attr.byte_order) + attr_payload = format.pack(int(value)) else: raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}') @@ -434,22 +427,16 @@ def _decode(self, attrs, space): if attr_spec["type"] == 'nest': subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes']) decoded = subdict - elif attr_spec['type'] == 'u8': - decoded = attr.as_u8() - elif attr_spec['type'] == 'u16': - decoded = attr.as_u16(attr_spec.byte_order) - elif attr_spec['type'] == 'u32': - decoded = attr.as_u32(attr_spec.byte_order) - elif attr_spec['type'] == 'u64': - decoded = attr.as_u64(attr_spec.byte_order) elif attr_spec["type"] == 'string': decoded = attr.as_strz() elif attr_spec["type"] == 'binary': decoded = self._decode_binary(attr, attr_spec) elif attr_spec["type"] == 'flag': decoded = True + elif attr_spec["type"] in NlAttr.type_formats: + decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) else: - raise Exception(f'Unknown {attr.type} {attr_spec["name"]} {attr_spec["type"]}') + raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') if not attr_spec.is_multi: rsp[attr_spec['name']] = decoded @@ -555,8 +542,8 @@ def _op(self, method, vals, dump=False): fixed_header_members = self.consts[op.fixed_header].members for m in fixed_header_members: value = vals.pop(m.name) - format, _ = NlAttr.type_formats[m.type] - msg += struct.pack(format, value) + format = NlAttr.get_format(m.type) + msg += format.pack(value) for name, value in vals.items(): msg += self._add_attr(op.attr_set.name, name, value) msg = _genl_msg_finalize(msg) From bddd2e561b0ad5ca42e16fb26a20fc806d521912 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 23 May 2023 10:37:48 +0100 Subject: [PATCH 2/2] tools: ynl: Handle byte-order in struct members Add support for byte-order in struct members in the genetlink-legacy spec. Signed-off-by: Donald Hunter Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/netlink/genetlink-legacy.yaml | 2 ++ tools/net/ynl/lib/nlspec.py | 4 +++- tools/net/ynl/lib/ynl.py | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index b33541a51d6bb..b5319cde9e171 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -122,6 +122,8 @@ properties: enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string ] len: $ref: '#/$defs/len-or-define' + byte-order: + enum: [ little-endian, big-endian ] # End genetlink-legacy attribute-sets: diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index a0241add38392..c624cdfde223e 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -226,11 +226,13 @@ class SpecStructMember(SpecElement): Represents a single struct member attribute. Attributes: - type string, type of the member attribute + type string, type of the member attribute + byte_order string or None for native byte order """ def __init__(self, family, yaml): super().__init__(family, yaml) self.type = yaml['type'] + self.byte_order = yaml.get('byte-order') class SpecStruct(SpecElement): diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py index 6185ba27f2e7f..39a2296c0003b 100644 --- a/tools/net/ynl/lib/ynl.py +++ b/tools/net/ynl/lib/ynl.py @@ -124,7 +124,7 @@ def as_struct(self, members): offset = 0 for m in members: # TODO: handle non-scalar members - format = self.get_format(m.type) + format = self.get_format(m.type, m.byte_order) decoded = format.unpack_from(self.raw, offset) offset += format.size value[m.name] = decoded[0] @@ -305,7 +305,7 @@ def __init__(self, nl_msg, fixed_header_members=[]): self.fixed_header_attrs = dict() for m in fixed_header_members: - format = NlAttr.get_format(m.type) + format = NlAttr.get_format(m.type, m.byte_order) decoded = format.unpack_from(nl_msg.raw, offset) offset += format.size self.fixed_header_attrs[m.name] = decoded[0] @@ -542,7 +542,7 @@ def _op(self, method, vals, dump=False): fixed_header_members = self.consts[op.fixed_header].members for m in fixed_header_members: value = vals.pop(m.name) - format = NlAttr.get_format(m.type) + format = NlAttr.get_format(m.type, m.byte_order) msg += format.pack(value) for name, value in vals.items(): msg += self._add_attr(op.attr_set.name, name, value)