tools: ynl: Add fixed-header support to ynl
Add support for netlink families that add an optional fixed header structure after the genetlink header and before any attributes. The fixed-header can be specified on a per op basis, or once for all operations, which serves as a default value that can be overridden. Signed-off-by: Donald Hunter <donald.hunter@gmail.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
2607191395
commit
f036d936ca
@ -261,6 +261,14 @@ properties:
|
|||||||
async-enum:
|
async-enum:
|
||||||
description: Name for the enum type with notifications/events.
|
description: Name for the enum type with notifications/events.
|
||||||
type: string
|
type: string
|
||||||
|
# Start genetlink-legacy
|
||||||
|
fixed-header: &fixed-header
|
||||||
|
description: |
|
||||||
|
Name of the structure defining the optional fixed-length protocol
|
||||||
|
header. This header is placed in a message after the netlink and
|
||||||
|
genetlink headers and before any attributes.
|
||||||
|
type: string
|
||||||
|
# End genetlink-legacy
|
||||||
list:
|
list:
|
||||||
description: List of commands
|
description: List of commands
|
||||||
type: array
|
type: array
|
||||||
@ -293,6 +301,9 @@ properties:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
enum: [ strict, dump ]
|
enum: [ strict, dump ]
|
||||||
|
# Start genetlink-legacy
|
||||||
|
fixed-header: *fixed-header
|
||||||
|
# End genetlink-legacy
|
||||||
do: &subop-type
|
do: &subop-type
|
||||||
description: Main command handler.
|
description: Main command handler.
|
||||||
type: object
|
type: object
|
||||||
|
@ -263,16 +263,17 @@ class SpecOperation(SpecElement):
|
|||||||
Information about a single Netlink operation.
|
Information about a single Netlink operation.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
value numerical ID when serialized, None if req/rsp values differ
|
value numerical ID when serialized, None if req/rsp values differ
|
||||||
|
|
||||||
req_value numerical ID when serialized, user -> kernel
|
req_value numerical ID when serialized, user -> kernel
|
||||||
rsp_value numerical ID when serialized, user <- kernel
|
rsp_value numerical ID when serialized, user <- kernel
|
||||||
is_call bool, whether the operation is a call
|
is_call bool, whether the operation is a call
|
||||||
is_async bool, whether the operation is a notification
|
is_async bool, whether the operation is a notification
|
||||||
is_resv bool, whether the operation does not exist (it's just a reserved ID)
|
is_resv bool, whether the operation does not exist (it's just a reserved ID)
|
||||||
attr_set attribute set name
|
attr_set attribute set name
|
||||||
|
fixed_header string, optional name of fixed header struct
|
||||||
|
|
||||||
yaml raw spec as loaded from the spec file
|
yaml raw spec as loaded from the spec file
|
||||||
"""
|
"""
|
||||||
def __init__(self, family, yaml, req_value, rsp_value):
|
def __init__(self, family, yaml, req_value, rsp_value):
|
||||||
super().__init__(family, yaml)
|
super().__init__(family, yaml)
|
||||||
@ -284,6 +285,7 @@ class SpecOperation(SpecElement):
|
|||||||
self.is_call = 'do' in yaml or 'dump' in yaml
|
self.is_call = 'do' in yaml or 'dump' in yaml
|
||||||
self.is_async = 'notify' in yaml or 'event' in yaml
|
self.is_async = 'notify' in yaml or 'event' in yaml
|
||||||
self.is_resv = not self.is_async and not self.is_call
|
self.is_resv = not self.is_async and not self.is_call
|
||||||
|
self.fixed_header = self.yaml.get('fixed-header', family.fixed_header)
|
||||||
|
|
||||||
# Added by resolve:
|
# Added by resolve:
|
||||||
self.attr_set = None
|
self.attr_set = None
|
||||||
@ -324,6 +326,7 @@ class SpecFamily(SpecElement):
|
|||||||
msgs_by_value dict of all messages (indexed by name)
|
msgs_by_value dict of all messages (indexed by name)
|
||||||
ops dict of all valid requests / responses
|
ops dict of all valid requests / responses
|
||||||
consts dict of all constants/enums
|
consts dict of all constants/enums
|
||||||
|
fixed_header string, optional name of family default fixed header struct
|
||||||
"""
|
"""
|
||||||
def __init__(self, spec_path, schema_path=None):
|
def __init__(self, spec_path, schema_path=None):
|
||||||
with open(spec_path, "r") as stream:
|
with open(spec_path, "r") as stream:
|
||||||
@ -397,6 +400,7 @@ class SpecFamily(SpecElement):
|
|||||||
self._resolution_list.append(elem)
|
self._resolution_list.append(elem)
|
||||||
|
|
||||||
def _dictify_ops_unified(self):
|
def _dictify_ops_unified(self):
|
||||||
|
self.fixed_header = self.yaml['operations'].get('fixed-header')
|
||||||
val = 1
|
val = 1
|
||||||
for elem in self.yaml['operations']['list']:
|
for elem in self.yaml['operations']['list']:
|
||||||
if 'value' in elem:
|
if 'value' in elem:
|
||||||
@ -408,6 +412,7 @@ class SpecFamily(SpecElement):
|
|||||||
self.msgs[op.name] = op
|
self.msgs[op.name] = op
|
||||||
|
|
||||||
def _dictify_ops_directional(self):
|
def _dictify_ops_directional(self):
|
||||||
|
self.fixed_header = self.yaml['operations'].get('fixed-header')
|
||||||
req_val = rsp_val = 1
|
req_val = rsp_val = 1
|
||||||
for elem in self.yaml['operations']['list']:
|
for elem in self.yaml['operations']['list']:
|
||||||
if 'notify' in elem:
|
if 'notify' in elem:
|
||||||
|
@ -278,14 +278,22 @@ def _genl_load_families():
|
|||||||
|
|
||||||
|
|
||||||
class GenlMsg:
|
class GenlMsg:
|
||||||
def __init__(self, nl_msg):
|
def __init__(self, nl_msg, fixed_header_members=[]):
|
||||||
self.nl = nl_msg
|
self.nl = nl_msg
|
||||||
|
|
||||||
self.hdr = nl_msg.raw[0:4]
|
self.hdr = nl_msg.raw[0:4]
|
||||||
self.raw = nl_msg.raw[4:]
|
offset = 4
|
||||||
|
|
||||||
self.genl_cmd, self.genl_version, _ = struct.unpack("BBH", self.hdr)
|
self.genl_cmd, self.genl_version, _ = struct.unpack("BBH", self.hdr)
|
||||||
|
|
||||||
|
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
|
||||||
|
self.fixed_header_attrs[m.name] = decoded[0]
|
||||||
|
|
||||||
|
self.raw = nl_msg.raw[offset:]
|
||||||
self.raw_attrs = NlAttrs(self.raw)
|
self.raw_attrs = NlAttrs(self.raw)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -509,6 +517,13 @@ class YnlFamily(SpecFamily):
|
|||||||
|
|
||||||
req_seq = random.randint(1024, 65535)
|
req_seq = random.randint(1024, 65535)
|
||||||
msg = _genl_msg(self.family.family_id, nl_flags, op.req_value, 1, req_seq)
|
msg = _genl_msg(self.family.family_id, 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)
|
||||||
|
format, _ = NlAttr.type_formats[m.type]
|
||||||
|
msg += struct.pack(format, value)
|
||||||
for name, value in vals.items():
|
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)
|
||||||
msg = _genl_msg_finalize(msg)
|
msg = _genl_msg_finalize(msg)
|
||||||
@ -535,7 +550,7 @@ class YnlFamily(SpecFamily):
|
|||||||
done = True
|
done = True
|
||||||
break
|
break
|
||||||
|
|
||||||
gm = GenlMsg(nl_msg)
|
gm = GenlMsg(nl_msg, fixed_header_members)
|
||||||
# Check if this is a reply to our request
|
# Check if this is a reply to our request
|
||||||
if nl_msg.nl_seq != req_seq or gm.genl_cmd != op.rsp_value:
|
if nl_msg.nl_seq != req_seq or gm.genl_cmd != op.rsp_value:
|
||||||
if gm.genl_cmd in self.async_msg_ids:
|
if gm.genl_cmd in self.async_msg_ids:
|
||||||
@ -545,7 +560,8 @@ class YnlFamily(SpecFamily):
|
|||||||
print('Unexpected message: ' + repr(gm))
|
print('Unexpected message: ' + repr(gm))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
rsp.append(self._decode(gm.raw_attrs, op.attr_set.name))
|
rsp.append(self._decode(gm.raw_attrs, op.attr_set.name)
|
||||||
|
| gm.fixed_header_attrs)
|
||||||
|
|
||||||
if not rsp:
|
if not rsp:
|
||||||
return None
|
return None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user