netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link
@ 2025-05-15 23:16 Jakub Kicinski
  2025-05-15 23:16 ` [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn Jakub Kicinski
                   ` (9 more replies)
  0 siblings, 10 replies; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Sub-messages are how we express "polymorphism" in YNL. Donald added
the support to specs and Python a while back, support them in C, too.
Sub-message is a nest, but the interpretation of the attribute types
within that nest depends on a value of another attribute. For example
in rt-link the "kind" attribute contains the link type (veth, bonding,
etc.) and based on that the right enum has to be applied to interpret
link-specific attributes.

The last message is probably the most interesting to look at, as it
adds a fairly advanced sample.

This patch only contains enough support for rtnetlink, we will need
a little more complexity to support TC, where sub-messages may contain
fixed headers, and where the selector may be in a different nest than
the submessage.

Jakub Kicinski (9):
  netlink: specs: rt-link: add C naming info for ovpn
  tools: ynl-gen: factor out the annotation of pure nested struct
  tools: ynl-gen: prepare for submsg structs
  tools: ynl-gen: submsg: plumb thru an empty type
  tools: ynl-gen: submsg: render the structs
  tools: ynl-gen: submsg: support parsing and rendering sub-messages
  tools: ynl: submsg: reverse parse / error reporting
  tools: ynl: enable codegen for all rt- families
  tools: ynl: add a sample for rt-link

 Documentation/netlink/specs/rt-link.yaml |   4 +
 tools/net/ynl/Makefile.deps              |   4 +
 tools/net/ynl/generated/Makefile         |   7 +-
 tools/net/ynl/lib/ynl-priv.h             |   8 +-
 tools/net/ynl/lib/ynl.h                  |   1 +
 tools/net/ynl/lib/ynl.c                  |  93 +++++++-
 tools/net/ynl/samples/rt-link.c          | 184 +++++++++++++++
 tools/net/ynl/pyynl/lib/__init__.py      |   5 +-
 tools/net/ynl/pyynl/ynl_gen_c.py         | 272 +++++++++++++++++++----
 tools/net/ynl/samples/.gitignore         |   1 +
 10 files changed, 517 insertions(+), 62 deletions(-)
 create mode 100644 tools/net/ynl/samples/rt-link.c

-- 
2.49.0


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16  9:08   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct Jakub Kicinski
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

C naming info for OVPN which was added since I adjusted
the existing attrs. Also add missing reference to a header needed
for a bridge struct.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 Documentation/netlink/specs/rt-link.yaml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index 7f91f474ff25..5ec3d35b7a38 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -594,6 +594,7 @@ protonum: 0
         name: reasm-overlaps
   - name: br-boolopt-multi
     type: struct
+    header: linux/if_bridge.h
     members:
       -
         name: optval
@@ -826,6 +827,8 @@ protonum: 0
       - name: default
   -
     name: ovpn-mode
+    enum-name: ovpn-mode
+    name-prefix: ovpn-mode
     type: enum
     entries:
       - p2p
@@ -2195,6 +2198,7 @@ protonum: 0
         type: u16
   -
     name: linkinfo-ovpn-attrs
