Commit 1769e2be authored by Donald Hunter's avatar Donald Hunter Committed by Jakub Kicinski

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: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDonald Hunter <donald.hunter@gmail.com>
Link: https://lore.kernel.org/r/20231215093720.18774-5-donald.hunter@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 17ed5c1a
...@@ -158,6 +158,9 @@ class SpecAttr(SpecElement): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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: ...@@ -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: ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment