diff mbox series

[net-next,v2,05/10] tools/net/ynl: Refactor decode_fixed_header into NlMsg

Message ID 20230815194254.89570-6-donald.hunter@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series tools/net/ynl: Add support for netlink-raw families | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/apply fail Patch does not apply to net-next

Commit Message

Donald Hunter Aug. 15, 2023, 7:42 p.m. UTC
Move decode_fixed_header into NlMsg in preparation for adding
netlink-raw support.

Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
---
 tools/net/ynl/lib/ynl.py | 39 ++++++++++++++++++++++++---------------
 1 file changed, 24 insertions(+), 15 deletions(-)

Comments

Jakub Kicinski Aug. 16, 2023, 3:20 p.m. UTC | #1
On Tue, 15 Aug 2023 20:42:49 +0100 Donald Hunter wrote:
> +    def __init__(self, nl_msg, ynl=None):
> +        self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0)
> +        nl_msg.raw = nl_msg.raw[4:]

It's a bit of a layering violation that we are futzing with the raw
member of NlMsg inside GenlMsg, no?

Should we add "fixed hdrs len" argument to NlMsg? Either directly or
pass ynl and let get the expected len from ynl? That way NlMsg can
split itself into hdr, userhdrs and attrs without GenlMsg "fixing it
up"?

> -        self.hdr = nl_msg.raw[0: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 = 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]
> +        if ynl:
> +            op = ynl.rsp_by_value[self.genl_cmd]
> +            if op.fixed_header:
> +                nl_msg.decode_fixed_header(ynl, op.fixed_header)
>  
> -        self.raw = nl_msg.raw[offset:]
> +        self.raw = nl_msg.raw
Donald Hunter Aug. 17, 2023, 3:14 p.m. UTC | #2
On Wed, 16 Aug 2023 at 16:20, Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 15 Aug 2023 20:42:49 +0100 Donald Hunter wrote:
> > +    def __init__(self, nl_msg, ynl=None):
> > +        self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0)
> > +        nl_msg.raw = nl_msg.raw[4:]
>
> It's a bit of a layering violation that we are futzing with the raw
> member of NlMsg inside GenlMsg, no?
>
> Should we add "fixed hdrs len" argument to NlMsg? Either directly or
> pass ynl and let get the expected len from ynl? That way NlMsg can
> split itself into hdr, userhdrs and attrs without GenlMsg "fixing it
> up"?

I agree, it breaks the layering. The issue is that GenlMsg gets created at
some point after NlMsg, only when we know the nl_msg is suitable for
decoding. The fixed header bit is quite well encapsulated in NlMsg,
it's the genl header that needs pulled out and NlMsg shouldn't know
anything about it. How about I add a take_bytes(length) method or a
generic decode_subheader(format, length) method to NlMsg?
Jakub Kicinski Aug. 18, 2023, 1:37 a.m. UTC | #3
On Thu, 17 Aug 2023 16:14:35 +0100 Donald Hunter wrote:
> > It's a bit of a layering violation that we are futzing with the raw
> > member of NlMsg inside GenlMsg, no?
> >
> > Should we add "fixed hdrs len" argument to NlMsg? Either directly or
> > pass ynl and let get the expected len from ynl? That way NlMsg can
> > split itself into hdr, userhdrs and attrs without GenlMsg "fixing it
> > up"?  
> 
> I agree, it breaks the layering. The issue is that GenlMsg gets created at
> some point after NlMsg, only when we know the nl_msg is suitable for
> decoding. The fixed header bit is quite well encapsulated in NlMsg,
> it's the genl header that needs pulled out and NlMsg shouldn't know
> anything about it. How about I add a take_bytes(length) method or a
> generic decode_subheader(format, length) method to NlMsg?

Why do we need to fix up the .raw of NlMsg underlying the GenlMsg
in the first place? GenlMsg by itself didn't need to do that until now.