+    name-prefix: ifla-ovpn-
     attributes:
       -
         name: mode
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
  2025-05-15 23:16 ` [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16  9:57   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs Jakub Kicinski
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

We're about to add some code here for sub-messages.
Factor out the nest-related logic to make the code readable.
No functional change.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/pyynl/ynl_gen_c.py | 39 ++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 9e143520e2f7..84140ce3a48d 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -1239,6 +1239,25 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
             else:
                 pns_key_list.append(name)
 
+    def _load_nested_set_nest(self, spec):
+        inherit = set()
+        nested = spec['nested-attributes']
+        if nested not in self.root_sets:
+            if nested not in self.pure_nested_structs:
+                self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
+        else:
+            raise Exception(f'Using attr set as root and nested not supported - {nested}')
+
+        if 'type-value' in spec:
+            if nested in self.root_sets:
+                raise Exception("Inheriting members to a space used as root not supported")
+            inherit.update(set(spec['type-value']))
+        elif spec['type'] == 'indexed-array':
+            inherit.add('idx')
+        self.pure_nested_structs[nested].set_inherited(inherit)
+
+        return nested
+
     def _load_nested_sets(self):
         attr_set_queue = list(self.root_sets.keys())
         attr_set_seen = set(self.root_sets.keys())
@@ -1246,29 +1265,15 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
         while len(attr_set_queue):
             a_set = attr_set_queue.pop(0)
             for attr, spec in self.attr_sets[a_set].items():
-                if 'nested-attributes' not in spec:
+                if 'nested-attributes' in spec:
+                    nested = self._load_nested_set_nest(spec)
+                else:
                     continue
 
-                nested = spec['nested-attributes']
                 if nested not in attr_set_seen:
                     attr_set_queue.append(nested)
                     attr_set_seen.add(nested)
 
-                inherit = set()
-                if nested not in self.root_sets:
-                    if nested not in self.pure_nested_structs:
-                        self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
-                else:
-                    raise Exception(f'Using attr set as root and nested not supported - {nested}')
-
-                if 'type-value' in spec:
-                    if nested in self.root_sets:
-                        raise Exception("Inheriting members to a space used as root not supported")
-                    inherit.update(set(spec['type-value']))
-                elif spec['type'] == 'indexed-array':
-                    inherit.add('idx')
-                self.pure_nested_structs[nested].set_inherited(inherit)
-
         for root_set, rs_members in self.root_sets.items():
             for attr, spec in self.attr_sets[root_set].items():
                 if 'nested-attributes' in spec:
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
  2025-05-15 23:16 ` [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn Jakub Kicinski
  2025-05-15 23:16 ` [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 10:15   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type Jakub Kicinski
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Prepare for constructing Struct() instances which represent
sub-messages rather than nested attributes.
Restructure the code / indentation to more easily insert
a case where nested reference comes from annotation other
than the 'nested-attributes' property. Make sure we don't
construct the Struct() object from scratch in multiple
places as the constructor will soon have more arguments.

This should cause no functional change.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/pyynl/ynl_gen_c.py | 62 ++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 23 deletions(-)

diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 84140ce3a48d..c8b2a2ab2e5d 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -60,7 +60,12 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
             self.len = attr['len']
 
         if 'nested-attributes' in attr:
-            self.nested_attrs = attr['nested-attributes']
+            nested = attr['nested-attributes']
+        else:
+            nested = None
+
+        if nested:
+            self.nested_attrs = nested
             if self.nested_attrs == family.name:
                 self.nested_render_name = c_lower(f"{family.ident_name}")
             else:
@@ -1225,15 +1230,18 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
             for _, spec in self.attr_sets[name].items():
                 if 'nested-attributes' in spec:
                     nested = spec['nested-attributes']
-                    # If the unknown nest we hit is recursive it's fine, it'll be a pointer
-                    if self.pure_nested_structs[nested].recursive:
-                        continue
-                    if nested not in pns_key_seen:
-                        # Dicts are sorted, this will make struct last
-                        struct = self.pure_nested_structs.pop(name)
-                        self.pure_nested_structs[name] = struct
-                        finished = False
-                        break
+                else:
+                    continue
+
+                # If the unknown nest we hit is recursive it's fine, it'll be a pointer
+                if self.pure_nested_structs[nested].recursive:
+                    continue
+                if nested not in pns_key_seen:
+                    # Dicts are sorted, this will make struct last
+                    struct = self.pure_nested_structs.pop(name)
+                    self.pure_nested_structs[name] = struct
+                    finished = False
+                    break
             if finished:
                 pns_key_seen.add(name)
             else:
@@ -1278,10 +1286,15 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
             for attr, spec in self.attr_sets[root_set].items():
                 if 'nested-attributes' in spec:
                     nested = spec['nested-attributes']
+                else:
+                    nested = None
+
+                if nested:
                     if attr in rs_members['request']:
                         self.pure_nested_structs[nested].request = True
                     if attr in rs_members['reply']:
                         self.pure_nested_structs[nested].reply = True
+
                     if spec.is_multi_val():
                         child = self.pure_nested_structs.get(nested)
                         child.in_multi_val = True
@@ -1291,20 +1304,24 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
         # Propagate the request / reply / recursive
         for attr_set, struct in reversed(self.pure_nested_structs.items()):
             for _, spec in self.attr_sets[attr_set].items():
-                if 'nested-attributes' in spec:
-                    child_name = spec['nested-attributes']
-                    struct.child_nests.add(child_name)
-                    child = self.pure_nested_structs.get(child_name)
-                    if child:
-                        if not child.recursive:
-                            struct.child_nests.update(child.child_nests)
-                        child.request |= struct.request
-                        child.reply |= struct.reply
-                        if spec.is_multi_val():
-                            child.in_multi_val = True
                 if attr_set in struct.child_nests:
                     struct.recursive = True
 
+                if 'nested-attributes' in spec:
+                    child_name = spec['nested-attributes']
+                else:
+                    continue
+
+                struct.child_nests.add(child_name)
+                child = self.pure_nested_structs.get(child_name)
+                if child:
+                    if not child.recursive:
+                        struct.child_nests.update(child.child_nests)
+                    child.request |= struct.request
+                    child.reply |= struct.reply
+                    if spec.is_multi_val():
+                        child.in_multi_val = True
+
         self._sort_pure_types()
 
     def _load_attr_use(self):
@@ -3307,8 +3324,7 @@ _C_KW = {
                     has_recursive_nests = True
             if has_recursive_nests:
                 cw.nl()
-            for name in parsed.pure_nested_structs:
-                struct = Struct(parsed, name)
+            for struct in parsed.pure_nested_structs.values():
                 put_typol(cw, struct)
             for name in parsed.root_sets:
                 struct = Struct(parsed, name)
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (2 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 10:20   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs Jakub Kicinski
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Hook in handling of sub-messages, for now treat them as ignored attrs.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/pyynl/lib/__init__.py |  5 +++--
 tools/net/ynl/pyynl/ynl_gen_c.py    | 20 ++++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py
index 9137b83e580a..71518b9842ee 100644
--- a/tools/net/ynl/pyynl/lib/__init__.py
+++ b/tools/net/ynl/pyynl/lib/__init__.py
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
-    SpecFamily, SpecOperation
+    SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat
 from .ynl import YnlFamily, Netlink, NlError
 
 __all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
-           "SpecFamily", "SpecOperation", "YnlFamily", "Netlink", "NlError"]
+           "SpecFamily", "SpecOperation", "SpecSubMessage", "SpecSubMessageFormat",
+           "YnlFamily", "Netlink", "NlError"]
diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index c8b2a2ab2e5d..2292bbb68836 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -14,6 +14,7 @@ import yaml
 
 sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())
 from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
+from lib import SpecSubMessage, SpecSubMessageFormat
 
 
 def c_upper(name):
@@ -872,6 +873,10 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
         return get_lines, init_lines, local_vars
 
 
+class TypeSubMessage(TypeUnused):
+    pass
+
+
 class Struct:
     def __init__(self, family, space_name, type_list=None, inherited=None):
         self.family = family
@@ -1052,6 +1057,8 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
                 raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}')
         elif elem['type'] == 'nest-type-value':
             t = TypeNestTypeValue(self.family, self, elem, value)
+        elif elem['type'] == 'sub-message':
+            t = TypeSubMessage(self.family, self, elem, value)
         else:
             raise Exception(f"No typed class for type {elem['type']}")
 
@@ -1096,6 +1103,16 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
         self.has_ntf = True
 
 
+class SubMessage(SpecSubMessage):
+    def __init__(self, family, yaml):
+        super().__init__(family, yaml)
+
+        self.render_name = c_lower(family.ident_name + '-' + yaml['name'])
+
+    def resolve(self):
+        self.resolve_up(super())
+
+
 class Family(SpecFamily):
     def __init__(self, file_name, exclude_ops):
         # Added by resolve:
@@ -1178,6 +1195,9 @@ from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, S
     def new_operation(self, elem, req_value, rsp_value):
         return Operation(self, elem, req_value, rsp_value)
 
+    def new_sub_message(self, elem):
+        return SubMessage(self, elem)
+
     def is_classic(self):
         return self.proto == 'netlink-raw'
 
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (3 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 10:31   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages Jakub Kicinski
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

The easiest (or perhaps only sane) way to support submessages in C
is to treat them as if they were nests. Build fake attributes to
that effect in the codegen. Render the submsg as a big nest of all
possible values.

With this in place the main missing part is to hook in the switch
which selects how to parse based on the key.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/pyynl/ynl_gen_c.py | 46 +++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 2292bbb68836..020aa34b890b 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -62,6 +62,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
 
         if 'nested-attributes' in attr:
             nested = attr['nested-attributes']
+        elif 'sub-message' in attr:
+            nested = attr['sub-message']
         else:
             nested = None
 
@@ -125,7 +127,9 @@ from lib import SpecSubMessage, SpecSubMessageFormat
         return c_upper(value)
 
     def resolve(self):
-        if 'name-prefix' in self.attr:
+        if 'parent-sub-message' in self.attr:
+            enum_name = self.attr['parent-sub-message'].enum_name
+        elif 'name-prefix' in self.attr:
             enum_name = f"{self.attr['name-prefix']}{self.name}"
         else:
             enum_name = f"{self.attr_set.name_prefix}{self.name}"
@@ -873,18 +877,20 @@ from lib import SpecSubMessage, SpecSubMessageFormat
         return get_lines, init_lines, local_vars
 
 
-class TypeSubMessage(TypeUnused):
+class TypeSubMessage(TypeNest):
     pass
 
 
 class Struct:
-    def __init__(self, family, space_name, type_list=None, inherited=None):
+    def __init__(self, family, space_name, type_list=None,
+                 inherited=None, submsg=None):
         self.family = family
         self.space_name = space_name
         self.attr_set = family.attr_sets[space_name]
         # Use list to catch comparisons with empty sets
         self._inherited = inherited if inherited is not None else []
         self.inherited = []
+        self.submsg = submsg
 
         self.nested = type_list is None
         if family.name == c_lower(space_name):
@@ -1250,6 +1256,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
             for _, spec in self.attr_sets[name].items():
                 if 'nested-attributes' in spec:
                     nested = spec['nested-attributes']
+                elif 'sub-message' in spec:
+                    nested = spec.sub_message
                 else:
                     continue
 
@@ -1286,6 +1294,32 @@ from lib import SpecSubMessage, SpecSubMessageFormat
 
         return nested
 
+    def _load_nested_set_submsg(self, spec):
+        # Fake the struct type for the sub-message itself
+        # its not a attr_set but codegen wants attr_sets.
+        submsg = self.sub_msgs[spec["sub-message"]]
+        nested = submsg.name
+
+        attrs = []
+        for name, fmt in submsg.formats.items():
+            attrs.append({
+                "name": name,
+                "type": "nest",
+                "parent-sub-message": spec,
+                "nested-attributes": fmt['attribute-set']
+            })
+
+        self.attr_sets[nested] = AttrSet(self, {
+            "name": nested,
+            "name-pfx": self.name + '-' + spec.name + '-',
+            "attributes": attrs
+        })
+
+        if nested not in self.pure_nested_structs:
+            self.pure_nested_structs[nested] = Struct(self, nested, submsg=submsg)
+
+        return nested
+
     def _load_nested_sets(self):
         attr_set_queue = list(self.root_sets.keys())
         attr_set_seen = set(self.root_sets.keys())
@@ -1295,6 +1329,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
             for attr, spec in self.attr_sets[a_set].items():
                 if 'nested-attributes' in spec:
                     nested = self._load_nested_set_nest(spec)
+                elif 'sub-message' in spec:
+                    nested = self._load_nested_set_submsg(spec)
                 else:
                     continue
 
@@ -1306,6 +1342,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
             for attr, spec in self.attr_sets[root_set].items():
                 if 'nested-attributes' in spec:
                     nested = spec['nested-attributes']
+                elif 'sub-message' in spec:
+                    nested = spec.sub_message
                 else:
                     nested = None
 
@@ -1329,6 +1367,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
 
                 if 'nested-attributes' in spec:
                     child_name = spec['nested-attributes']
+                elif 'sub-message' in spec:
+                    child_name = spec.sub_message
                 else:
                     continue
 
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (4 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 10:43   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting Jakub Kicinski
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Adjust parsing and rendering appropriately to make sub-messages work.
Rendering is pretty trivial, as the submsg -> netlink conversion looks
like rendering a nest in which only one attr was set. Only trick
is that we use the enum value of the sub-message rather than the nest
as the type, and effectively skip one layer of nesting. A real double
nested struct would look like this:

  [SELECTOR]
  [SUBMSG]
    [NEST]
      [MSG1-ATTR]

A submsg "is" the nest so by skipping I mean:

  [SELECTOR]
  [SUBMSG]
    [MSG1-ATTR]

There is no extra validation in YNL if caller has set the selector
matching the submsg type (e.g. link type = "macvlan" but the nest
attrs are set to carry "veth"). Let the kernel handle that.

Parsing side is a little more specialized as we need to render and
insert a new kind of function which switches between what to parse
based on the selector. But code isn't too complicated.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/lib/ynl-priv.h     |  3 ++
 tools/net/ynl/lib/ynl.h          |  1 +
 tools/net/ynl/lib/ynl.c          |  9 ++++
 tools/net/ynl/pyynl/ynl_gen_c.py | 80 ++++++++++++++++++++++++++++++--
 4 files changed, 89 insertions(+), 4 deletions(-)

diff --git a/tools/net/ynl/lib/ynl-priv.h b/tools/net/ynl/lib/ynl-priv.h
index 5debb09491e7..fbc058dd1c3e 100644
--- a/tools/net/ynl/lib/ynl-priv.h
+++ b/tools/net/ynl/lib/ynl-priv.h
@@ -25,6 +25,7 @@ enum ynl_policy_type {
 	YNL_PT_UINT,
 	YNL_PT_NUL_STR,
 	YNL_PT_BITFIELD32,
+	YNL_PT_SUBMSG,
 };
 
 enum ynl_parse_result {
@@ -103,6 +104,8 @@ struct nlmsghdr *
 ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version);
 
 int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr);
+int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
+		      const char *sel_name);
 
 /* YNL specific helpers used by the auto-generated code */
 
diff --git a/tools/net/ynl/lib/ynl.h b/tools/net/ynl/lib/ynl.h
index 32efeb224829..db7c0591a63f 100644
--- a/tools/net/ynl/lib/ynl.h
+++ b/tools/net/ynl/lib/ynl.h
@@ -23,6 +23,7 @@ enum ynl_error_code {
 	YNL_ERROR_INV_RESP,
 	YNL_ERROR_INPUT_INVALID,
 	YNL_ERROR_INPUT_TOO_BIG,
+	YNL_ERROR_SUBMSG_KEY,
 };
 
 /**
diff --git a/tools/net/ynl/lib/ynl.c b/tools/net/ynl/lib/ynl.c
index a0b54ad4c073..25fc6501349b 100644
--- a/tools/net/ynl/lib/ynl.c
+++ b/tools/net/ynl/lib/ynl.c
@@ -384,6 +384,15 @@ int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
 	return 0;
 }
 
+int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
+		      const char *sel_name)
+{
+	yerr(yarg->ys, YNL_ERROR_SUBMSG_KEY,
+	     "Parsing error: Sub-message key not set (msg %s, key %s)",
+	     field_name, sel_name);
+	return YNL_PARSE_CB_ERROR;
+}
+
 /* Generic code */
 
 static void ynl_err_reset(struct ynl_sock *ys)
diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 020aa34b890b..b6b54d6fa906 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -878,7 +878,16 @@ from lib import SpecSubMessage, SpecSubMessageFormat
 
 
 class TypeSubMessage(TypeNest):
-    pass
+    def _attr_get(self, ri, var):
+        sel = c_lower(self['selector'])
+        get_lines = [f'if (!{var}->{sel})',
+                     f'return ynl_submsg_failed(yarg, "%s", "%s");' %
+                        (self.name, self['selector']),
+                    f"if ({self.nested_render_name}_parse(&parg, {var}->{sel}, attr))",
+                     "return YNL_PARSE_CB_ERROR;"]
+        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
+                      f"parg.data = &{var}->{self.c_name};"]
+        return get_lines, init_lines, None
 
 
 class Struct:
@@ -1818,11 +1827,34 @@ _C_KW = {
     print_prototype(ri, "request")
 
 
+def put_typol_submsg(cw, struct):
+    cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[] =')
+
+    i = 0
+    for name, arg in struct.member_list():
+        cw.p('[%d] = { .type = YNL_PT_SUBMSG, .name = "%s", .nest = &%s_nest, },' %
+             (i, name, arg.nested_render_name))
+        i += 1
+
+    cw.block_end(line=';')
+    cw.nl()
+
+    cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =')
+    cw.p(f'.max_attr = {i - 1},')
+    cw.p(f'.table = {struct.render_name}_policy,')
+    cw.block_end(line=';')
+    cw.nl()
+
+
 def put_typol_fwd(cw, struct):
     cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;')
 
 
 def put_typol(cw, struct):
+    if struct.submsg:
+        put_typol_submsg(cw, struct)
+        return
+
     type_max = struct.attr_set.max_name
     cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
 
@@ -1908,8 +1940,9 @@ _C_KW = {
     local_vars = []
     init_lines = []
 
-    local_vars.append('struct nlattr *nest;')
-    init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);")
+    if struct.submsg is None:
+        local_vars.append('struct nlattr *nest;')
+        init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);")
 
     has_anest = False
     has_count = False
@@ -1931,7 +1964,8 @@ _C_KW = {
     for _, arg in struct.member_list():
         arg.attr_put(ri, "obj")
 
-    ri.cw.p("ynl_attr_nest_end(nlh, nest);")
+    if struct.submsg is None:
+        ri.cw.p("ynl_attr_nest_end(nlh, nest);")
 
     ri.cw.nl()
     ri.cw.p('return 0;')
@@ -1968,6 +2002,7 @@ _C_KW = {
         if 'multi-attr' in aspec:
             multi_attrs.add(arg)
         needs_parg |= 'nested-attributes' in aspec
+        needs_parg |= 'sub-message' in aspec
     if array_nests or multi_attrs:
         local_vars.append('int i;')
     if needs_parg:
@@ -2086,9 +2121,43 @@ _C_KW = {
     ri.cw.nl()
 
 
+def parse_rsp_submsg(ri, struct):
+    parse_rsp_nested_prototype(ri, struct, suffix='')
+
+    var = 'dst'
+
+    ri.cw.block_start()
+    ri.cw.write_func_lvar(['const struct nlattr *attr = nested;',
+                          f'{struct.ptr_name}{var} = yarg->data;',
+                          'struct ynl_parse_arg parg;'])
+
+    ri.cw.p('parg.ys = yarg->ys;')
+    ri.cw.nl()
+
+    first = True
+    for name, arg in struct.member_list():
+        kw = 'if' if first else 'else if'
+        first = False
+
+        ri.cw.block_start(line=f'{kw} (!strcmp(sel, "{name}"))')
+        get_lines, init_lines, _ = arg._attr_get(ri, var)
+        for line in init_lines:
+            ri.cw.p(line)
+        for line in get_lines:
+            ri.cw.p(line)
+        if arg.presence_type() == 'present':
+            ri.cw.p(f"{var}->_present.{arg.c_name} = 1;")
+        ri.cw.block_end()
+    ri.cw.p('return 0;')
+    ri.cw.block_end()
+    ri.cw.nl()
+
+
 def parse_rsp_nested_prototype(ri, struct, suffix=';'):
     func_args = ['struct ynl_parse_arg *yarg',
                  'const struct nlattr *nested']
+    if struct.submsg:
+        func_args.insert(1, 'const char *sel')
     for arg in struct.inherited:
         func_args.append('__u32 ' + arg)
 
@@ -2097,6 +2166,9 @@ _C_KW = {
 
 
 def parse_rsp_nested(ri, struct):
+    if struct.submsg:
+        return parse_rsp_submsg(ri, struct)
+
     parse_rsp_nested_prototype(ri, struct, suffix='')
 
     local_vars = ['const struct nlattr *attr;',
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (5 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 10:58   ` Donald Hunter
  2025-05-15 23:16 ` [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families Jakub Kicinski
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Reverse parsing lets YNL convert bad and missing attr pointers
from extack into a string like "missing attribute nest1.nest2.attr_name".
It's a feature that's unique to YNL C AFAIU (even the Python YNL
can't do nested reverse parsing). Add support for reverse-parsing
of sub-messages.

To simplify the logic and the code annotate the type policies
with extra metadata. Mark the selectors and the messages with
the information we need. We assume that key / selector always
precedes the sub-message while parsing (and also if there are
multiple sub-messages like in rt-link they are interleaved
selector 1 ... submsg 1 ... selector 2 .. submsg 2, not
selector 1 ... selector 2 ... submsg 1 ... submsg 2).

The rt-link sample in a subsequent changes shows reverse parsing
of sub-messages in action.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/lib/ynl-priv.h     |  5 +-
 tools/net/ynl/lib/ynl.c          | 84 ++++++++++++++++++++++++++++----
 tools/net/ynl/pyynl/ynl_gen_c.py | 29 ++++++++++-
 3 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/tools/net/ynl/lib/ynl-priv.h b/tools/net/ynl/lib/ynl-priv.h
index fbc058dd1c3e..416866f85820 100644
--- a/tools/net/ynl/lib/ynl-priv.h
+++ b/tools/net/ynl/lib/ynl-priv.h
@@ -43,7 +43,10 @@ typedef int (*ynl_parse_cb_t)(const struct nlmsghdr *nlh,
 			      struct ynl_parse_arg *yarg);
 
 struct ynl_policy_attr {
-	enum ynl_policy_type type;
+	enum ynl_policy_type type:8;
+	__u8 is_submsg:1;
+	__u8 is_selector:1;
+	__u16 selector_type;
 	unsigned int len;
 	const char *name;
 	const struct ynl_policy_nest *nest;
diff --git a/tools/net/ynl/lib/ynl.c b/tools/net/ynl/lib/ynl.c
index 25fc6501349b..2a169c3c0797 100644
--- a/tools/net/ynl/lib/ynl.c
+++ b/tools/net/ynl/lib/ynl.c
@@ -45,8 +45,39 @@
 #define perr(_ys, _msg)			__yerr(&(_ys)->err, errno, _msg)
 
 /* -- Netlink boiler plate */
+static bool
+ynl_err_walk_is_sel(const struct ynl_policy_nest *policy,
+		    const struct nlattr *attr)
+{
+	unsigned int type = ynl_attr_type(attr);
+
+	return policy && type <= policy->max_attr &&
+		policy->table[type].is_selector;
+}
+
+static const struct ynl_policy_nest *
+ynl_err_walk_sel_policy(const struct ynl_policy_attr *policy_attr,
+			const struct nlattr *selector)
+{
+	const struct ynl_policy_nest *policy = policy_attr->nest;
+	const char *sel;
+	unsigned int i;
+
+	if (!policy_attr->is_submsg)
+		return policy;
+
+	sel = ynl_attr_get_str(selector);
+	for (i = 0; i <= policy->max_attr; i++) {
+		if (!strcmp(sel, policy->table[i].name))
+			return policy->table[i].nest;
+	}
+
+	return NULL;
+}
+
 static int
-ynl_err_walk_report_one(const struct ynl_policy_nest *policy, unsigned int type,
+ynl_err_walk_report_one(const struct ynl_policy_nest *policy,
+			const struct nlattr *selector, unsigned int type,
 			char *str, int str_sz, int *n)
 {
 	if (!policy) {
@@ -67,9 +98,34 @@ ynl_err_walk_report_one(const struct ynl_policy_nest *policy, unsigned int type,
 		return 1;
 	}
 
-	if (*n < str_sz)
-		*n += snprintf(str, str_sz - *n,
-			       ".%s", policy->table[type].name);
+	if (*n < str_sz) {
+		int sz;
+
+		sz = snprintf(str, str_sz - *n,
+			      ".%s", policy->table[type].name);
+		*n += sz;
+		str += sz;
+	}
+
+	if (policy->table[type].is_submsg) {
+		if (!selector) {
+			if (*n < str_sz)
+				*n += snprintf(str, str_sz, "(!selector)");
+			return 1;
+		}
+
+		if (ynl_attr_type(selector) !=
+		    policy->table[type].selector_type) {
+			if (*n < str_sz)
+				*n += snprintf(str, str_sz, "(!=selector)");
+			return 1;
+		}
+
+		if (*n < str_sz)
+			*n += snprintf(str, str_sz - *n, "(%s)",
+				       ynl_attr_get_str(selector));
+	}
+
 	return 0;
 }
 
@@ -78,6 +134,8 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 	     const struct ynl_policy_nest *policy, char *str, int str_sz,
 	     const struct ynl_policy_nest **nest_pol)
 {
+	const struct ynl_policy_nest *next_pol;
+	const struct nlattr *selector = NULL;
 	unsigned int astart_off, aend_off;
 	const struct nlattr *attr;
 	unsigned int data_len;
@@ -96,6 +154,10 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 	ynl_attr_for_each_payload(start, data_len, attr) {
 		astart_off = (char *)attr - (char *)start;
 		aend_off = (char *)ynl_attr_data_end(attr) - (char *)start;
+
+		if (ynl_err_walk_is_sel(policy, attr))
+			selector = attr;
+
 		if (aend_off <= off)
 			continue;
 
@@ -109,16 +171,20 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 
 	type = ynl_attr_type(attr);
 
-	if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
+	if (ynl_err_walk_report_one(policy, selector, type, str, str_sz, &n))
+		return n;
+
+	next_pol = ynl_err_walk_sel_policy(&policy->table[type], selector);
+	if (!next_pol)
 		return n;
 
 	if (!off) {
 		if (nest_pol)
-			*nest_pol = policy->table[type].nest;
+			*nest_pol = next_pol;
 		return n;
 	}
 
-	if (!policy->table[type].nest) {
+	if (!next_pol) {
 		if (n < str_sz)
 			n += snprintf(str, str_sz, "!nest");
 		return n;
@@ -128,7 +194,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 	start =  ynl_attr_data(attr);
 	end = start + ynl_attr_data_len(attr);
 
-	return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
+	return n + ynl_err_walk(ys, start, end, off, next_pol,
 				&str[n], str_sz - n, nest_pol);
 }
 
@@ -231,7 +297,7 @@ ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
 		}
 
 		n2 = 0;
-		ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
+		ynl_err_walk_report_one(nest_pol, NULL, type, &miss_attr[n],
 					sizeof(miss_attr) - n, &n2);
 		n += n2;
 
diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index b6b54d6fa906..1f8cc34ab3f0 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -57,6 +57,8 @@ from lib import SpecSubMessage, SpecSubMessageFormat
         self.request = False
         self.reply = False
 
+        self.is_selector = False
+
         if 'len' in attr:
             self.len = attr['len']
 
@@ -484,7 +486,10 @@ from lib import SpecSubMessage, SpecSubMessageFormat
         ri.cw.p(f"char *{self.c_name};")
 
     def _attr_typol(self):
-        return f'.type = YNL_PT_NUL_STR, '
+        typol = f'.type = YNL_PT_NUL_STR, '
+        if self.is_selector:
+            typol += '.is_selector = 1, '
+        return typol
 
     def _attr_policy(self, policy):
         if 'exact-len' in self.checks:
@@ -878,6 +883,16 @@ from lib import SpecSubMessage, SpecSubMessageFormat
 
 
 class TypeSubMessage(TypeNest):
+    def __init__(self, family, attr_set, attr, value):
+        super().__init__(family, attr_set, attr, value)
+
+        self.selector = Selector(attr, attr_set)
+
+    def _attr_typol(self):
+        typol = f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
+        typol += f'.is_submsg = 1, .selector_type = {self.attr_set[self["selector"]].value} '
+        return typol
+
     def _attr_get(self, ri, var):
         sel = c_lower(self['selector'])
         get_lines = [f'if (!{var}->{sel})',
@@ -890,6 +905,18 @@ from lib import SpecSubMessage, SpecSubMessageFormat
         return get_lines, init_lines, None
 
 
+class Selector:
+    def __init__(self, msg_attr, attr_set):
+        self.name = msg_attr["selector"]
+
+        if self.name in attr_set:
+            self.attr = attr_set[self.name]
+            self.attr.is_selector = True
+            self._external = False
+        else:
+            raise Exception("Passing selectors from external nests not supported")
+
+
 class Struct:
     def __init__(self, family, space_name, type_list=None,
                  inherited=None, submsg=None):
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (6 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 11:00   ` Donald Hunter
  2025-05-19 14:49   ` Kory Maincent
  2025-05-15 23:16 ` [PATCH net-next 9/9] tools: ynl: add a sample for rt-link Jakub Kicinski
  2025-05-16 23:40 ` [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link patchwork-bot+netdevbpf
  9 siblings, 2 replies; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski

Switch from including Classic netlink families one by one to excluding.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/net/ynl/Makefile.deps      | 4 ++++
 tools/net/ynl/generated/Makefile | 7 +++----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
index a5e6093903fb..e5a5cb1b2cff 100644
--- a/tools/net/ynl/Makefile.deps
+++ b/tools/net/ynl/Makefile.deps
@@ -33,5 +33,9 @@ CFLAGS_ovs_flow:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
 CFLAGS_ovs_vport:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
 CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)
+CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
+	$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
+CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
 CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
+CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
 CFLAGS_tcp_metrics:=$(call get_hdr_inc,_LINUX_TCP_METRICS_H,tcp_metrics.h)
diff --git a/tools/net/ynl/generated/Makefile b/tools/net/ynl/generated/Makefile
index 6603ad8d4ce1..9208feed28c1 100644
--- a/tools/net/ynl/generated/Makefile
+++ b/tools/net/ynl/generated/Makefile
@@ -22,10 +22,9 @@ TOOL:=../pyynl/ynl_gen_c.py
 TOOL_RST:=../pyynl/ynl_gen_rst.py
 
 SPECS_DIR:=../../../../Documentation/netlink/specs
-GENS_PATHS=$(shell grep -nrI --files-without-match \
-		'protocol: netlink' \
-		$(SPECS_DIR))
-GENS=$(patsubst $(SPECS_DIR)/%.yaml,%,${GENS_PATHS}) rt-addr rt-route
+SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
+GENS_UNSUP=conntrack nftables tc
+GENS=$(filter-out ${GENS_UNSUP},$(patsubst $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS}))
 SRCS=$(patsubst %,%-user.c,${GENS})
 HDRS=$(patsubst %,%-user.h,${GENS})
 OBJS=$(patsubst %,%-user.o,${GENS})
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH net-next 9/9] tools: ynl: add a sample for rt-link
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (7 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families Jakub Kicinski
@ 2025-05-15 23:16 ` Jakub Kicinski
  2025-05-16 11:40   ` Donald Hunter
  2025-05-16 23:40 ` [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link patchwork-bot+netdevbpf
  9 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-15 23:16 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
	daniel, nicolas.dichtel, jacob.e.keller, Jakub Kicinski, bpf

Add a fairly complete example of rt-link usage. If run without any
arguments it simply lists the interfaces and some of their attrs.
If run with an arg it tries to create and delete a netkit device.

 1 # ./tools/net/ynl/samples/rt-link 1
 2 Trying to create a Netkit interface
 3 Testing error message for policy being bad:
 4     Kernel error: 'Provided default xmit policy not supported' (bad attribute: .linkinfo.data(netkit).policy)
 5   1:               lo: mtu 65536
 6   2:           wlp0s1: mtu  1500
 7   3:          enp0s13: mtu  1500
 8   4:           dummy0: mtu  1500  kind dummy     altname one two
 9   5:              nk0: mtu  1500  kind netkit    primary 0  policy forward
10   6:              nk1: mtu  1500  kind netkit    primary 1  policy blackhole
11 Trying to delete a Netkit interface (ifindex 6)

Sample creates the device first, it sets an invalid value for a netkit
attribute to trigger reverse parsing. Line 4 shows the error with the
attribute path correctly generated by YNL.

Then sample fixes the bad attribute and re-issues the request, with
NLM_F_ECHO set. This flag causes the notification to be looped back
to the initiating socket (our socket). Sample parses this notification
to save the ifindex of the created netkit.

Sample then proceeds to list the devices. Line 8 above shows a dummy
device with two alt names. Lines 9 and 10 show the netkit devices
the sample itself created.

The "primary" and "policy" attrs are from inside the netkit submsg.
The string values are auto-generated for the enums by YNL.

To clean up sample deletes the interface it created (line 11).

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: bpf@vger.kernel.org
---
 tools/net/ynl/samples/rt-link.c  | 184 +++++++++++++++++++++++++++++++
 tools/net/ynl/samples/.gitignore |   1 +
 2 files changed, 185 insertions(+)
 create mode 100644 tools/net/ynl/samples/rt-link.c

diff --git a/tools/net/ynl/samples/rt-link.c b/tools/net/ynl/samples/rt-link.c
new file mode 100644
index 000000000000..acdd4b4a0f74
--- /dev/null
+++ b/tools/net/ynl/samples/rt-link.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <string.h>
+
+#include <ynl.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "rt-link-user.h"
+
+static void rt_link_print(struct rt_link_getlink_rsp *r)
+{
+	unsigned int i;
+
+	printf("%3d: ", r->_hdr.ifi_index);
+
+	if (r->_len.ifname)
+		printf("%16s: ", r->ifname);
+
+	if (r->_present.mtu)
+		printf("mtu %5d  ", r->mtu);
+
+	if (r->linkinfo._len.kind)
+		printf("kind %-8s  ", r->linkinfo.kind);
+	else
+		printf("     %8s  ", "");
+
+	if (r->prop_list._count.alt_ifname) {
+		printf("altname ");
+		for (i = 0; i < r->prop_list._count.alt_ifname; i++)
+			printf("%s ", r->prop_list.alt_ifname[i]->str);
+		printf(" ");
+	}
+
+	if (r->linkinfo._present.data && r->linkinfo.data._present.netkit) {
+		struct rt_link_linkinfo_netkit_attrs *netkit;
+		const char *name;
+
+		netkit = &r->linkinfo.data.netkit;
+		printf("primary %d  ", netkit->primary);
+
+		name = NULL;
+		if (netkit->_present.policy)
+			name = rt_link_netkit_policy_str(netkit->policy);
+		if (name)
+			printf("policy %s  ", name);
+	}
+
+	printf("\n");
+}
+
+static int rt_link_create_netkit(struct ynl_sock *ys)
+{
+	struct rt_link_getlink_ntf *ntf_gl;
+	struct rt_link_newlink_req *req;
+	struct ynl_ntf_base_type *ntf;
+	int ret;
+
+	req = rt_link_newlink_req_alloc();
+	if (!req) {
+		fprintf(stderr, "Can't alloc req\n");
+		return -1;
+	}
+
+	/* rtnetlink doesn't provide info about the created object.
+	 * It expects us to set the ECHO flag and the dig the info out
+	 * of the notifications...
+	 */
+	rt_link_newlink_req_set_nlflags(req, NLM_F_CREATE | NLM_F_ECHO);
+
+	rt_link_newlink_req_set_linkinfo_kind(req, "netkit");
+
+	/* Test error messages */
+	rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, 10);
+	ret = rt_link_newlink(ys, req);
+	if (ret) {
+		printf("Testing error message for policy being bad:\n\t%s\n", ys->err.msg);
+	} else {
+		fprintf(stderr,	"Warning: unexpected success creating netkit with bad attrs\n");
+		goto created;
+	}
+
+	rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, NETKIT_DROP);
+
+	ret = rt_link_newlink(ys, req);
+created:
+	rt_link_newlink_req_free(req);
+	if (ret) {
+		fprintf(stderr, "YNL: %s\n", ys->err.msg);
+		return -1;
+	}
+
+	if (!ynl_has_ntf(ys)) {
+		fprintf(stderr,
+			"Warning: interface created but received no notification, won't delete the interface\n");
+		return 0;
+	}
+
+	ntf = ynl_ntf_dequeue(ys);
+	if (ntf->cmd !=	RTM_NEWLINK) {
+		fprintf(stderr,
+			"Warning: unexpected notification type, won't delete the interface\n");
+		return 0;
+	}
+	ntf_gl = (void *)ntf;
+	ret = ntf_gl->obj._hdr.ifi_index;
+	ynl_ntf_free(ntf);
+
+	return ret;
+}
+
+static void rt_link_del(struct ynl_sock *ys, int ifindex)
+{
+	struct rt_link_dellink_req *req;
+
+	req = rt_link_dellink_req_alloc();
+	if (!req) {
+		fprintf(stderr, "Can't alloc req\n");
+		return;
+	}
+
+	req->_hdr.ifi_index = ifindex;
+	if (rt_link_dellink(ys, req))
+		fprintf(stderr, "YNL: %s\n", ys->err.msg);
+	else
+		fprintf(stderr,
+			"Trying to delete a Netkit interface (ifindex %d)\n",
+			ifindex);
+
+	rt_link_dellink_req_free(req);
+}
+
+int main(int argc, char **argv)
+{
+	struct rt_link_getlink_req_dump *req;
+	struct rt_link_getlink_list *rsp;
+	struct ynl_error yerr;
+	struct ynl_sock *ys;
+	int created = 0;
+
+	ys = ynl_sock_create(&ynl_rt_link_family, &yerr);
+	if (!ys) {
+		fprintf(stderr, "YNL: %s\n", yerr.msg);
+		return 1;
+	}
+
+	if (argc > 1) {
+		fprintf(stderr, "Trying to create a Netkit interface\n");
+		created = rt_link_create_netkit(ys);
+		if (created < 0)
+			goto err_destroy;
+	}
+
+	req = rt_link_getlink_req_dump_alloc();
+	if (!req)
+		goto err_del_ifc;
+
+	rsp = rt_link_getlink_dump(ys, req);
+	rt_link_getlink_req_dump_free(req);
+	if (!rsp)
+		goto err_close;
+
+	if (ynl_dump_empty(rsp))
+		fprintf(stderr, "Error: no links reported\n");
+	ynl_dump_foreach(rsp, link)
+		rt_link_print(link);
+	rt_link_getlink_list_free(rsp);
+
+	if (created)
+		rt_link_del(ys, created);
+
+	ynl_sock_destroy(ys);
+	return 0;
+
+err_close:
+	fprintf(stderr, "YNL: %s\n", ys->err.msg);
+err_del_ifc:
+	if (created)
+		rt_link_del(ys, created);
+err_destroy:
+	ynl_sock_destroy(ys);
+	return 2;
+}
diff --git a/tools/net/ynl/samples/.gitignore b/tools/net/ynl/samples/.gitignore
index 7f9781cf532f..b3ec3fb0929f 100644
--- a/tools/net/ynl/samples/.gitignore
+++ b/tools/net/ynl/samples/.gitignore
@@ -4,4 +4,5 @@ netdev
 ovs
 page-pool
 rt-addr
+rt-link
 rt-route
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn
  2025-05-15 23:16 ` [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn Jakub Kicinski
@ 2025-05-16  9:08   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16  9:08 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> C naming info for OVPN which was added since I adjusted
> the existing attrs. Also add missing reference to a header needed
> for a bridge struct.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct
  2025-05-15 23:16 ` [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct Jakub Kicinski
@ 2025-05-16  9:57   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16  9:57 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> We're about to add some code here for sub-messages.
> Factor out the nest-related logic to make the code readable.
> No functional change.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs
  2025-05-15 23:16 ` [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs Jakub Kicinski
@ 2025-05-16 10:15   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 10:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> Prepare for constructing Struct() instances which represent
> sub-messages rather than nested attributes.
> Restructure the code / indentation to more easily insert
> a case where nested reference comes from annotation other
> than the 'nested-attributes' property. Make sure we don't
> construct the Struct() object from scratch in multiple
> places as the constructor will soon have more arguments.
>
> This should cause no functional change.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type
  2025-05-15 23:16 ` [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type Jakub Kicinski
@ 2025-05-16 10:20   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 10:20 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> Hook in handling of sub-messages, for now treat them as ignored attrs.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs
  2025-05-15 23:16 ` [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs Jakub Kicinski
@ 2025-05-16 10:31   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 10:31 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> The easiest (or perhaps only sane) way to support submessages in C
> is to treat them as if they were nests. Build fake attributes to
> that effect in the codegen. Render the submsg as a big nest of all
> possible values.
>
> With this in place the main missing part is to hook in the switch
> which selects how to parse based on the key.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages
  2025-05-15 23:16 ` [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages Jakub Kicinski
@ 2025-05-16 10:43   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 10:43 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> Adjust parsing and rendering appropriately to make sub-messages work.
> Rendering is pretty trivial, as the submsg -> netlink conversion looks
> like rendering a nest in which only one attr was set. Only trick
> is that we use the enum value of the sub-message rather than the nest
> as the type, and effectively skip one layer of nesting. A real double
> nested struct would look like this:
>
>   [SELECTOR]
>   [SUBMSG]
>     [NEST]
>       [MSG1-ATTR]
>
> A submsg "is" the nest so by skipping I mean:
>
>   [SELECTOR]
>   [SUBMSG]
>     [MSG1-ATTR]
>
> There is no extra validation in YNL if caller has set the selector
> matching the submsg type (e.g. link type = "macvlan" but the nest
> attrs are set to carry "veth"). Let the kernel handle that.
>
> Parsing side is a little more specialized as we need to render and
> insert a new kind of function which switches between what to parse
> based on the selector. But code isn't too complicated.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting
  2025-05-15 23:16 ` [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting Jakub Kicinski
@ 2025-05-16 10:58   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 10:58 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> Reverse parsing lets YNL convert bad and missing attr pointers
> from extack into a string like "missing attribute nest1.nest2.attr_name".
> It's a feature that's unique to YNL C AFAIU (even the Python YNL
> can't do nested reverse parsing). Add support for reverse-parsing
> of sub-messages.
>
> To simplify the logic and the code annotate the type policies
> with extra metadata. Mark the selectors and the messages with
> the information we need. We assume that key / selector always
> precedes the sub-message while parsing (and also if there are
> multiple sub-messages like in rt-link they are interleaved
> selector 1 ... submsg 1 ... selector 2 .. submsg 2, not
> selector 1 ... selector 2 ... submsg 1 ... submsg 2).
>
> The rt-link sample in a subsequent changes shows reverse parsing
> of sub-messages in action.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families
  2025-05-15 23:16 ` [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families Jakub Kicinski
@ 2025-05-16 11:00   ` Donald Hunter
  2025-05-19 14:49   ` Kory Maincent
  1 sibling, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 11:00 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller

Jakub Kicinski <kuba@kernel.org> writes:

> Switch from including Classic netlink families one by one to excluding.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 9/9] tools: ynl: add a sample for rt-link
  2025-05-15 23:16 ` [PATCH net-next 9/9] tools: ynl: add a sample for rt-link Jakub Kicinski
@ 2025-05-16 11:40   ` Donald Hunter
  0 siblings, 0 replies; 23+ messages in thread
From: Donald Hunter @ 2025-05-16 11:40 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, daniel,
	nicolas.dichtel, jacob.e.keller, bpf

On Fri, 16 May 2025 at 00:17, Jakub Kicinski <kuba@kernel.org> wrote:
>
> Add a fairly complete example of rt-link usage. If run without any
> arguments it simply lists the interfaces and some of their attrs.
> If run with an arg it tries to create and delete a netkit device.
>
>  1 # ./tools/net/ynl/samples/rt-link 1
>  2 Trying to create a Netkit interface
>  3 Testing error message for policy being bad:
>  4     Kernel error: 'Provided default xmit policy not supported' (bad attribute: .linkinfo.data(netkit).policy)
>  5   1:               lo: mtu 65536
>  6   2:           wlp0s1: mtu  1500
>  7   3:          enp0s13: mtu  1500
>  8   4:           dummy0: mtu  1500  kind dummy     altname one two
>  9   5:              nk0: mtu  1500  kind netkit    primary 0  policy forward
> 10   6:              nk1: mtu  1500  kind netkit    primary 1  policy blackhole
> 11 Trying to delete a Netkit interface (ifindex 6)
>
> Sample creates the device first, it sets an invalid value for a netkit
> attribute to trigger reverse parsing. Line 4 shows the error with the
> attribute path correctly generated by YNL.
>
> Then sample fixes the bad attribute and re-issues the request, with
> NLM_F_ECHO set. This flag causes the notification to be looped back
> to the initiating socket (our socket). Sample parses this notification
> to save the ifindex of the created netkit.
>
> Sample then proceeds to list the devices. Line 8 above shows a dummy
> device with two alt names. Lines 9 and 10 show the netkit devices
> the sample itself created.
>
> The "primary" and "policy" attrs are from inside the netkit submsg.
> The string values are auto-generated for the enums by YNL.
>
> To clean up sample deletes the interface it created (line 11).
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link
  2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
                   ` (8 preceding siblings ...)
  2025-05-15 23:16 ` [PATCH net-next 9/9] tools: ynl: add a sample for rt-link Jakub Kicinski
@ 2025-05-16 23:40 ` patchwork-bot+netdevbpf
  9 siblings, 0 replies; 23+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-05-16 23:40 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
	donald.hunter, daniel, nicolas.dichtel, jacob.e.keller

Hello:

This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Thu, 15 May 2025 16:16:41 -0700 you wrote:
> Sub-messages are how we express "polymorphism" in YNL. Donald added
> the support to specs and Python a while back, support them in C, too.
> Sub-message is a nest, but the interpretation of the attribute types
> within that nest depends on a value of another attribute. For example
> in rt-link the "kind" attribute contains the link type (veth, bonding,
> etc.) and based on that the right enum has to be applied to interpret
> link-specific attributes.
> 
> [...]

Here is the summary with links:
  - [net-next,1/9] netlink: specs: rt-link: add C naming info for ovpn
    https://git.kernel.org/netdev/net-next/c/dc3f63bc3e33
  - [net-next,2/9] tools: ynl-gen: factor out the annotation of pure nested struct
    https://git.kernel.org/netdev/net-next/c/c9c048993d4c
  - [net-next,3/9] tools: ynl-gen: prepare for submsg structs
    https://git.kernel.org/netdev/net-next/c/99b76908a7a3
  - [net-next,4/9] tools: ynl-gen: submsg: plumb thru an empty type
    https://git.kernel.org/netdev/net-next/c/3186a8e55ae3
  - [net-next,5/9] tools: ynl-gen: submsg: render the structs
    https://git.kernel.org/netdev/net-next/c/6366d267788f
  - [net-next,6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages
    (no matching commit)
  - [net-next,7/9] tools: ynl: submsg: reverse parse / error reporting
    https://git.kernel.org/netdev/net-next/c/0939a418b3b0
  - [net-next,8/9] tools: ynl: enable codegen for all rt- families
    https://git.kernel.org/netdev/net-next/c/6bab77ced3ff
  - [net-next,9/9] tools: ynl: add a sample for rt-link
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families
  2025-05-15 23:16 ` [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families Jakub Kicinski
  2025-05-16 11:00   ` Donald Hunter
@ 2025-05-19 14:49   ` Kory Maincent
  2025-05-19 15:57     ` Jakub Kicinski
  1 sibling, 1 reply; 23+ messages in thread
From: Kory Maincent @ 2025-05-19 14:49 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
	donald.hunter, daniel, nicolas.dichtel, jacob.e.keller

On Thu, 15 May 2025 16:16:49 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> Switch from including Classic netlink families one by one to excluding.
> 
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
>  tools/net/ynl/Makefile.deps      | 4 ++++
>  tools/net/ynl/generated/Makefile | 7 +++----
>  2 files changed, 7 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
> index a5e6093903fb..e5a5cb1b2cff 100644
> --- a/tools/net/ynl/Makefile.deps
> +++ b/tools/net/ynl/Makefile.deps
> @@ -33,5 +33,9 @@ CFLAGS_ovs_flow:=$(call
> get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) CFLAGS_ovs_vport:=$(call
> get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) CFLAGS_rt-addr:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \ $(call
> get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h) +CFLAGS_rt-link:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
> +	$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
> +CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
>  CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
> +CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
>  CFLAGS_tcp_metrics:=$(call get_hdr_inc,_LINUX_TCP_METRICS_H,tcp_metrics.h)
> diff --git a/tools/net/ynl/generated/Makefile
> b/tools/net/ynl/generated/Makefile index 6603ad8d4ce1..9208feed28c1 100644
> --- a/tools/net/ynl/generated/Makefile
> +++ b/tools/net/ynl/generated/Makefile
> @@ -22,10 +22,9 @@ TOOL:=../pyynl/ynl_gen_c.py
>  TOOL_RST:=../pyynl/ynl_gen_rst.py
>  
>  SPECS_DIR:=../../../../Documentation/netlink/specs
> -GENS_PATHS=$(shell grep -nrI --files-without-match \
> -		'protocol: netlink' \
> -		$(SPECS_DIR))
> -GENS=$(patsubst $(SPECS_DIR)/%.yaml,%,${GENS_PATHS}) rt-addr rt-route
> +SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
> +GENS_UNSUP=conntrack nftables tc
> +GENS=$(filter-out ${GENS_UNSUP},$(patsubst
> $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS})) SRCS=$(patsubst %,%-user.c,${GENS})
>  HDRS=$(patsubst %,%-user.h,${GENS})
>  OBJS=$(patsubst %,%-user.o,${GENS})

This patch introduces a build error when building the specs.

Maybe we should add a spec build check in the net CI?

$ make -C tools/net/ynl -j9
-e 	CC rt-neigh-user.o
rt-neigh-user.c:122:10: error: ‘NDTPA_INTERVAL_PROBE_TIME_MS’ undeclared here (not in a function); did you mean ‘NDTPA_DELAY_PROBE_TIME’?
  122 |         [NDTPA_INTERVAL_PROBE_TIME_MS] = { .name = "interval-probe-time-ms", .type = YNL_PT_U64, },
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |          NDTPA_DELAY_PROBE_TIME
rt-neigh-user.c:122:10: error: array index in initializer not of integer type
rt-neigh-user.c:122:10: note: (near initialization for ‘rt_neigh_ndtpa_attrs_policy’)
rt-neigh-user.c:122:42: warning: excess elements in array initializer
  122 |         [NDTPA_INTERVAL_PROBE_TIME_MS] = { .name = "interval-probe-time-ms", .type = YNL_PT_U64, },
      |                                          ^
rt-neigh-user.c:122:42: note: (near initialization for ‘rt_neigh_ndtpa_attrs_policy’)
rt-neigh-user.c:146:10: error: ‘NDA_FLAGS_EXT’ undeclared here (not in a function)
  146 |         [NDA_FLAGS_EXT] = { .name = "flags-ext", .type = YNL_PT_U32, },
      |          ^~~~~~~~~~~~~
rt-neigh-user.c:146:10: error: array index in initializer not of integer type
rt-neigh-user.c:146:10: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
rt-neigh-user.c:146:27: warning: excess elements in array initializer
  146 |         [NDA_FLAGS_EXT] = { .name = "flags-ext", .type = YNL_PT_U32, },
      |                           ^
rt-neigh-user.c:146:27: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
rt-neigh-user.c:147:10: error: ‘NDA_NDM_STATE_MASK’ undeclared here (not in a function)
  147 |         [NDA_NDM_STATE_MASK] = { .name = "ndm-state-mask", .type = YNL_PT_U16, },
      |          ^~~~~~~~~~~~~~~~~~
rt-neigh-user.c:147:10: error: array index in initializer not of integer type
rt-neigh-user.c:147:10: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
rt-neigh-user.c:147:32: warning: excess elements in array initializer
  147 |         [NDA_NDM_STATE_MASK] = { .name = "ndm-state-mask", .type = YNL_PT_U16, },
      |                                ^
rt-neigh-user.c:147:32: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
rt-neigh-user.c:148:10: error: ‘NDA_NDM_FLAGS_MASK’ undeclared here (not in a function); did you mean ‘XDP_FLAGS_MASK’?
  148 |         [NDA_NDM_FLAGS_MASK] = { .name = "ndm-flags-mask", .type = YNL_PT_U8, },
      |          ^~~~~~~~~~~~~~~~~~
      |          XDP_FLAGS_MASK
rt-neigh-user.c:148:10: error: array index in initializer not of integer type
rt-neigh-user.c:148:10: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
rt-neigh-user.c:148:32: warning: excess elements in array initializer
  148 |         [NDA_NDM_FLAGS_MASK] = { .name = "ndm-flags-mask", .type = YNL_PT_U8, },
      |                                ^
rt-neigh-user.c:148:32: note: (near initialization for ‘rt_neigh_neighbour_attrs_policy’)
make[1]: *** [Makefile:52: rt-neigh-user.o] Error 1

Regards,
-- 
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families
  2025-05-19 14:49   ` Kory Maincent
@ 2025-05-19 15:57     ` Jakub Kicinski
  2025-05-20  8:28       ` Kory Maincent
  0 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2025-05-19 15:57 UTC (permalink / raw)
  To: Kory Maincent
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
	donald.hunter, daniel, nicolas.dichtel, jacob.e.keller

On Mon, 19 May 2025 16:49:49 +0200 Kory Maincent wrote:
> > -GENS=$(patsubst $(SPECS_DIR)/%.yaml,%,${GENS_PATHS}) rt-addr rt-route
> > +SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
> > +GENS_UNSUP=conntrack nftables tc
> > +GENS=$(filter-out ${GENS_UNSUP},$(patsubst
> > $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS})) SRCS=$(patsubst %,%-user.c,${GENS})
> >  HDRS=$(patsubst %,%-user.h,${GENS})
> >  OBJS=$(patsubst %,%-user.o,${GENS})  
> 
> This patch introduces a build error when building the specs.
> 
> Maybe we should add a spec build check in the net CI?

Sorry about that :( We do have build tests, but the problem only
happens if system headers are much older than the spec. Looks like
these defines are there on both Fedora and Ubuntu LTS so builds pass.
Once the initial support is merged we should be out of the woods.

I can't repro on any of my systems, could you see if 
https://lore.kernel.org/all/20250517001318.285800-1-kuba@kernel.org/
will also give you trouble?

For the issue reported here could you see if this is enough?

diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
index 4e5c4dff9188..21132e89ceba 100644
--- a/tools/net/ynl/Makefile.deps
+++ b/tools/net/ynl/Makefile.deps
@@ -35,7 +35,8 @@ CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)
 CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
-CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
+CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
+	$(call get_hdr_inc,__LINUX_NEIGHBOUR_H,neighbour.h)
 CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
 CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
 CFLAGS_tc:=$(call get_hdr_inc,__LINUX_PKT_SCHED_H,pkt_sched.h) \
diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
index 5e67a7eaf4a7..b851c36ad25d 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/uapi/linux/neighbour.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_NEIGHBOUR_H
-#define __LINUX_NEIGHBOUR_H
+#ifndef _UAPI__LINUX_NEIGHBOUR_H
+#define _UAPI__LINUX_NEIGHBOUR_H
 
 #include <linux/types.h>
 #include <linux/netlink.h>

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families
  2025-05-19 15:57     ` Jakub Kicinski
@ 2025-05-20  8:28       ` Kory Maincent
  0 siblings, 0 replies; 23+ messages in thread
From: Kory Maincent @ 2025-05-20  8:28 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
	donald.hunter, daniel, nicolas.dichtel, jacob.e.keller

On Mon, 19 May 2025 08:57:03 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> On Mon, 19 May 2025 16:49:49 +0200 Kory Maincent wrote:
> > > -GENS=$(patsubst $(SPECS_DIR)/%.yaml,%,${GENS_PATHS}) rt-addr rt-route
> > > +SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
> > > +GENS_UNSUP=conntrack nftables tc
> > > +GENS=$(filter-out ${GENS_UNSUP},$(patsubst
> > > $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS})) SRCS=$(patsubst %,%-user.c,${GENS})
> > >  HDRS=$(patsubst %,%-user.h,${GENS})
> > >  OBJS=$(patsubst %,%-user.o,${GENS})    
> > 
> > This patch introduces a build error when building the specs.
> > 
> > Maybe we should add a spec build check in the net CI?  
> 
> Sorry about that :( We do have build tests, but the problem only
> happens if system headers are much older than the spec. Looks like
> these defines are there on both Fedora and Ubuntu LTS so builds pass.
> Once the initial support is merged we should be out of the woods.

Yeah, I am on an old Ubuntu 22.04 and I have to update my distro one day. ;)

> I can't repro on any of my systems, could you see if 
> https://lore.kernel.org/all/20250517001318.285800-1-kuba@kernel.org/
> will also give you trouble?

Indeed it does. I have replied to it.

> For the issue reported here could you see if this is enough?
> 
> diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
> index 4e5c4dff9188..21132e89ceba 100644
> --- a/tools/net/ynl/Makefile.deps
> +++ b/tools/net/ynl/Makefile.deps
> @@ -35,7 +35,8 @@ CFLAGS_rt-addr:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \ $(call
> get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h) CFLAGS_rt-link:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \ $(call
> get_hdr_inc,_LINUX_IF_LINK_H,if_link.h) -CFLAGS_rt-neigh:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) +CFLAGS_rt-neigh:=$(call
> get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
> +	$(call get_hdr_inc,__LINUX_NEIGHBOUR_H,neighbour.h)
>  CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
>  CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
>  CFLAGS_tc:=$(call get_hdr_inc,__LINUX_PKT_SCHED_H,pkt_sched.h) \
> diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
> index 5e67a7eaf4a7..b851c36ad25d 100644
> --- a/include/uapi/linux/neighbour.h
> +++ b/include/uapi/linux/neighbour.h
> @@ -1,6 +1,6 @@
>  /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> -#ifndef __LINUX_NEIGHBOUR_H
> -#define __LINUX_NEIGHBOUR_H
> +#ifndef _UAPI__LINUX_NEIGHBOUR_H
> +#define _UAPI__LINUX_NEIGHBOUR_H
>  
>  #include <linux/types.h>
>  #include <linux/netlink.h>

This change indeed fix this build issue.

Regards,
-- 
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2025-05-20  8:28 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-15 23:16 [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link Jakub Kicinski
2025-05-15 23:16 ` [PATCH net-next 1/9] netlink: specs: rt-link: add C naming info for ovpn Jakub Kicinski
2025-05-16  9:08   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 2/9] tools: ynl-gen: factor out the annotation of pure nested struct Jakub Kicinski
2025-05-16  9:57   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 3/9] tools: ynl-gen: prepare for submsg structs Jakub Kicinski
2025-05-16 10:15   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 4/9] tools: ynl-gen: submsg: plumb thru an empty type Jakub Kicinski
2025-05-16 10:20   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 5/9] tools: ynl-gen: submsg: render the structs Jakub Kicinski
2025-05-16 10:31   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 6/9] tools: ynl-gen: submsg: support parsing and rendering sub-messages Jakub Kicinski
2025-05-16 10:43   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 7/9] tools: ynl: submsg: reverse parse / error reporting Jakub Kicinski
2025-05-16 10:58   ` Donald Hunter
2025-05-15 23:16 ` [PATCH net-next 8/9] tools: ynl: enable codegen for all rt- families Jakub Kicinski
2025-05-16 11:00   ` Donald Hunter
2025-05-19 14:49   ` Kory Maincent
2025-05-19 15:57     ` Jakub Kicinski
2025-05-20  8:28       ` Kory Maincent
2025-05-15 23:16 ` [PATCH net-next 9/9] tools: ynl: add a sample for rt-link Jakub Kicinski
2025-05-16 11:40   ` Donald Hunter
2025-05-16 23:40 ` [PATCH net-next 0/9] tools: ynl-gen: support sub-messages and rt-link patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).