Merge branch 'netlink-add-display-hint-to-ynl'

Donald Hunter says:

====================
netlink: add display-hint to ynl

Add a display-hint property to the netlink schema, to be used by generic
netlink clients as hints about how to display attribute values.

A display-hint on an attribute definition is intended for letting a
client such as ynl know that, for example, a u32 should be rendered as
an ipv4 address. The display-hint enumeration includes a small number of
networking domain-specific value types.
====================

Link: https://lore.kernel.org/r/20230623201928.14275-1-donald.hunter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2023-06-24 15:45:51 -07:00
commit 35bf34b078
6 changed files with 168 additions and 6 deletions

View file

@ -195,6 +195,12 @@ properties:
description: Max length for a string or a binary attribute. description: Max length for a string or a binary attribute.
$ref: '#/$defs/len-or-define' $ref: '#/$defs/len-or-define'
sub-type: *attr-type sub-type: *attr-type
display-hint: &display-hint
description: |
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 ]
# Start genetlink-c # Start genetlink-c
name-prefix: name-prefix:
type: string type: string

View file

@ -119,7 +119,8 @@ properties:
name: name:
type: string type: string
type: type:
enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string ] description: The netlink attribute type
enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string, binary ]
len: len:
$ref: '#/$defs/len-or-define' $ref: '#/$defs/len-or-define'
byte-order: byte-order:
@ -130,6 +131,12 @@ properties:
enum: enum:
description: Name of the enum type used for the attribute. description: Name of the enum type used for the attribute.
type: string type: string
display-hint: &display-hint
description: |
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 ]
# End genetlink-legacy # End genetlink-legacy
attribute-sets: attribute-sets:
@ -179,6 +186,7 @@ properties:
name: name:
type: string type: string
type: &attr-type type: &attr-type
description: The netlink attribute type
enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64, enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64,
string, nest, array-nest, nest-type-value ] string, nest, array-nest, nest-type-value ]
doc: doc:
@ -226,6 +234,7 @@ properties:
description: Max length for a string or a binary attribute. description: Max length for a string or a binary attribute.
$ref: '#/$defs/len-or-define' $ref: '#/$defs/len-or-define'
sub-type: *attr-type sub-type: *attr-type
display-hint: *display-hint
# Start genetlink-c # Start genetlink-c
name-prefix: name-prefix:
type: string type: string

View file

@ -168,6 +168,12 @@ properties:
description: Max length for a string or a binary attribute. description: Max length for a string or a binary attribute.
$ref: '#/$defs/len-or-define' $ref: '#/$defs/len-or-define'
sub-type: *attr-type sub-type: *attr-type
display-hint: &display-hint
description: |
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 ]
# Make sure name-prefix does not appear in subsets (subsets inherit naming) # Make sure name-prefix does not appear in subsets (subsets inherit naming)
dependencies: dependencies:

View file

