Skip to content

Commit

Permalink
tools: ynl: Add fixed-header support to ynl
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
donaldh authored and kuba-moo committed Mar 29, 2023
1 parent 2607191 commit f036d93
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 12 deletions.
11 changes: 11 additions & 0 deletions Documentation/netlink/genetlink-legacy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ properties:
async-enum:
description: Name for the enum type with notifications/events.
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:
description: List of commands
type: array
Expand Down Expand Up @@ -293,6 +301,9 @@ properties:
type: array
items:
enum: [ strict, dump ]
# Start genetlink-legacy
fixed-header: *fixed-header
# End genetlink-legacy
do: &subop-type
description: Main command handler.
type: object
Expand Down
21 changes: 13 additions & 8 deletions tools/net/ynl/lib/nlspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,17 @@ class SpecOperation(SpecElement):
Information about a single Netlink operation.
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
rsp_value numerical ID when serialized, user <- kernel
is_call bool, whether the operation is a call
is_async bool, whether the operation is a notification
is_resv bool, whether the operation does not exist (it's just a reserved ID)
attr_set attribute set name
req_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_async bool, whether the operation is a notification
is_resv bool, whether the operation does not exist (it's just a reserved ID)
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):
super().__init__(family, yaml)
Expand All @@ -284,6 +285,7 @@ def __init__(self, family, yaml, req_value, rsp_value):
self.is_call = 'do' in yaml or 'dump' 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.fixed_header = self.yaml.get('fixed-header', family.fixed_header)

# Added by resolve:
self.attr_set = None
Expand Down Expand Up @@ -324,6 +326,7 @@ class SpecFamily(SpecElement):
msgs_by_value dict of all messages (indexed by name)
ops dict of all valid requests / responses
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):
with open(spec_path, "r") as stream:
Expand Down Expand Up @@ -397,6 +400,7 @@ def add_unresolved(self, elem):
self._resolution_list.append(elem)

def _dictify_ops_unified(self):
self.fixed_header = self.yaml['operations'].get('fixed-header')
val = 1
for elem in self.yaml['operations']['list']:
if 'value' in elem:
Expand All @@ -408,6 +412,7 @@ def _dictify_ops_unified(self):
self.msgs[op.name] = op

def _dictify_ops_directional(self):
self.fixed_header = self.yaml['operations'].get('fixed-header')
req_val = rsp_val = 1
for elem in self.yaml['operations']['list']:
if 'notify' in elem:
Expand Down
24 changes: 20 additions & 4 deletions tools/net/ynl/lib/ynl.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,14 +278,22 @@ def _genl_load_families():


class GenlMsg:
def __init__(self, nl_msg):
def __init__(self, nl_msg, fixed_header_members=[]):
self.nl = nl_msg

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.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)

def __repr__(self):
Expand Down Expand Up @@ -509,6 +517,13 @@ def _op(self, method, vals, dump=False):

req_seq = random.randint(1024, 65535)
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():
msg += self._add_attr(op.attr_set.name, name, value)
msg = _genl_msg_finalize(msg)
Expand All @@ -535,7 +550,7 @@ def _op(self, method, vals, dump=False):
done = True
break

gm = GenlMsg(nl_msg)
gm = GenlMsg(nl_msg, fixed_header_members)
# Check if this is a reply to our request
if nl_msg.nl_seq != req_seq or gm.genl_cmd != op.rsp_value:
if gm.genl_cmd in self.async_msg_ids:
Expand All @@ -545,7 +560,8 @@ def _op(self, method, vals, dump=False):
print('Unexpected message: ' + repr(gm))
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:
return None
Expand Down

0 comments on commit f036d93

Please sign in to comment.