Commit b9e03e26 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

tools: ynl-gen: submsg: support parsing and rendering sub-messages



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.

Reviewed-by: default avatarDonald Hunter <donald.hunter@gmail.com>
Link: https://patch.msgid.link/20250515231650.1325372-7-kuba@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 6366d267
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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 */

+9 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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,
};

/**
+76 −4
Original line number Diff line number Diff line
@@ -878,7 +878,16 @@ class TypeNestTypeValue(Type):


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 @@ def print_dump_prototype(ri):
    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,6 +1940,7 @@ def put_req_nested(ri, struct):
    local_vars = []
    init_lines = []

    if struct.submsg is None:
        local_vars.append('struct nlattr *nest;')
        init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);")

@@ -1931,6 +1964,7 @@ def put_req_nested(ri, struct):
    for _, arg in struct.member_list():
        arg.attr_put(ri, "obj")

    if struct.submsg is None:
        ri.cw.p("ynl_attr_nest_end(nlh, nest);")

    ri.cw.nl()
@@ -1968,6 +2002,7 @@ def _multi_parse(ri, struct, init_lines, local_vars):
        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 @@ def _multi_parse(ri, struct, init_lines, local_vars):
    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 @@ def parse_rsp_nested_prototype(ri, struct, suffix=';'):


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;',