Merge branch 'tools-ynl-prepare-for-wireguard'

Asbjørn Sloth Tønnesen says:

====================
tools: ynl: prepare for wireguard

This series contains the last batch of YNL changes to support
the wireguard YNL conversion.
====================

Link: https://patch.msgid.link/20250915144301.725949-1-ast@fiberby.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-09-16 08:14:43 -07:00
5 changed files with 100 additions and 48 deletions

View File

@@ -154,7 +154,7 @@ properties:
Optional format indicator that is intended only for choosing
the right formatting mechanism when displaying values of this
type.
enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
enum: [ hex, mac, fddi, ipv4, ipv6, ipv4-or-v6, uuid ]
struct:
description: Name of the nested struct type.
type: string

View File

@@ -106,7 +106,6 @@ ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version);
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);
@@ -467,4 +466,13 @@ ynl_attr_put_sint(struct nlmsghdr *nlh, __u16 type, __s64 data)
else
ynl_attr_put_s64(nlh, type, data);
}
int __ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr,
unsigned int type);
static inline int ynl_attr_validate(struct ynl_parse_arg *yarg,
const struct nlattr *attr)
{
return __ynl_attr_validate(yarg, attr, ynl_attr_type(attr));
}
#endif

View File

@@ -360,15 +360,15 @@ static int ynl_cb_done(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
/* Attribute validation */
int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
int __ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr,
unsigned int type)
{
const struct ynl_policy_attr *policy;
unsigned int type, len;
unsigned char *data;
unsigned int len;
data = ynl_attr_data(attr);
len = ynl_attr_data_len(attr);
type = ynl_attr_type(attr);
if (type > yarg->rsp_policy->max_attr) {
yerr(yarg->ys, YNL_ERROR_INTERNAL,
"Internal error, validating unknown attribute");

View File

@@ -561,11 +561,13 @@ class YnlFamily(SpecFamily):
if attr["type"] == 'nest':
nl_type |= Netlink.NLA_F_NESTED
attr_payload = b''
sub_space = attr['nested-attributes']
sub_attrs = SpaceAttrs(self.attr_sets[sub_space], value, search_attrs)
for subname, subvalue in value.items():
attr_payload += self._add_attr(sub_space, subname, subvalue, sub_attrs)
attr_payload = self._add_nest_attrs(value, sub_space, search_attrs)
elif attr['type'] == 'indexed-array' and attr['sub-type'] == 'nest':
nl_type |= Netlink.NLA_F_NESTED
sub_space = attr['nested-attributes']
attr_payload = self._encode_indexed_array(value, sub_space,
search_attrs)
elif attr["type"] == 'flag':
if not value:
# If value is absent or false then skip attribute creation.
@@ -619,9 +621,28 @@ class YnlFamily(SpecFamily):
else:
raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}')
return self._add_attr_raw(nl_type, attr_payload)
def _add_attr_raw(self, nl_type, attr_payload):
pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4)
return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad
def _add_nest_attrs(self, value, sub_space, search_attrs):
sub_attrs = SpaceAttrs(self.attr_sets[sub_space], value, search_attrs)
attr_payload = b''
for subname, subvalue in value.items():
attr_payload += self._add_attr(sub_space, subname, subvalue,
sub_attrs)
return attr_payload
def _encode_indexed_array(self, vals, sub_space, search_attrs):
attr_payload = b''
for i, val in enumerate(vals):
idx = i | Netlink.NLA_F_NESTED
val_payload = self._add_nest_attrs(val, sub_space, search_attrs)
attr_payload += self._add_attr_raw(idx, val_payload)
return attr_payload
def _get_enum_or_unknown(self, enum, raw):
try:
name = enum.entries_by_val[raw].name
@@ -935,7 +956,7 @@ class YnlFamily(SpecFamily):
formatted = hex(raw)
else:
formatted = bytes.hex(raw, ' ')
elif display_hint in [ 'ipv4', 'ipv6' ]:
elif display_hint in [ 'ipv4', 'ipv6', 'ipv4-or-v6' ]:
formatted = format(ipaddress.ip_address(raw))
elif display_hint == 'uuid':
formatted = str(uuid.UUID(bytes=raw))
@@ -944,12 +965,17 @@ class YnlFamily(SpecFamily):
return formatted
def _from_string(self, string, attr_spec):
if attr_spec.display_hint in ['ipv4', 'ipv6']:
if attr_spec.display_hint in ['ipv4', 'ipv6', 'ipv4-or-v6']:
ip = ipaddress.ip_address(string)
if attr_spec['type'] == 'binary':
raw = ip.packed
else:
raw = int(ip)
elif attr_spec.display_hint == 'hex':
if attr_spec['type'] == 'binary':
raw = bytes.fromhex(string)
else:
raw = int(string, 16)
else:
raise Exception(f"Display hint '{attr_spec.display_hint}' not implemented"
f" when parsing '{attr_spec['name']}'")

