Merge branch 'tools-ynl-gen-support-sub-messages-and-rt-link'

Jakub Kicinski says:

====================
tools: ynl-gen: support sub-messages and rt-link

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

Link: https://patch.msgid.link/20250515231650.1325372-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-05-16 16:32:08 -07:00
10 changed files with 517 additions and 62 deletions

View File

@@ -594,6 +594,7 @@ definitions:
name: reasm-overlaps
- name: br-boolopt-multi
type: struct
header: linux/if_bridge.h
members:
-
name: optval
@@ -826,6 +827,8 @@ definitions:
- name: default
-
name: ovpn-mode
enum-name: ovpn-mode
name-prefix: ovpn-mode
type: enum
entries:
- p2p
@@ -2195,6 +2198,7 @@ attribute-sets:
type: u16
-
name: linkinfo-ovpn-attrs
name-prefix: ifla-ovpn-
attributes:
-
name: mode

View File

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

View File

@@ -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})

View File

@@ -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 {
@@ -42,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;
@@ -103,6 +107,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 */

View File

@@ -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;
@@ -384,6 +450,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)

View File

@@ -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,
};
/**

View File

@@ -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"]

View File

@@ -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):
@@ -56,11 +57,20 @@ class Type(SpecAttr):
self.request = False
self.reply = False
self.is_selector = False
if 'len' in attr:
self.len = attr['len']
if 'nested-attributes' in attr:
self.nested_attrs = attr['nested-attributes']
nested = attr['nested-attributes']
elif 'sub-message' in attr:
nested = attr['sub-message']
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:
@@ -119,7 +129,9 @@ class Type(SpecAttr):
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}"
@@ -474,7 +486,10 @@ class TypeString(Type):
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:
@@ -867,14 +882,51 @@ class TypeNestTypeValue(Type):
return get_lines, init_lines, local_vars
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})',
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 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):
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):
@@ -1047,6 +1099,8 @@ class AttrSet(SpecAttrSet):
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']}")
@@ -1091,6 +1145,16 @@ class Operation(SpecOperation):
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:
@@ -1173,6 +1237,9 @@ class Family(SpecFamily):
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'
@@ -1225,20 +1292,70 @@ class Family(SpecFamily):
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
elif 'sub-message' in spec:
nested = spec.sub_message
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:
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_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())
@@ -1246,37 +1363,32 @@ class Family(SpecFamily):
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)
elif 'sub-message' in spec:
nested = self._load_nested_set_submsg(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:
nested = spec['nested-attributes']
elif 'sub-message' in spec:
nested = spec.sub_message
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
@@ -1286,20 +1398,26 @@ class Family(SpecFamily):
# 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']
elif 'sub-message' in spec:
child_name = spec.sub_message
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):
@@ -1736,11 +1854,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] =')
@@ -1826,8 +1967,9 @@ def put_req_nested(ri, struct):
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
@@ -1849,7 +1991,8 @@ def put_req_nested(ri, struct):
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;')
@@ -1886,6 +2029,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:
@@ -2004,9 +2148,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)
@@ -2015,6 +2193,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;',
@@ -3302,8 +3483,7 @@ def main():
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)

View File

@@ -4,4 +4,5 @@ netdev
ovs
page-pool
rt-addr
rt-link
rt-route

View File

@@ -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;
}