summaryrefslogtreecommitdiff
path: root/tools/net/ynl/lib/ynl.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/net/ynl/lib/ynl.py')
-rw-r--r--tools/net/ynl/lib/ynl.py175
1 files changed, 135 insertions, 40 deletions
diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 13c4b019a881..1e10512b2117 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -98,11 +98,12 @@ class NlAttr:
}
def __init__(self, raw, offset):
- self._len, self._type = struct.unpack("HH", raw[offset:offset + 4])
+ self._len, self._type = struct.unpack("HH", raw[offset : offset + 4])
self.type = self._type & ~Netlink.NLA_TYPE_MASK
+ self.is_nest = self._type & Netlink.NLA_F_NESTED
self.payload_len = self._len
self.full_len = (self.payload_len + 3) & ~3
- self.raw = raw[offset + 4:offset + self.payload_len]
+ self.raw = raw[offset + 4 : offset + self.payload_len]
@classmethod
def get_format(cls, attr_type, byte_order=None):
@@ -130,6 +131,13 @@ class NlAttr:
format = self.get_format(attr_type, byte_order)
return format.unpack(self.raw)[0]
+ def as_auto_scalar(self, attr_type, byte_order=None):
+ if len(self.raw) != 4 and len(self.raw) != 8:
+ raise Exception(f"Auto-scalar len payload be 4 or 8 bytes, got {len(self.raw)}")
+ real_type = attr_type[0] + str(len(self.raw) * 8)
+ format = self.get_format(real_type, byte_order)
+ return format.unpack(self.raw)[0]
+
def as_strz(self):
return self.raw.decode('ascii')[:-1]
@@ -146,7 +154,7 @@ class NlAttr:
for m in members:
# TODO: handle non-scalar members
if m.type == 'binary':
- decoded = self.raw[offset:offset+m['len']]
+ decoded = self.raw[offset : offset + m['len']]
offset += m['len']
elif m.type in NlAttr.type_formats:
format = self.get_format(m.type, m.byte_order)
@@ -162,10 +170,9 @@ class NlAttr:
class NlAttrs:
- def __init__(self, msg):
+ def __init__(self, msg, offset=0):
self.attrs = []
- offset = 0
while offset < len(msg):
attr = NlAttr(msg, offset)
offset += attr.full_len
@@ -185,12 +192,12 @@ class NlAttrs:
class NlMsg:
def __init__(self, msg, offset, attr_space=None):
- self.hdr = msg[offset:offset + 16]
+ self.hdr = msg[offset : offset + 16]
self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \
struct.unpack("IHHII", self.hdr)
- self.raw = msg[offset + 16:offset + self.nl_len]
+ self.raw = msg[offset + 16 : offset + self.nl_len]
self.error = 0
self.done = 0
@@ -363,8 +370,8 @@ class NetlinkProtocol:
fixed_header_size = 0
if ynl:
op = ynl.rsp_by_value[msg.cmd()]
- fixed_header_size = ynl._fixed_header_size(op)
- msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:])
+ fixed_header_size = ynl._fixed_header_size(op.fixed_header)
+ msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size)
return msg
def get_mcast_id(self, mcast_name, mcast_groups):
@@ -404,10 +411,11 @@ class GenlProtocol(NetlinkProtocol):
class YnlFamily(SpecFamily):
- def __init__(self, def_path, schema=None):
+ def __init__(self, def_path, schema=None, process_unknown=False):
super().__init__(def_path, schema)
self.include_raw = False
+ self.process_unknown = process_unknown
try:
if self.proto == "netlink-raw":
@@ -463,9 +471,16 @@ class YnlFamily(SpecFamily):
attr_payload = bytes.fromhex(value)
else:
raise Exception(f'Unknown type for binary attribute, value: {value}')
+ elif attr.is_auto_scalar:
+ scalar = int(value)
+ real_type = attr["type"][0] + ('32' if scalar.bit_length() <= 32 else '64')
+ format = NlAttr.get_format(real_type, attr.byte_order)
+ attr_payload = format.pack(int(value))
elif attr['type'] in NlAttr.type_formats:
format = NlAttr.get_format(attr['type'], attr.byte_order)
attr_payload = format.pack(int(value))
+ elif attr['type'] in "bitfield32":
+ attr_payload = struct.pack("II", int(value["value"]), int(value["selector"]))
else:
raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}')
@@ -474,7 +489,7 @@ class YnlFamily(SpecFamily):
def _decode_enum(self, raw, attr_spec):
enum = self.consts[attr_spec['enum']]
- if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
+ if enum.type == 'flags' or attr_spec.get('enum-as-flags', False):
i = 0
value = set()
while raw:
@@ -512,14 +527,72 @@ class YnlFamily(SpecFamily):
decoded.append({ item.type: subattrs })
return decoded
+ def _decode_unknown(self, attr):
+ if attr.is_nest:
+ return self._decode(NlAttrs(attr.raw), None)
+ else:
+ return attr.as_bin()
+
+ def _rsp_add(self, rsp, name, is_multi, decoded):
+ if is_multi == None:
+ if name in rsp and type(rsp[name]) is not list:
+ rsp[name] = [rsp[name]]
+ is_multi = True
+ else:
+ is_multi = False
+
+ if not is_multi:
+ rsp[name] = decoded
+ elif name in rsp:
+ rsp[name].append(decoded)
+ else:
+ rsp[name] = [decoded]
+
+ def _resolve_selector(self, attr_spec, vals):
+ sub_msg = attr_spec.sub_message
+ if sub_msg not in self.sub_msgs:
+ raise Exception(f"No sub-message spec named {sub_msg} for {attr_spec.name}")
+ sub_msg_spec = self.sub_msgs[sub_msg]
+
+ selector = attr_spec.selector
+ if selector not in vals:
+ raise Exception(f"There is no value for {selector} to resolve '{attr_spec.name}'")
+ value = vals[selector]
+ if value not in sub_msg_spec.formats:
+ raise Exception(f"No message format for '{value}' in sub-message spec '{sub_msg}'")
+
+ spec = sub_msg_spec.formats[value]
+ return spec
+
+ def _decode_sub_msg(self, attr, attr_spec, rsp):
+ msg_format = self._resolve_selector(attr_spec, rsp)
+ decoded = {}
+ offset = 0
+ if msg_format.fixed_header:
+ decoded.update(self._decode_fixed_header(attr, msg_format.fixed_header));
+ offset = self._fixed_header_size(msg_format.fixed_header)
+ if msg_format.attr_set:
+ if msg_format.attr_set in self.attr_sets:
+ subdict = self._decode(NlAttrs(attr.raw, offset), msg_format.attr_set)
+ decoded.update(subdict)
+ else:
+ raise Exception(f"Unknown attribute-set '{attr_space}' when decoding '{attr_spec.name}'")
+ return decoded
+
def _decode(self, attrs, space):
- attr_space = self.attr_sets[space]
+ if space:
+ attr_space = self.attr_sets[space]
rsp = dict()
for attr in attrs:
try:
attr_spec = attr_space.attrs_by_val[attr.type]
- except KeyError:
- raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
+ except (KeyError, UnboundLocalError):
+ if not self.process_unknown:
+ raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'")
+ attr_name = f"UnknownAttr({attr.type})"
+ self._rsp_add(rsp, attr_name, None, self._decode_unknown(attr))
+ continue
+
if attr_spec["type"] == 'nest':
subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes'])
decoded = subdict
@@ -529,22 +602,28 @@ class YnlFamily(SpecFamily):
decoded = self._decode_binary(attr, attr_spec)
elif attr_spec["type"] == 'flag':
decoded = True
+ elif attr_spec.is_auto_scalar:
+ decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order)
elif attr_spec["type"] in NlAttr.type_formats:
decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order)
+ if 'enum' in attr_spec:
+ decoded = self._decode_enum(decoded, attr_spec)
elif attr_spec["type"] == 'array-nest':
decoded = self._decode_array_nest(attr, attr_spec)
+ elif attr_spec["type"] == 'bitfield32':
+ value, selector = struct.unpack("II", attr.raw)
+ if 'enum' in attr_spec:
+ value = self._decode_enum(value, attr_spec)
+ selector = self._decode_enum(selector, attr_spec)
+ decoded = {"value": value, "selector": selector}
+ elif attr_spec["type"] == 'sub-message':
+ decoded = self._decode_sub_msg(attr, attr_spec, rsp)
else:
- raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
+ if not self.process_unknown:
+ raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
+ decoded = self._decode_unknown(attr)
- if 'enum' in attr_spec:
- decoded = self._decode_enum(decoded, attr_spec)
-
- if not attr_spec.is_multi:
- rsp[attr_spec['name']] = decoded
- elif attr_spec.name in rsp:
- rsp[attr_spec.name].append(decoded)
- else:
- rsp[attr_spec.name] = [decoded]
+ self._rsp_add(rsp, attr_spec["name"], attr_spec.is_multi, decoded)
return rsp
@@ -579,20 +658,23 @@ class YnlFamily(SpecFamily):
return
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set))
- offset = 20 + self._fixed_header_size(op)
+ offset = 20 + self._fixed_header_size(op.fixed_header)
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
extack['bad-attr-offs'])
if path:
del extack['bad-attr-offs']
extack['bad-attr'] = path
- def _fixed_header_size(self, op):
- if op.fixed_header:
- fixed_header_members = self.consts[op.fixed_header].members
+ def _fixed_header_size(self, name):
+ if name:
+ fixed_header_members = self.consts[name].members
size = 0
for m in fixed_header_members:
- format = NlAttr.get_format(m.type, m.byte_order)
- size += format.size
+ if m.type in ['pad', 'binary']:
+ size += m.len
+ else:
+ format = NlAttr.get_format(m.type, m.byte_order)
+ size += format.size
return size
else:
return 0
@@ -602,12 +684,20 @@ class YnlFamily(SpecFamily):
fixed_header_attrs = dict()
offset = 0
for m in fixed_header_members:
- format = NlAttr.get_format(m.type, m.byte_order)
- [ value ] = format.unpack_from(msg.raw, offset)
- offset += format.size
- if m.enum:
- value = self._decode_enum(value, m)
- fixed_header_attrs[m.name] = value
+ value = None
+ if m.type == 'pad':
+ offset += m.len
+ elif m.type == 'binary':
+ value = msg.raw[offset : offset + m.len]
+ offset += m.len
+ else:
+ format = NlAttr.get_format(m.type, m.byte_order)
+ [ value ] = format.unpack_from(msg.raw, offset)
+ offset += format.size
+ if value is not None:
+ if m.enum:
+ value = self._decode_enum(value, m)
+ fixed_header_attrs[m.name] = value
return fixed_header_attrs
def handle_ntf(self, decoded):
@@ -658,7 +748,7 @@ class YnlFamily(SpecFamily):
return op['do']['request']['attributes'].copy()
- def _op(self, method, vals, flags, dump=False):
+ def _op(self, method, vals, flags=None, dump=False):
op = self.ops[method]
nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK
@@ -674,8 +764,13 @@ class YnlFamily(SpecFamily):
fixed_header_members = self.consts[op.fixed_header].members
for m in fixed_header_members:
value = vals.pop(m.name) if m.name in vals else 0
- format = NlAttr.get_format(m.type, m.byte_order)
- msg += format.pack(value)
+ if m.type == 'pad':
+ msg += bytearray(m.len)
+ elif m.type == 'binary':
+ msg += bytes.fromhex(value)
+ else:
+ format = NlAttr.get_format(m.type, m.byte_order)
+ msg += format.pack(value)
for name, value in vals.items():
msg += self._add_attr(op.attr_set.name, name, value)
msg = _genl_msg_finalize(msg)
@@ -722,7 +817,7 @@ class YnlFamily(SpecFamily):
return rsp[0]
return rsp
- def do(self, method, vals, flags):
+ def do(self, method, vals, flags=None):
return self._op(method, vals, flags)
def dump(self, method, vals):