View File

@@ -242,7 +242,7 @@ class Type(SpecAttr):
raise Exception(f"Attr get not implemented for class type {self.type}")
def attr_get(self, ri, var, first):
lines, init_lines, local_vars = self._attr_get(ri, var)
lines, init_lines, _ = self._attr_get(ri, var)
if type(lines) is str:
lines = [lines]
if type(init_lines) is str:
@@ -250,10 +250,6 @@ class Type(SpecAttr):
kw = 'if' if first else 'else if'
ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
if local_vars:
for local in local_vars:
ri.cw.p(local)
ri.cw.nl()
if not self.is_multi_val():
ri.cw.p("if (ynl_attr_validate(yarg, attr))")
@@ -791,7 +787,7 @@ class TypeMultiAttr(Type):
f"{presence} = n_{self.c_name};"]
class TypeArrayNest(Type):
class TypeIndexedArray(Type):
def is_multi_val(self):
return True
@@ -815,19 +811,26 @@ class TypeArrayNest(Type):
f'unsigned int n_{self.c_name}']
return super().arg_member(ri)
def _attr_policy(self, policy):
if self.attr['sub-type'] == 'nest':
return f'NLA_POLICY_NESTED_ARRAY({self.nested_render_name}_nl_policy)'
return super()._attr_policy(policy)
def _attr_typol(self):
if self.attr['sub-type'] in scalars:
return f'.type = YNL_PT_U{c_upper(self.sub_type[1:])}, '
elif self.attr['sub-type'] == 'binary' and 'exact-len' in self.checks:
return f'.type = YNL_PT_BINARY, .len = {self.checks["exact-len"]}, '
else:
elif self.attr['sub-type'] == 'nest':
return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
else:
raise Exception(f"Typol for IndexedArray sub-type {self.attr['sub-type']} not supported, yet")
def _attr_get(self, ri, var):
local_vars = ['const struct nlattr *attr2;']
get_lines = [f'attr_{self.c_name} = attr;',
'ynl_attr_for_each_nested(attr2, attr) {',
'\tif (ynl_attr_validate(yarg, attr2))',
'\tif (__ynl_attr_validate(yarg, attr2, type))',
'\t\treturn YNL_PARSE_CB_ERROR;',
f'\tn_{self.c_name}++;',
'}']
@@ -847,7 +850,7 @@ class TypeArrayNest(Type):
ri.cw.p(f'for (i = 0; i < {var}->_count.{self.c_name}; i++)')
ri.cw.p(f"{self.nested_render_name}_put(nlh, i, &{var}->{self.c_name}[i]);")
else:
raise Exception(f"Put for ArrayNest sub-type {self.attr['sub-type']} not supported, yet")
raise Exception(f"Put for IndexedArray sub-type {self.attr['sub-type']} not supported, yet")
ri.cw.p('ynl_attr_nest_end(nlh, array);')
def _setter_lines(self, ri, member, presence):
@@ -1124,7 +1127,7 @@ class AttrSet(SpecAttrSet):
t = TypeNest(self.family, self, elem, value)
elif elem['type'] == 'indexed-array' and 'sub-type' in elem:
if elem["sub-type"] in ['binary', 'nest', 'u32']:
t = TypeArrayNest(self.family, self, elem, value)
t = TypeIndexedArray(self.family, self, elem, value)
else:
raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}')
elif elem['type'] == 'nest-type-value':
@@ -2033,6 +2036,20 @@ def put_enum_to_str(family, cw, enum):
_put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
def put_local_vars(struct):
local_vars = []
has_array = False
has_count = False
for _, arg in struct.member_list():
has_array |= arg.type == 'indexed-array'
has_count |= arg.presence_type() == 'count'
if has_array:
local_vars.append('struct nlattr *array;')
if has_count:
local_vars.append('unsigned int i;')
return local_vars
def put_req_nested_prototype(ri, struct, suffix=';'):
func_args = ['struct nlmsghdr *nlh',
'unsigned int attr_type',
@@ -2055,15 +2072,7 @@ def put_req_nested(ri, struct):
init_lines.append(f"hdr = ynl_nlmsg_put_extra_header(nlh, {struct_sz});")
init_lines.append(f"memcpy(hdr, &obj->_hdr, {struct_sz});")
has_anest = False
has_count = False
for _, arg in struct.member_list():
has_anest |= arg.type == 'indexed-array'
has_count |= arg.presence_type() == 'count'
if has_anest:
local_vars.append('struct nlattr *array;')
if has_count:
local_vars.append('unsigned int i;')
local_vars += put_local_vars(struct)
put_req_nested_prototype(ri, struct, suffix='')
ri.cw.block_start()
@@ -2100,33 +2109,41 @@ def _multi_parse(ri, struct, init_lines, local_vars):
else:
raise Exception("Per-op fixed header not supported, yet")
array_nests = set()
indexed_arrays = set()
multi_attrs = set()
needs_parg = False
var_set = set()
for arg, aspec in struct.member_list():
if aspec['type'] == 'indexed-array' and 'sub-type' in aspec:
if aspec["sub-type"] in {'binary', 'nest'}:
local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;')
array_nests.add(arg)
indexed_arrays.add(arg)
elif aspec['sub-type'] in scalars:
local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;')
array_nests.add(arg)
indexed_arrays.add(arg)
else:
raise Exception(f'Not supported sub-type {aspec["sub-type"]}')
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:
try:
_, _, l_vars = aspec._attr_get(ri, '')
var_set |= set(l_vars) if l_vars else set()
except Exception:
pass # _attr_get() not implemented by simple types, ignore
local_vars += list(var_set)
if indexed_arrays or multi_attrs:
local_vars.append('int i;')
if needs_parg:
local_vars.append('struct ynl_parse_arg parg;')
init_lines.append('parg.ys = yarg->ys;')
all_multi = array_nests | multi_attrs
all_multi = indexed_arrays | multi_attrs
for anest in sorted(all_multi):
local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
for arg in sorted(all_multi):
local_vars.append(f"unsigned int n_{struct[arg].c_name} = 0;")
ri.cw.block_start()
ri.cw.write_func_lvar(local_vars)
@@ -2146,8 +2163,8 @@ def _multi_parse(ri, struct, init_lines, local_vars):
else:
ri.cw.p('hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));')
ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({struct.fixed_header}));")
for anest in sorted(all_multi):
aspec = struct[anest]
for arg in sorted(all_multi):
aspec = struct[arg]
ri.cw.p(f"if (dst->{aspec.c_name})")
ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
@@ -2165,8 +2182,8 @@ def _multi_parse(ri, struct, init_lines, local_vars):
ri.cw.block_end()
ri.cw.nl()
for anest in sorted(array_nests):
aspec = struct[anest]
for arg in sorted(indexed_arrays):
aspec = struct[arg]
ri.cw.block_start(line=f"if (n_{aspec.c_name})")
ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
@@ -2191,8 +2208,8 @@ def _multi_parse(ri, struct, init_lines, local_vars):
ri.cw.block_end()
ri.cw.nl()
for anest in sorted(multi_attrs):
aspec = struct[anest]
for arg in sorted(multi_attrs):
aspec = struct[arg]
ri.cw.block_start(line=f"if (n_{aspec.c_name})")
ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};")
@@ -2347,10 +2364,7 @@ def print_req(ri):
local_vars += ['size_t hdr_len;',
'void *hdr;']
for _, attr in ri.struct["request"].member_list():
if attr.presence_type() == 'count':
local_vars += ['unsigned int i;']
break
local_vars += put_local_vars(ri.struct['request'])
print_prototype(ri, direction, terminate=False)
ri.cw.block_start()
@@ -2417,6 +2431,9 @@ def print_dump(ri):
local_vars += ['size_t hdr_len;',
'void *hdr;']
if 'request' in ri.op[ri.op_mode]:
local_vars += put_local_vars(ri.struct['request'])
ri.cw.write_func_lvar(local_vars)
ri.cw.p('yds.yarg.ys = ys;')
@@ -3208,8 +3225,9 @@ def render_uapi(family, cw):
cw.block_end(line=';')
cw.nl()
elif const['type'] == 'const':
name_pfx = const.get('name-prefix', f"{family.ident_name}-")
defines.append([c_upper(family.get('c-define-name',
f"{family.ident_name}-{const['name']}")),
f"{name_pfx}{const['name']}")),
const['value']])
if defines: