diff --git a/Documentation/netlink/genetlink-c.yaml b/Documentation/netlink/genetlink-c.yaml index 9806c44f604c39e36db3b4fc3be4b9b8d9ffedd4..3ad2338dd4b077e4fe2dba96aaf619fa096abb5d 100644 --- a/Documentation/netlink/genetlink-c.yaml +++ b/Documentation/netlink/genetlink-c.yaml @@ -13,6 +13,11 @@ $defs: type: [ string, integer ] pattern: ^[0-9A-Za-z_]+( - 1)?$ minimum: 0 + len-or-limit: + # literal int or limit based on fixed-width type e.g. u8-min, u16-max, etc. + type: [ string, integer ] + pattern: ^[su](8|16|32|64)-(min|max)$ + minimum: 0 # Schema for specs title: Protocol @@ -187,13 +192,19 @@ properties: type: string min: description: Min value for an integer attribute. - type: integer + $ref: '#/$defs/len-or-limit' + max: + description: Max value for an integer attribute. + $ref: '#/$defs/len-or-limit' min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' max-len: description: Max length for a string or a binary attribute. $ref: '#/$defs/len-or-define' + exact-len: + description: Exact length for a string or a binary attribute. + $ref: '#/$defs/len-or-define' sub-type: *attr-type display-hint: &display-hint description: | diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index 12a0a045605d2248b3103f7fc91f79879719c813..068808045fdf4c9baddcd70b3f50a0d03979bea2 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -13,6 +13,11 @@ $defs: type: [ string, integer ] pattern: ^[0-9A-Za-z_]+( - 1)?$ minimum: 0 + len-or-limit: + # literal int or limit based on fixed-width type e.g. u8-min, u16-max, etc. + type: [ string, integer ] + pattern: ^[su](8|16|32|64)-(min|max)$ + minimum: 0 # Schema for specs title: Protocol @@ -226,13 +231,19 @@ properties: type: string min: description: Min value for an integer attribute. - type: integer + $ref: '#/$defs/len-or-limit' + max: + description: Max value for an integer attribute. + $ref: '#/$defs/len-or-limit' min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' max-len: description: Max length for a string or a binary attribute. $ref: '#/$defs/len-or-define' + exact-len: + description: Exact length for a string or a binary attribute. + $ref: '#/$defs/len-or-define' sub-type: *attr-type display-hint: *display-hint # Start genetlink-c @@ -316,7 +327,7 @@ properties: description: Command flags. type: array items: - enum: [ admin-perm ] + enum: [ admin-perm, uns-admin-perm ] dont-validate: description: Kernel attribute validation flags. type: array diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml index 3d338c48bf21f0050f748a53dc6551362116b3f7..661fe282801a833a73032a20381b3c1438d8ea26 100644 --- a/Documentation/netlink/genetlink.yaml +++ b/Documentation/netlink/genetlink.yaml @@ -13,6 +13,11 @@ $defs: type: [ string, integer ] pattern: ^[0-9A-Za-z_]+( - 1)?$ minimum: 0 + len-or-limit: + # literal int or limit based on fixed-width type e.g. u8-min, u16-max, etc. + type: [ string, integer ] + pattern: ^[su](8|16|32|64)-(min|max)$ + minimum: 0 # Schema for specs title: Protocol @@ -160,13 +165,19 @@ properties: type: string min: description: Min value for an integer attribute. - type: integer + $ref: '#/$defs/len-or-limit' + max: + description: Max value for an integer attribute. + $ref: '#/$defs/len-or-limit' min-len: description: Min length for a binary attribute. $ref: '#/$defs/len-or-define' max-len: description: Max length for a string or a binary attribute. $ref: '#/$defs/len-or-define' + exact-len: + description: Exact length for a string or a binary attribute. + $ref: '#/$defs/len-or-define' sub-type: *attr-type display-hint: &display-hint description: | diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml index 89679787641469ee899eab03039a6f0170ac2f41..96913857ac714c6aaea16fc9a5a604d134fa7280 100644 --- a/Documentation/netlink/netlink-raw.yaml +++ b/Documentation/netlink/netlink-raw.yaml @@ -240,6 +240,9 @@ properties: max-len: description: Max length for a string or a binary attribute. $ref: '#/$defs/len-or-define' + exact-len: + description: Exact length for a string or a binary attribute. + $ref: '#/$defs/len-or-define' sub-type: *attr-type display-hint: *display-hint # Start genetlink-c diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index 575b7e248e521277d5f580f318298be77e22edf7..db918a10d506c7061a3094d68b0fbe19e336390f 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -20,6 +20,21 @@ def c_lower(name): return name.lower().replace('-', '_') +def limit_to_number(name): + """ + Turn a string limit like u32-max or s64-min into its numerical value + """ + if name[0] == 'u' and name.endswith('-min'): + return 0 + width = int(name[1:-4]) + if name[0] == 's': + width -= 1 + value = (1 << width) - 1 + if name[0] == 's' and name.endswith('-min'): + value = -value - 1 + return value + + class BaseNlLib: def get_family_id(self): return 'ys->family_id' @@ -42,8 +57,12 @@ class Type(SpecAttr): self.type = attr['type'] self.checks = attr.get('checks', {}) + self.request = False + self.reply = False + if 'len' in attr: self.len = attr['len'] + if 'nested-attributes' in attr: self.nested_attrs = attr['nested-attributes'] if self.nested_attrs == family.name: @@ -64,6 +83,14 @@ class Type(SpecAttr): self.enum_name = None delattr(self, "enum_name") + def get_limit(self, limit, default=None): + value = self.checks.get(limit, default) + if value is None: + return value + if not isinstance(value, int): + value = limit_to_number(value) + return value + def resolve(self): if 'name-prefix' in self.attr: enum_name = f"{self.attr['name-prefix']}{self.name}" @@ -262,6 +289,27 @@ class TypeScalar(Type): if 'byte-order' in attr: self.byte_order_comment = f" /* {attr['byte-order']} */" + if 'enum' in self.attr: + enum = self.family.consts[self.attr['enum']] + low, high = enum.value_range() + if 'min' not in self.checks: + if low != 0 or self.type[0] == 's': + self.checks['min'] = low + if 'max' not in self.checks: + self.checks['max'] = high + + if 'min' in self.checks and 'max' in self.checks: + if self.get_limit('min') > self.get_limit('max'): + raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}') + self.checks['range'] = True + + low = min(self.get_limit('min', 0), self.get_limit('max', 0)) + high = max(self.get_limit('min', 0), self.get_limit('max', 0)) + if low < 0 and self.type[0] == 'u': + raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type') + if low < -32768 or high > 32767: + self.checks['full-range'] = True + # Added by resolve(): self.is_bitfield = None delattr(self, "is_bitfield") @@ -301,14 +349,14 @@ class TypeScalar(Type): flag_cnt = len(flags['entries']) mask = (1 << flag_cnt) - 1 return f"NLA_POLICY_MASK({policy}, 0x{mask:x})" + elif 'full-range' in self.checks: + return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)" + elif 'range' in self.checks: + return f"NLA_POLICY_RANGE({policy}, {self.get_limit('min')}, {self.get_limit('max')})" elif 'min' in self.checks: - return f"NLA_POLICY_MIN({policy}, {self.checks['min']})" - elif 'enum' in self.attr: - enum = self.family.consts[self.attr['enum']] - low, high = enum.value_range() - if low == 0: - return f"NLA_POLICY_MAX({policy}, {high})" - return f"NLA_POLICY_RANGE({policy}, {low}, {high})" + return f"NLA_POLICY_MIN({policy}, {self.get_limit('min')})" + elif 'max' in self.checks: + return f"NLA_POLICY_MAX({policy}, {self.get_limit('max')})" return super()._attr_policy(policy) def _attr_typol(self): @@ -358,10 +406,13 @@ class TypeString(Type): return f'.type = YNL_PT_NUL_STR, ' def _attr_policy(self, policy): - mem = '{ .type = ' + policy - if 'max-len' in self.checks: - mem += ', .len = ' + str(self.checks['max-len']) - mem += ', }' + if 'exact-len' in self.checks: + mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')' + else: + mem = '{ .type = ' + policy + if 'max-len' in self.checks: + mem += ', .len = ' + str(self.get_limit('max-len')) + mem += ', }' return mem def attr_policy(self, cw): @@ -407,14 +458,17 @@ class TypeBinary(Type): return f'.type = YNL_PT_BINARY,' def _attr_policy(self, policy): - mem = '{ ' - if len(self.checks) == 1 and 'min-len' in self.checks: - mem += '.len = ' + str(self.checks['min-len']) - elif len(self.checks) == 0: - mem += '.type = NLA_BINARY' + if 'exact-len' in self.checks: + mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')' else: - raise Exception('One or more of binary type checks not implemented, yet') - mem += ', }' + mem = '{ ' + if len(self.checks) == 1 and 'min-len' in self.checks: + mem += '.len = ' + str(self.get_limit('min-len')) + elif len(self.checks) == 0: + mem += '.type = NLA_BINARY' + else: + raise Exception('One or more of binary type checks not implemented, yet') + mem += ', }' return mem def attr_put(self, ri, var): @@ -845,6 +899,7 @@ class Family(SpecFamily): self._load_root_sets() self._load_nested_sets() + self._load_attr_use() self._load_hooks() self.kernel_policy = self.yaml.get('kernel-policy', 'split') @@ -965,6 +1020,22 @@ class Family(SpecFamily): child.request |= struct.request child.reply |= struct.reply + def _load_attr_use(self): + for _, struct in self.pure_nested_structs.items(): + if struct.request: + for _, arg in struct.member_list(): + arg.request = True + if struct.reply: + for _, arg in struct.member_list(): + arg.reply = True + + for root_set, rs_members in self.root_sets.items(): + for attr, spec in self.attr_sets[root_set].items(): + if attr in rs_members['request']: + spec.request = True + if attr in rs_members['reply']: + spec.reply = True + def _load_global_policy(self): global_set = set() attr_set_name = None @@ -1218,7 +1289,7 @@ class CodeWriter: for one in members: line = '.' + one[0] line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8) - line += '= ' + one[1] + ',' + line += '= ' + str(one[1]) + ',' self.p(line) @@ -1913,6 +1984,34 @@ def policy_should_be_static(family): return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family) +def print_kernel_policy_ranges(family, cw): + first = True + for _, attr_set in family.attr_sets.items(): + if attr_set.subset_of: + continue + + for _, attr in attr_set.items(): + if not attr.request: + continue + if 'full-range' not in attr.checks: + continue + + if first: + cw.p('/* Integer value ranges */') + first = False + + sign = '' if attr.type[0] == 'u' else '_signed' + cw.block_start(line=f'struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =') + members = [] + if 'min' in attr.checks: + members.append(('min', attr.get_limit('min'))) + if 'max' in attr.checks: + members.append(('max', attr.get_limit('max'))) + cw.write_struct_init(members) + cw.block_end(line=';') + cw.nl() + + def print_kernel_op_table_fwd(family, cw, terminate): exported = not kernel_can_gen_family_struct(family) @@ -2452,6 +2551,8 @@ def main(): print_kernel_mcgrp_hdr(parsed, cw) print_kernel_family_struct_hdr(parsed, cw) else: + print_kernel_policy_ranges(parsed, cw) + for _, struct in sorted(parsed.pure_nested_structs.items()): if struct.request: cw.p('/* Common nested types */')