mirror of
https://github.com/torvalds/linux.git
synced 2026-05-22 14:12:07 +02:00
tools/net/ynl: Add 'sub-message' attribute decoding to ynl
Implement the 'sub-message' attribute type in ynl. Encode support is not yet implemented. Support for sub-message selectors at a different nest level from the key attribute is not yet supported. Reviewed-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Donald Hunter <donald.hunter@gmail.com> Link: https://lore.kernel.org/r/20231215093720.18774-5-donald.hunter@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
17ed5c1a9e
commit
1769e2be4b
|
|
@ -158,6 +158,9 @@ class SpecAttr(SpecElement):
|
||||||
len integer, optional byte length of binary types
|
len integer, optional byte length of binary types
|
||||||
display_hint string, hint to help choose format specifier
|
display_hint string, hint to help choose format specifier
|
||||||
when displaying the value
|
when displaying the value
|
||||||
|
sub_message string, name of sub message type
|
||||||
|
selector string, name of attribute used to select
|
||||||
|
sub-message type
|
||||||
|
|
||||||
is_auto_scalar bool, attr is a variable-size scalar
|
is_auto_scalar bool, attr is a variable-size scalar
|
||||||
"""
|
"""
|
||||||
|
|
@ -173,6 +176,8 @@ class SpecAttr(SpecElement):
|
||||||
self.byte_order = yaml.get('byte-order')
|
self.byte_order = yaml.get('byte-order')
|
||||||
self.len = yaml.get('len')
|
self.len = yaml.get('len')
|
||||||
self.display_hint = yaml.get('display-hint')
|
self.display_hint = yaml.get('display-hint')
|
||||||
|
self.sub_message = yaml.get('sub-message')
|
||||||
|
self.selector = yaml.get('selector')
|
||||||
|
|
||||||
self.is_auto_scalar = self.type == "sint" or self.type == "uint"
|
self.is_auto_scalar = self.type == "sint" or self.type == "uint"
|
||||||
|
|
||||||
|
|
@ -278,6 +283,47 @@ class SpecStruct(SpecElement):
|
||||||
return self.members.items()
|
return self.members.items()
|
||||||
|
|
||||||
|
|
||||||
|
class SpecSubMessage(SpecElement):
|
||||||
|
""" Netlink sub-message definition
|
||||||
|
|
||||||
|
Represents a set of sub-message formats for polymorphic nlattrs
|
||||||
|
that contain type-specific sub messages.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name string, name of sub-message definition
|
||||||
|
formats dict of sub-message formats indexed by match value
|
||||||
|
"""
|
||||||
|
def __init__(self, family, yaml):
|
||||||
|
super().__init__(family, yaml)
|
||||||
|
|
||||||
|
self.formats = collections.OrderedDict()
|
||||||
|
for elem in self.yaml['formats']:
|
||||||
|
format = self.new_format(family, elem)
|
||||||
|
self.formats[format.value] = format
|
||||||
|
|
||||||
|
def new_format(self, family, format):
|
||||||
|
return SpecSubMessageFormat(family, format)
|
||||||
|
|
||||||
|
|
||||||
|
class SpecSubMessageFormat(SpecElement):
|
||||||
|
""" Netlink sub-message definition
|
||||||
|
|
||||||
|
Represents a set of sub-message formats for polymorphic nlattrs
|
||||||
|
that contain type-specific sub messages.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
value attribute value to match against type selector
|
||||||
|
fixed_header string, name of fixed header, or None
|
||||||
|
attr_set string, name of attribute set, or None
|
||||||
|
"""
|
||||||
|
def __init__(self, family, yaml):
|
||||||
|
super().__init__(family, yaml)
|
||||||
|
|
||||||
|
self.value = yaml.get('value')
|
||||||
|
self.fixed_header = yaml.get('fixed-header')
|
||||||
|
self.attr_set = yaml.get('attribute-set')
|
||||||
|
|
||||||
|
|
||||||
class SpecOperation(SpecElement):
|
class SpecOperation(SpecElement):
|
||||||
"""Netlink Operation
|
"""Netlink Operation
|
||||||
|
|
||||||
|
|
@ -365,6 +411,7 @@ class SpecFamily(SpecElement):
|
||||||
|
|
||||||
attr_sets dict of attribute sets
|
attr_sets dict of attribute sets
|
||||||
msgs dict of all messages (index by name)
|
msgs dict of all messages (index by name)
|
||||||
|
sub_msgs dict of all sub messages (index by name)
|
||||||
ops dict of all valid requests / responses
|
ops dict of all valid requests / responses
|
||||||
ntfs dict of all async events
|
ntfs dict of all async events
|
||||||
consts dict of all constants/enums
|
consts dict of all constants/enums
|
||||||
|
|
@ -405,6 +452,7 @@ class SpecFamily(SpecElement):
|
||||||
jsonschema.validate(self.yaml, schema)
|
jsonschema.validate(self.yaml, schema)
|
||||||
|
|
||||||
self.attr_sets = collections.OrderedDict()
|
self.attr_sets = collections.OrderedDict()
|
||||||
|
self.sub_msgs = collections.OrderedDict()
|
||||||
self.msgs = collections.OrderedDict()
|
self.msgs = collections.OrderedDict()
|
||||||
self.req_by_value = collections.OrderedDict()
|
self.req_by_value = collections.OrderedDict()
|
||||||
self.rsp_by_value = collections.OrderedDict()
|
self.rsp_by_value = collections.OrderedDict()
|
||||||
|
|
@ -441,6 +489,9 @@ class SpecFamily(SpecElement):
|
||||||
def new_struct(self, elem):
|
def new_struct(self, elem):
|
||||||
return SpecStruct(self, elem)
|
return SpecStruct(self, elem)
|
||||||
|
|
||||||
|
def new_sub_message(self, elem):
|
||||||
|
return SpecSubMessage(self, elem);
|
||||||
|
|
||||||
def new_operation(self, elem, req_val, rsp_val):
|
def new_operation(self, elem, req_val, rsp_val):
|
||||||
return SpecOperation(self, elem, req_val, rsp_val)
|
return SpecOperation(self, elem, req_val, rsp_val)
|
||||||
|
|
||||||
|
|
@ -529,6 +580,10 @@ class SpecFamily(SpecElement):
|
||||||
attr_set = self.new_attr_set(elem)
|
attr_set = self.new_attr_set(elem)
|
||||||
self.attr_sets[elem['name']] = attr_set
|
self.attr_sets[elem['name']] = attr_set
|
||||||
|
|
||||||
|
for elem in self.yaml.get('sub-messages', []):
|
||||||
|
sub_message = self.new_sub_message(elem)
|
||||||
|
self.sub_msgs[sub_message.name] = sub_message
|
||||||
|
|
||||||
if self.msg_id_model == 'unified':
|
if self.msg_id_model == 'unified':
|
||||||
self._dictify_ops_unified()
|
self._dictify_ops_unified()
|
||||||
elif self.msg_id_model == 'directional':
|
elif self.msg_id_model == 'directional':
|
||||||
|
|
|
||||||
|
|
@ -170,10 +170,9 @@ class NlAttr:
|
||||||
|
|
||||||
|
|
||||||
class NlAttrs:
|
class NlAttrs:
|
||||||
def __init__(self, msg):
|
def __init__(self, msg, offset=0):
|
||||||
self.attrs = []
|
self.attrs = []
|
||||||
|
|
||||||
offset = 0
|
|
||||||
while offset < len(msg):
|
while offset < len(msg):
|
||||||
attr = NlAttr(msg, offset)
|
attr = NlAttr(msg, offset)
|
||||||
offset += attr.full_len
|
offset += attr.full_len
|
||||||
|
|
@ -371,8 +370,8 @@ class NetlinkProtocol:
|
||||||
fixed_header_size = 0
|
fixed_header_size = 0
|
||||||
if ynl:
|
if ynl:
|
||||||
op = ynl.rsp_by_value[msg.cmd()]
|
op = ynl.rsp_by_value[msg.cmd()]
|
||||||
fixed_header_size = ynl._fixed_header_size(op)
|
fixed_header_size = ynl._fixed_header_size(op.fixed_header)
|
||||||
msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:])
|
msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def get_mcast_id(self, mcast_name, mcast_groups):
|
def get_mcast_id(self, mcast_name, mcast_groups):
|
||||||
|
|
@ -549,6 +548,37 @@ class YnlFamily(SpecFamily):
|
||||||
else:
|
else:
|
||||||
rsp[name] = [decoded]
|
rsp[name] = [decoded]
|
||||||
|
|
||||||
|
def _resolve_selector(self, attr_spec, vals):
|
||||||
|
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]
|
||||||
|
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)
|
||||||
|
decoded = {}
|
||||||
|
offset = 0
|
||||||
|
if msg_format.fixed_header:
|
||||||
|
decoded.update(self._decode_fixed_header(attr, 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:
|
||||||
|
subdict = self._decode(NlAttrs(attr.raw, offset), msg_format.attr_set)
|
||||||
|
decoded.update(subdict)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown attribute-set '{attr_space}' when decoding '{attr_spec.name}'")
|
||||||
|
return decoded
|
||||||
|
|
||||||
def _decode(self, attrs, space):
|
def _decode(self, attrs, space):
|
||||||
if space:
|
if space:
|
||||||
attr_space = self.attr_sets[space]
|
attr_space = self.attr_sets[space]
|
||||||
|
|
@ -586,6 +616,8 @@ class YnlFamily(SpecFamily):
|
||||||
value = self._decode_enum(value, attr_spec)
|
value = self._decode_enum(value, attr_spec)
|
||||||
selector = self._decode_enum(selector, attr_spec)
|
selector = self._decode_enum(selector, attr_spec)
|
||||||
decoded = {"value": value, "selector": selector}
|
decoded = {"value": value, "selector": selector}
|
||||||
|
elif attr_spec["type"] == 'sub-message':
|
||||||
|
decoded = self._decode_sub_msg(attr, attr_spec, rsp)
|
||||||
else:
|
else:
|
||||||
if not self.process_unknown:
|
if not self.process_unknown:
|
||||||
raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
|
raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
|
||||||
|
|
@ -626,16 +658,16 @@ class YnlFamily(SpecFamily):
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set))
|
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set))
|
||||||
offset = 20 + self._fixed_header_size(op)
|
offset = 20 + self._fixed_header_size(op.fixed_header)
|
||||||
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
|
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
|
||||||
extack['bad-attr-offs'])
|
extack['bad-attr-offs'])
|
||||||
if path:
|
if path:
|
||||||
del extack['bad-attr-offs']
|
del extack['bad-attr-offs']
|
||||||
extack['bad-attr'] = path
|
extack['bad-attr'] = path
|
||||||
|
|
||||||
def _fixed_header_size(self, op):
|
def _fixed_header_size(self, name):
|
||||||
if op.fixed_header:
|
if name:
|
||||||
fixed_header_members = self.consts[op.fixed_header].members
|
fixed_header_members = self.consts[name].members
|
||||||
size = 0
|
size = 0
|
||||||
for m in fixed_header_members:
|
for m in fixed_header_members:
|
||||||
format = NlAttr.get_format(m.type, m.byte_order)
|
format = NlAttr.get_format(m.type, m.byte_order)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user