@ -33,6 +33,20 @@ definitions:
name: n-bytes name: n-bytes
type: u64 type: u64
doc: Number of matched bytes. doc: Number of matched bytes.
-
name: ovs-key-ethernet
type: struct
members:
-
name: eth-src
type: binary
len: 6
display-hint: mac
-
name: eth-dst
type: binary
len: 6
display-hint: mac
- -
name: ovs-key-mpls name: ovs-key-mpls
type: struct type: struct
@ -49,10 +63,12 @@ definitions:
name: ipv4-src name: ipv4-src
type: u32 type: u32
byte-order: big-endian byte-order: big-endian
display-hint: ipv4
- -
name: ipv4-dst name: ipv4-dst
type: u32 type: u32
byte-order: big-endian byte-order: big-endian
display-hint: ipv4
- -
name: ipv4-proto name: ipv4-proto
type: u8 type: u8
@ -66,6 +82,45 @@ definitions:
name: ipv4-frag name: ipv4-frag
type: u8 type: u8
enum: ovs-frag-type enum: ovs-frag-type
-
name: ovs-key-ipv6
type: struct
members:
-
name: ipv6-src
type: binary
len: 16
byte-order: big-endian
display-hint: ipv6
-
name: ipv6-dst
type: binary
len: 16
byte-order: big-endian
display-hint: ipv6
-
name: ipv6-label
type: u32
byte-order: big-endian
-
name: ipv6-proto
type: u8
-
name: ipv6-tclass
type: u8
-
name: ipv6-hlimit
type: u8
-
name: ipv6-frag
type: u8
-
name: ovs-key-ipv6-exthdrs
type: struct
members:
-
name: hdrs
type: u16
- -
name: ovs-frag-type name: ovs-frag-type
name-prefix: ovs-frag-type- name-prefix: ovs-frag-type-
@ -129,6 +184,51 @@ definitions:
- -
name: icmp-code name: icmp-code
type: u8 type: u8
-
name: ovs-key-arp
type: struct
members:
-
name: arp-sip
type: u32
byte-order: big-endian
-
name: arp-tip
type: u32
byte-order: big-endian
-
name: arp-op
type: u16
byte-order: big-endian
-
name: arp-sha
type: binary
len: 6
display-hint: mac
-
name: arp-tha
type: binary
len: 6
display-hint: mac
-
name: ovs-key-nd
type: struct
members:
-
name: nd_target
type: binary
len: 16
byte-order: big-endian
-
name: nd-sll
type: binary
len: 6
display-hint: mac
-
name: nd-tll
type: binary
len: 6
display-hint: mac
- -
name: ovs-key-ct-tuple-ipv4 name: ovs-key-ct-tuple-ipv4
type: struct type: struct
@ -345,6 +445,7 @@ attribute-sets:
value of the OVS_FLOW_ATTR_KEY attribute. Optional for all value of the OVS_FLOW_ATTR_KEY attribute. Optional for all
requests. Present in notifications if the flow was created with this requests. Present in notifications if the flow was created with this
attribute. attribute.
display-hint: uuid
- -
name: ufid-flags name: ufid-flags
type: u32 type: u32
@ -374,6 +475,7 @@ attribute-sets:
- -
name: ethernet name: ethernet
type: binary type: binary
struct: ovs-key-ethernet
doc: struct ovs_key_ethernet doc: struct ovs_key_ethernet
- -
name: vlan name: vlan
@ -390,6 +492,7 @@ attribute-sets:
- -
name: ipv6 name: ipv6
type: binary type: binary
struct: ovs-key-ipv6
doc: struct ovs_key_ipv6 doc: struct ovs_key_ipv6
- -
name: tcp name: tcp
@ -410,10 +513,12 @@ attribute-sets:
- -
name: arp name: arp
type: binary type: binary
struct: ovs-key-arp
doc: struct ovs_key_arp doc: struct ovs_key_arp
- -
name: nd name: nd
type: binary type: binary
struct: ovs-key-nd
doc: struct ovs_key_nd doc: struct ovs_key_nd
- -
name: skb-mark name: skb-mark
@ -457,6 +562,7 @@ attribute-sets:
- -
name: ct-labels name: ct-labels
type: binary type: binary
display-hint: hex
doc: 16-octet connection tracking label doc: 16-octet connection tracking label
- -
name: ct-orig-tuple-ipv4 name: ct-orig-tuple-ipv4
@ -486,6 +592,7 @@ attribute-sets:
- -
name: ipv6-exthdrs name: ipv6-exthdrs
type: binary type: binary
struct: ovs-key-ipv6-exthdrs
doc: struct ovs_key_ipv6_exthdr doc: struct ovs_key_ipv6_exthdr
- -
name: action-attrs name: action-attrs

View file

@ -154,6 +154,9 @@ class SpecAttr(SpecElement):
is_multi bool, attr may repeat multiple times is_multi bool, attr may repeat multiple times
struct_name string, name of struct definition struct_name string, name of struct definition
sub_type string, name of sub type sub_type string, name of sub type
len integer, optional byte length of binary types
display_hint string, hint to help choose format specifier
when displaying the value
""" """
def __init__(self, family, attr_set, yaml, value): def __init__(self, family, attr_set, yaml, value):
super().__init__(family, yaml) super().__init__(family, yaml)
@ -164,6 +167,8 @@ class SpecAttr(SpecElement):
self.struct_name = yaml.get('struct') self.struct_name = yaml.get('struct')
self.sub_type = yaml.get('sub-type') self.sub_type = yaml.get('sub-type')
self.byte_order = yaml.get('byte-order') self.byte_order = yaml.get('byte-order')
self.len = yaml.get('len')
self.display_hint = yaml.get('display-hint')
class SpecAttrSet(SpecElement): class SpecAttrSet(SpecElement):
@ -229,12 +234,17 @@ class SpecStructMember(SpecElement):
type string, type of the member attribute type string, type of the member attribute
byte_order string or None for native byte order byte_order string or None for native byte order
enum string, name of the enum definition enum string, name of the enum definition
len integer, optional byte length of binary types
display_hint string, hint to help choose format specifier
when displaying the value
""" """
def __init__(self, family, yaml): def __init__(self, family, yaml):
super().__init__(family, yaml) super().__init__(family, yaml)
self.type = yaml['type'] self.type = yaml['type']
self.byte_order = yaml.get('byte-order') self.byte_order = yaml.get('byte-order')
self.enum = yaml.get('enum') self.enum = yaml.get('enum')
self.len = yaml.get('len')
self.display_hint = yaml.get('display-hint')
class SpecStruct(SpecElement): class SpecStruct(SpecElement):

View file

@ -8,6 +8,8 @@ import socket
import struct import struct
from struct import Struct from struct import Struct
import yaml import yaml
import ipaddress
import uuid
from .nlspec import SpecFamily from .nlspec import SpecFamily
@ -105,6 +107,20 @@ class NlAttr:
else format.little else format.little
return format.native return format.native
@classmethod
def formatted_string(cls, raw, display_hint):
if display_hint == 'mac':
formatted = ':'.join('%02x' % b for b in raw)
elif display_hint == 'hex':
formatted = bytes.hex(raw, ' ')
elif display_hint in [ 'ipv4', 'ipv6' ]:
formatted = format(ipaddress.ip_address(raw))
elif display_hint == 'uuid':
formatted = str(uuid.UUID(bytes=raw))
else:
formatted = raw
return formatted
def as_scalar(self, attr_type, byte_order=None): def as_scalar(self, attr_type, byte_order=None):
format = self.get_format(attr_type, byte_order) format = self.get_format(attr_type, byte_order)
return format.unpack(self.raw)[0] return format.unpack(self.raw)[0]
@ -124,10 +140,16 @@ class NlAttr:
offset = 0 offset = 0
for m in members: for m in members:
# TODO: handle non-scalar members # TODO: handle non-scalar members
format = self.get_format(m.type, m.byte_order) if m.type == 'binary':
decoded = format.unpack_from(self.raw, offset) decoded = self.raw[offset:offset+m['len']]
offset += format.size offset += m['len']
value[m.name] = decoded[0] elif m.type in NlAttr.type_formats:
format = self.get_format(m.type, m.byte_order)
[ decoded ] = format.unpack_from(self.raw, offset)
offset += format.size
if m.display_hint:
decoded = self.formatted_string(decoded, m.display_hint)
value[m.name] = decoded
return value return value
def __repr__(self): def __repr__(self):
@ -385,7 +407,7 @@ class YnlFamily(SpecFamily):
elif attr["type"] == 'string': elif attr["type"] == 'string':
attr_payload = str(value).encode('ascii') + b'\x00' attr_payload = str(value).encode('ascii') + b'\x00'
elif attr["type"] == 'binary': elif attr["type"] == 'binary':
attr_payload = value attr_payload = bytes.fromhex(value)
elif attr['type'] in NlAttr.type_formats: elif attr['type'] in NlAttr.type_formats:
format = NlAttr.get_format(attr['type'], attr.byte_order) format = NlAttr.get_format(attr['type'], attr.byte_order)
attr_payload = format.pack(int(value)) attr_payload = format.pack(int(value))
@ -421,6 +443,8 @@ class YnlFamily(SpecFamily):
decoded = attr.as_c_array(attr_spec.sub_type) decoded = attr.as_c_array(attr_spec.sub_type)
else: else:
decoded = attr.as_bin() decoded = attr.as_bin()
if attr_spec.display_hint:
decoded = NlAttr.formatted_string(decoded, attr_spec.display_hint)
return decoded return decoded
def _decode(self, attrs, space): def _decode(self, attrs, space):