mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 00:29:36 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
1
tools/net/ynl/samples/.gitignore
vendored
1
tools/net/ynl/samples/.gitignore
vendored
@@ -4,4 +4,5 @@ netdev
|
||||
ovs
|
||||
page-pool
|
||||
rt-addr
|
||||
rt-link
|
||||
rt-route
|
||||
|
||||
184
tools/net/ynl/samples/rt-link.c
Normal file
184
tools/net/ynl/samples/rt-link.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user