Another option to consider which would make things more symmetric
between raw and genetlink would be to add a wrapper class for old
families, too. ClassicMsg? CnlMsg? That way we could retain the
separation of NlMsg is just a raw message which could be a NLM_DONE or
some other control thing, and higher level class being used to pull
fixed headers and separate out attrs. Just a thought, not sure it helps.
Donald Hunter Aug. 18, 2023, 10:21 a.m. UTC | #4
On Fri, 18 Aug 2023 at 02:37, Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 17 Aug 2023 16:14:35 +0100 Donald Hunter wrote:
> > > It's a bit of a layering violation that we are futzing with the raw
> > > member of NlMsg inside GenlMsg, no?
> > >
> > > Should we add "fixed hdrs len" argument to NlMsg? Either directly or
> > > pass ynl and let get the expected len from ynl? That way NlMsg can
> > > split itself into hdr, userhdrs and attrs without GenlMsg "fixing it
> > > up"?
> >
> > I agree, it breaks the layering. The issue is that GenlMsg gets created at
> > some point after NlMsg, only when we know the nl_msg is suitable for
> > decoding. The fixed header bit is quite well encapsulated in NlMsg,
> > it's the genl header that needs pulled out and NlMsg shouldn't know
> > anything about it. How about I add a take_bytes(length) method or a
> > generic decode_subheader(format, length) method to NlMsg?
>
> Why do we need to fix up the .raw of NlMsg underlying the GenlMsg
> in the first place? GenlMsg by itself didn't need to do that until now.

Fair point. I will refactor to leave nl_msg.raw untouched.

> Another option to consider which would make things more symmetric
> between raw and genetlink would be to add a wrapper class for old
> families, too. ClassicMsg? CnlMsg? That way we could retain the
> separation of NlMsg is just a raw message which could be a NLM_DONE or
> some other control thing, and higher level class being used to pull
> fixed headers and separate out attrs. Just a thought, not sure it helps.

I _think_ I can avoid doing this. There's an asymmetry to the way
the NlAttrs get created that I need to fix. When I do that, the rest
should be a bit cleaner.
diff mbox series

Patch

diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 3ca28d4bcb18..4fa42a7c5955 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -189,6 +189,7 @@  class NlMsg:
 
         self.error = 0
         self.done = 0
+        self.fixed_header_attrs = []
 
         extack_off = None
         if self.nl_type == Netlink.NLMSG_ERROR:
@@ -228,6 +229,19 @@  class NlMsg:
                             desc += f" ({spec['doc']})"
                         self.extack['miss-type'] = desc
 
+    def decode_fixed_header(self, ynl, name):
+        fixed_header_members = ynl.consts[name].members
+        self.fixed_header_attrs = dict()
+        offset = 0
+        for m in fixed_header_members:
+            format = NlAttr.get_format(m.type, m.byte_order)
+            [ value ] = format.unpack_from(self.raw, offset)
+            offset += format.size
+            if m.enum:
+                value = ynl._decode_enum(value, m)
+            self.fixed_header_attrs[m.name] = value
+        self.raw = self.raw[offset:]
+
     def __repr__(self):
         msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n"
         if self.error:
@@ -317,23 +331,18 @@  def _genl_load_families():
 
 
 class GenlMsg:
-    def __init__(self, nl_msg, fixed_header_members=[]):
-        self.nl = nl_msg
+    def __init__(self, nl_msg, ynl=None):
+        self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0)
+        nl_msg.raw = nl_msg.raw[4:]
 
-        self.hdr = nl_msg.raw[0: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 = 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]
+        if ynl:
+            op = ynl.rsp_by_value[self.genl_cmd]
+            if op.fixed_header:
+                nl_msg.decode_fixed_header(ynl, op.fixed_header)
 
-        self.raw = nl_msg.raw[offset:]
+        self.raw = nl_msg.raw
         self.raw_attrs = NlAttrs(self.raw)
+        self.fixed_header_attrs = nl_msg.fixed_header_attrs
 
     def __repr__(self):
         msg = repr(self.nl)
@@ -596,7 +605,7 @@  class YnlFamily(SpecFamily):
                     done = True
                     break
 
-                gm = GenlMsg(nl_msg, fixed_header_members)
+                gm = GenlMsg(nl_msg, self)
                 # 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: