1# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 3from collections import namedtuple 4import functools 5import os 6import random 7import socket 8import struct 9from struct import Struct 10import yaml 11import ipaddress 12import uuid 13 14from .nlspec import SpecFamily 15 16# 17# Generic Netlink code which should really be in some library, but I can't quickly find one. 18# 19 20 21class Netlink: 22 # Netlink socket 23 SOL_NETLINK = 270 24 25 NETLINK_ADD_MEMBERSHIP = 1 26 NETLINK_CAP_ACK = 10 27 NETLINK_EXT_ACK = 11 28 NETLINK_GET_STRICT_CHK = 12 29 30 # Netlink message 31 NLMSG_ERROR = 2 32 NLMSG_DONE = 3 33 34 NLM_F_REQUEST = 1 35 NLM_F_ACK = 4 36 NLM_F_ROOT = 0x100 37 NLM_F_MATCH = 0x200 38 39 NLM_F_REPLACE = 0x100 40 NLM_F_EXCL = 0x200 41 NLM_F_CREATE = 0x400 42 NLM_F_APPEND = 0x800 43 44 NLM_F_CAPPED = 0x100 45 NLM_F_ACK_TLVS = 0x200 46 47 NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH 48 49 NLA_F_NESTED = 0x8000 50 NLA_F_NET_BYTEORDER = 0x4000 51 52 NLA_TYPE_MASK = NLA_F_NESTED | NLA_F_NET_BYTEORDER 53 54 # Genetlink defines 55 NETLINK_GENERIC = 16 56 57 GENL_ID_CTRL = 0x10 58 59 # nlctrl 60 CTRL_CMD_GETFAMILY = 3 61 62 CTRL_ATTR_FAMILY_ID = 1 63 CTRL_ATTR_FAMILY_NAME = 2 64 CTRL_ATTR_MAXATTR = 5 65 CTRL_ATTR_MCAST_GROUPS = 7 66 67 CTRL_ATTR_MCAST_GRP_NAME = 1 68 CTRL_ATTR_MCAST_GRP_ID = 2 69 70 # Extack types 71 NLMSGERR_ATTR_MSG = 1 72 NLMSGERR_ATTR_OFFS = 2 73 NLMSGERR_ATTR_COOKIE = 3 74 NLMSGERR_ATTR_POLICY = 4 75 NLMSGERR_ATTR_MISS_TYPE = 5 76 NLMSGERR_ATTR_MISS_NEST = 6 77 78 79class NlError(Exception): 80 def __init__(self, nl_msg): 81 self.nl_msg = nl_msg 82 83 def __str__(self): 84 return f"Netlink error: {os.strerror(-self.nl_msg.error)}\n{self.nl_msg}" 85 86 87class NlAttr: 88 ScalarFormat = namedtuple('ScalarFormat', ['native', 'big', 'little']) 89 type_formats = { 90 'u8' : ScalarFormat(Struct('B'), Struct("B"), Struct("B")), 91 's8' : ScalarFormat(Struct('b'), Struct("b"), Struct("b")), 92 'u16': ScalarFormat(Struct('H'), Struct(">H"), Struct("<H")), 93 's16': ScalarFormat(Struct('h'), Struct(">h"), Struct("<h")), 94 'u32': ScalarFormat(Struct('I'), Struct(">I"), Struct("<I")), 95 's32': ScalarFormat(Struct('i'), Struct(">i"), Struct("<i")), 96 'u64': ScalarFormat(Struct('Q'), Struct(">Q"), Struct("<Q")), 97 's64': ScalarFormat(Struct('q'), Struct(">q"), Struct("<q")) 98 } 99 100 def __init__(self, raw, offset): 101 self._len, self._type = struct.unpack("HH", raw[offset:offset + 4]) 102 self.type = self._type & ~Netlink.NLA_TYPE_MASK 103 self.payload_len = self._len 104 self.full_len = (self.payload_len + 3) & ~3 105 self.raw = raw[offset + 4:offset + self.payload_len] 106 107 @classmethod 108 def get_format(cls, attr_type, byte_order=None): 109 format = cls.type_formats[attr_type] 110 if byte_order: 111 return format.big if byte_order == "big-endian" \ 112 else format.little 113 return format.native 114 115 @classmethod 116 def formatted_string(cls, raw, display_hint): 117 if display_hint == 'mac': 118 formatted = ':'.join('%02x' % b for b in raw) 119 elif display_hint == 'hex': 120 formatted = bytes.hex(raw, ' ') 121 elif display_hint in [ 'ipv4', 'ipv6' ]: 122 formatted = format(ipaddress.ip_address(raw)) 123 elif display_hint == 'uuid': 124 formatted = str(uuid.UUID(bytes=raw)) 125 else: 126 formatted = raw 127 return formatted 128 129 def as_scalar(self, attr_type, byte_order=None): 130 format = self.get_format(attr_type, byte_order) 131 return format.unpack(self.raw)[0] 132 133 def as_strz(self): 134 return self.raw.decode('ascii')[:-1] 135 136 def as_bin(self): 137 return self.raw 138 139 def as_c_array(self, type): 140 format = self.get_format(type) 141 return [ x[0] for x in format.iter_unpack(self.raw) ] 142 143 def as_struct(self, members): 144 value = dict() 145 offset = 0 146 for m in members: 147 # TODO: handle non-scalar members 148 if m.type == 'binary': 149 decoded = self.raw[offset:offset+m['len']] 150 offset += m['len'] 151 elif m.type in NlAttr.type_formats: 152 format = self.get_format(m.type, m.byte_order) 153 [ decoded ] = format.unpack_from(self.raw, offset) 154 offset += format.size 155 if m.display_hint: 156 decoded = self.formatted_string(decoded, m.display_hint) 157 value[m.name] = decoded 158 return value 159 160 def __repr__(self): 161 return f"[type:{self.type} len:{self._len}] {self.raw}" 162 163 164class NlAttrs: 165 def __init__(self, msg): 166 self.attrs = [] 167 168 offset = 0 169 while offset < len(msg): 170 attr = NlAttr(msg, offset) 171 offset += attr.full_len 172 self.attrs.append(attr) 173 174 def __iter__(self): 175 yield from self.attrs 176 177 def __repr__(self): 178 msg = '' 179 for a in self.attrs: 180 if msg: 181 msg += '\n' 182 msg += repr(a) 183 return msg 184 185 186class NlMsg: 187 def __init__(self, msg, offset, attr_space=None): 188 self.hdr = msg[offset:offset + 16] 189 190 self.nl_len, self.nl_type, self.nl_flags, self.nl_seq, self.nl_portid = \ 191 struct.unpack("IHHII", self.hdr) 192 193 self.raw = msg[offset + 16:offset + self.nl_len] 194 195 self.error = 0 196 self.done = 0 197 198 extack_off = None 199 if self.nl_type == Netlink.NLMSG_ERROR: 200 self.error = struct.unpack("i", self.raw[0:4])[0] 201 self.done = 1 202 extack_off = 20 203 elif self.nl_type == Netlink.NLMSG_DONE: 204 self.done = 1 205 extack_off = 4 206 207 self.extack = None 208 if self.nl_flags & Netlink.NLM_F_ACK_TLVS and extack_off: 209 self.extack = dict() 210 extack_attrs = NlAttrs(self.raw[extack_off:]) 211 for extack in extack_attrs: 212 if extack.type == Netlink.NLMSGERR_ATTR_MSG: 213 self.extack['msg'] = extack.as_strz() 214 elif extack.type == Netlink.NLMSGERR_ATTR_MISS_TYPE: 215 self.extack['miss-type'] = extack.as_scalar('u32') 216 elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST: 217 self.extack['miss-nest'] = extack.as_scalar('u32') 218 elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: 219 self.extack['bad-attr-offs'] = extack.as_scalar('u32') 220 else: 221 if 'unknown' not in self.extack: 222 self.extack['unknown'] = [] 223 self.extack['unknown'].append(extack) 224 225 if attr_space: 226 # We don't have the ability to parse nests yet, so only do global 227 if 'miss-type' in self.extack and 'miss-nest' not in self.extack: 228 miss_type = self.extack['miss-type'] 229 if miss_type in attr_space.attrs_by_val: 230 spec = attr_space.attrs_by_val[miss_type] 231 desc = spec['name'] 232 if 'doc' in spec: 233 desc += f" ({spec['doc']})" 234 self.extack['miss-type'] = desc 235 236 def cmd(self): 237 return self.nl_type 238 239 def __repr__(self): 240 msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n" 241 if self.error: 242 msg += '\terror: ' + str(self.error) 243 if self.extack: 244 msg += '\textack: ' + repr(self.extack) 245 return msg 246 247 248class NlMsgs: 249 def __init__(self, data, attr_space=None): 250 self.msgs = [] 251 252 offset = 0 253 while offset < len(data): 254 msg = NlMsg(data, offset, attr_space=attr_space) 255 offset += msg.nl_len 256 self.msgs.append(msg) 257 258 def __iter__(self): 259 yield from self.msgs 260 261 262genl_family_name_to_id = None 263 264 265def _genl_msg(nl_type, nl_flags, genl_cmd, genl_version, seq=None): 266 # we prepend length in _genl_msg_finalize() 267 if seq is None: 268 seq = random.randint(1, 1024) 269 nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 270 genlmsg = struct.pack("BBH", genl_cmd, genl_version, 0) 271 return nlmsg + genlmsg 272 273 274def _genl_msg_finalize(msg): 275 return struct.pack("I", len(msg) + 4) + msg 276 277 278def _genl_load_families(): 279 with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, Netlink.NETLINK_GENERIC) as sock: 280 sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 281 282 msg = _genl_msg(Netlink.GENL_ID_CTRL, 283 Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK | Netlink.NLM_F_DUMP, 284 Netlink.CTRL_CMD_GETFAMILY, 1) 285 msg = _genl_msg_finalize(msg) 286 287 sock.send(msg, 0) 288 289 global genl_family_name_to_id 290 genl_family_name_to_id = dict() 291 292 while True: 293 reply = sock.recv(128 * 1024) 294 nms = NlMsgs(reply) 295 for nl_msg in nms: 296 if nl_msg.error: 297 print("Netlink error:", nl_msg.error) 298 return 299 if nl_msg.done: 300 return 301 302 gm = GenlMsg(nl_msg) 303 fam = dict() 304 for attr in NlAttrs(gm.raw): 305 if attr.type == Netlink.CTRL_ATTR_FAMILY_ID: 306 fam['id'] = attr.as_scalar('u16') 307 elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME: 308 fam['name'] = attr.as_strz() 309 elif attr.type == Netlink.CTRL_ATTR_MAXATTR: 310 fam['maxattr'] = attr.as_scalar('u32') 311 elif attr.type == Netlink.CTRL_ATTR_MCAST_GROUPS: 312 fam['mcast'] = dict() 313 for entry in NlAttrs(attr.raw): 314 mcast_name = None 315 mcast_id = None 316 for entry_attr in NlAttrs(entry.raw): 317 if entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_NAME: 318 mcast_name = entry_attr.as_strz() 319 elif entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_ID: 320 mcast_id = entry_attr.as_scalar('u32') 321 if mcast_name and mcast_id is not None: 322 fam['mcast'][mcast_name] = mcast_id 323 if 'name' in fam and 'id' in fam: 324 genl_family_name_to_id[fam['name']] = fam 325 326 327class GenlMsg: 328 def __init__(self, nl_msg): 329 self.nl = nl_msg 330 self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0) 331 self.raw = nl_msg.raw[4:] 332 333 def cmd(self): 334 return self.genl_cmd 335 336 def __repr__(self): 337 msg = repr(self.nl) 338 msg += f"\tgenl_cmd = {self.genl_cmd} genl_ver = {self.genl_version}\n" 339 for a in self.raw_attrs: 340 msg += '\t\t' + repr(a) + '\n' 341 return msg 342 343 344class NetlinkProtocol: 345 def __init__(self, family_name, proto_num): 346 self.family_name = family_name 347 self.proto_num = proto_num 348 349 def _message(self, nl_type, nl_flags, seq=None): 350 if seq is None: 351 seq = random.randint(1, 1024) 352 nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 353 return nlmsg 354 355 def message(self, flags, command, version, seq=None): 356 return self._message(command, flags, seq) 357 358 def _decode(self, nl_msg): 359 return nl_msg 360 361 def decode(self, ynl, nl_msg): 362 msg = self._decode(nl_msg) 363 fixed_header_size = 0 364 if ynl: 365 op = ynl.rsp_by_value[msg.cmd()] 366 fixed_header_size = ynl._fixed_header_size(op) 367 msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:]) 368 return msg 369 370 def get_mcast_id(self, mcast_name, mcast_groups): 371 if mcast_name not in mcast_groups: 372 raise Exception(f'Multicast group "{mcast_name}" not present in the spec') 373 return mcast_groups[mcast_name].value 374 375 376class GenlProtocol(NetlinkProtocol): 377 def __init__(self, family_name): 378 super().__init__(family_name, Netlink.NETLINK_GENERIC) 379 380 global genl_family_name_to_id 381 if genl_family_name_to_id is None: 382 _genl_load_families() 383 384 self.genl_family = genl_family_name_to_id[family_name] 385 self.family_id = genl_family_name_to_id[family_name]['id'] 386 387 def message(self, flags, command, version, seq=None): 388 nlmsg = self._message(self.family_id, flags, seq) 389 genlmsg = struct.pack("BBH", command, version, 0) 390 return nlmsg + genlmsg 391 392 def _decode(self, nl_msg): 393 return GenlMsg(nl_msg) 394 395 def get_mcast_id(self, mcast_name, mcast_groups): 396 if mcast_name not in self.genl_family['mcast']: 397 raise Exception(f'Multicast group "{mcast_name}" not present in the family') 398 return self.genl_family['mcast'][mcast_name] 399 400 401# 402# YNL implementation details. 403# 404 405 406class YnlFamily(SpecFamily): 407 def __init__(self, def_path, schema=None): 408 super().__init__(def_path, schema) 409 410 self.include_raw = False 411 412 try: 413 if self.proto == "netlink-raw": 414 self.nlproto = NetlinkProtocol(self.yaml['name'], 415 self.yaml['protonum']) 416 else: 417 self.nlproto = GenlProtocol(self.yaml['name']) 418 except KeyError: 419 raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel") 420 421 self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.nlproto.proto_num) 422 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 423 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1) 424 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_GET_STRICT_CHK, 1) 425 426 self.async_msg_ids = set() 427 self.async_msg_queue = [] 428 429 for msg in self.msgs.values(): 430 if msg.is_async: 431 self.async_msg_ids.add(msg.rsp_value) 432 433 for op_name, op in self.ops.items(): 434 bound_f = functools.partial(self._op, op_name) 435 setattr(self, op.ident_name, bound_f) 436 437 438 def ntf_subscribe(self, mcast_name): 439 mcast_id = self.nlproto.get_mcast_id(mcast_name, self.mcast_groups) 440 self.sock.bind((0, 0)) 441 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, 442 mcast_id) 443 444 def _add_attr(self, space, name, value): 445 try: 446 attr = self.attr_sets[space][name] 447 except KeyError: 448 raise Exception(f"Space '{space}' has no attribute '{name}'") 449 nl_type = attr.value 450 if attr["type"] == 'nest': 451 nl_type |= Netlink.NLA_F_NESTED 452 attr_payload = b'' 453 for subname, subvalue in value.items(): 454 attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue) 455 elif attr["type"] == 'flag': 456 attr_payload = b'' 457 elif attr["type"] == 'string': 458 attr_payload = str(value).encode('ascii') + b'\x00' 459 elif attr["type"] == 'binary': 460 if isinstance(value, bytes): 461 attr_payload = value 462 elif isinstance(value, str): 463 attr_payload = bytes.fromhex(value) 464 else: 465 raise Exception(f'Unknown type for binary attribute, value: {value}') 466 elif attr['type'] in NlAttr.type_formats: 467 format = NlAttr.get_format(attr['type'], attr.byte_order) 468 attr_payload = format.pack(int(value)) 469 else: 470 raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}') 471 472 pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4) 473 return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad 474 475 def _decode_enum(self, raw, attr_spec): 476 enum = self.consts[attr_spec['enum']] 477 if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']: 478 i = 0 479 value = set() 480 while raw: 481 if raw & 1: 482 value.add(enum.entries_by_val[i].name) 483 raw >>= 1 484 i += 1 485 else: 486 value = enum.entries_by_val[raw].name 487 return value 488 489 def _decode_binary(self, attr, attr_spec): 490 if attr_spec.struct_name: 491 members = self.consts[attr_spec.struct_name] 492 decoded = attr.as_struct(members) 493 for m in members: 494 if m.enum: 495 decoded[m.name] = self._decode_enum(decoded[m.name], m) 496 elif attr_spec.sub_type: 497 decoded = attr.as_c_array(attr_spec.sub_type) 498 else: 499 decoded = attr.as_bin() 500 if attr_spec.display_hint: 501 decoded = NlAttr.formatted_string(decoded, attr_spec.display_hint) 502 return decoded 503 504 def _decode_array_nest(self, attr, attr_spec): 505 decoded = [] 506 offset = 0 507 while offset < len(attr.raw): 508 item = NlAttr(attr.raw, offset) 509 offset += item.full_len 510 511 subattrs = self._decode(NlAttrs(item.raw), attr_spec['nested-attributes']) 512 decoded.append({ item.type: subattrs }) 513 return decoded 514 515 def _decode(self, attrs, space): 516 attr_space = self.attr_sets[space] 517 rsp = dict() 518 for attr in attrs: 519 try: 520 attr_spec = attr_space.attrs_by_val[attr.type] 521 except KeyError: 522 raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'") 523 if attr_spec["type"] == 'nest': 524 subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes']) 525 decoded = subdict 526 elif attr_spec["type"] == 'string': 527 decoded = attr.as_strz() 528 elif attr_spec["type"] == 'binary': 529 decoded = self._decode_binary(attr, attr_spec) 530 elif attr_spec["type"] == 'flag': 531 decoded = True 532 elif attr_spec["type"] in NlAttr.type_formats: 533 decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) 534 elif attr_spec["type"] == 'array-nest': 535 decoded = self._decode_array_nest(attr, attr_spec) 536 else: 537 raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') 538 539 if 'enum' in attr_spec: 540 decoded = self._decode_enum(decoded, attr_spec) 541 542 if not attr_spec.is_multi: 543 rsp[attr_spec['name']] = decoded 544 elif attr_spec.name in rsp: 545 rsp[attr_spec.name].append(decoded) 546 else: 547 rsp[attr_spec.name] = [decoded] 548 549 return rsp 550 551 def _decode_extack_path(self, attrs, attr_set, offset, target): 552 for attr in attrs: 553 try: 554 attr_spec = attr_set.attrs_by_val[attr.type] 555 except KeyError: 556 raise Exception(f"Space '{attr_set.name}' has no attribute with value '{attr.type}'") 557 if offset > target: 558 break 559 if offset == target: 560 return '.' + attr_spec.name 561 562 if offset + attr.full_len <= target: 563 offset += attr.full_len 564 continue 565 if attr_spec['type'] != 'nest': 566 raise Exception(f"Can't dive into {attr.type} ({attr_spec['name']}) for extack") 567 offset += 4 568 subpath = self._decode_extack_path(NlAttrs(attr.raw), 569 self.attr_sets[attr_spec['nested-attributes']], 570 offset, target) 571 if subpath is None: 572 return None 573 return '.' + attr_spec.name + subpath 574 575 return None 576 577 def _decode_extack(self, request, op, extack): 578 if 'bad-attr-offs' not in extack: 579 return 580 581 msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) 582 offset = 20 + self._fixed_header_size(op) 583 path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, 584 extack['bad-attr-offs']) 585 if path: 586 del extack['bad-attr-offs'] 587 extack['bad-attr'] = path 588 589 def _fixed_header_size(self, op): 590 if op.fixed_header: 591 fixed_header_members = self.consts[op.fixed_header].members 592 size = 0 593 for m in fixed_header_members: 594 format = NlAttr.get_format(m.type, m.byte_order) 595 size += format.size 596 return size 597 else: 598 return 0 599 600 def _decode_fixed_header(self, msg, name): 601 fixed_header_members = self.consts[name].members 602 fixed_header_attrs = dict() 603 offset = 0 604 for m in fixed_header_members: 605 format = NlAttr.get_format(m.type, m.byte_order) 606 [ value ] = format.unpack_from(msg.raw, offset) 607 offset += format.size 608 if m.enum: 609 value = self._decode_enum(value, m) 610 fixed_header_attrs[m.name] = value 611 return fixed_header_attrs 612 613 def handle_ntf(self, decoded): 614 msg = dict() 615 if self.include_raw: 616 msg['raw'] = decoded 617 op = self.rsp_by_value[decoded.cmd()] 618 attrs = self._decode(decoded.raw_attrs, op.attr_set.name) 619 if op.fixed_header: 620 attrs.update(self._decode_fixed_header(decoded, op.fixed_header)) 621 622 msg['name'] = op['name'] 623 msg['msg'] = attrs 624 self.async_msg_queue.append(msg) 625 626 def check_ntf(self): 627 while True: 628 try: 629 reply = self.sock.recv(128 * 1024, socket.MSG_DONTWAIT) 630 except BlockingIOError: 631 return 632 633 nms = NlMsgs(reply) 634 for nl_msg in nms: 635 if nl_msg.error: 636 print("Netlink error in ntf!?", os.strerror(-nl_msg.error)) 637 print(nl_msg) 638 continue 639 if nl_msg.done: 640 print("Netlink done while checking for ntf!?") 641 continue 642 643 decoded = self.nlproto.decode(self, nl_msg) 644 if decoded.cmd() not in self.async_msg_ids: 645 print("Unexpected msg id done while checking for ntf", decoded) 646 continue 647 648 self.handle_ntf(decoded) 649 650 def operation_do_attributes(self, name): 651 """ 652 For a given operation name, find and return a supported 653 set of attributes (as a dict). 654 """ 655 op = self.find_operation(name) 656 if not op: 657 return None 658 659 return op['do']['request']['attributes'].copy() 660 661 def _op(self, method, vals, flags, dump=False): 662 op = self.ops[method] 663 664 nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK 665 for flag in flags or []: 666 nl_flags |= flag 667 if dump: 668 nl_flags |= Netlink.NLM_F_DUMP 669 670 req_seq = random.randint(1024, 65535) 671 msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) 672 fixed_header_members = [] 673 if op.fixed_header: 674 fixed_header_members = self.consts[op.fixed_header].members 675 for m in fixed_header_members: 676 value = vals.pop(m.name) if m.name in vals else 0 677 format = NlAttr.get_format(m.type, m.byte_order) 678 msg += format.pack(value) 679 for name, value in vals.items(): 680 msg += self._add_attr(op.attr_set.name, name, value) 681 msg = _genl_msg_finalize(msg) 682 683 self.sock.send(msg, 0) 684 685 done = False 686 rsp = [] 687 while not done: 688 reply = self.sock.recv(128 * 1024) 689 nms = NlMsgs(reply, attr_space=op.attr_set) 690 for nl_msg in nms: 691 if nl_msg.extack: 692 self._decode_extack(msg, op, nl_msg.extack) 693 694 if nl_msg.error: 695 raise NlError(nl_msg) 696 if nl_msg.done: 697 if nl_msg.extack: 698 print("Netlink warning:") 699 print(nl_msg) 700 done = True 701 break 702 703 decoded = self.nlproto.decode(self, nl_msg) 704 705 # Check if this is a reply to our request 706 if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value: 707 if decoded.cmd() in self.async_msg_ids: 708 self.handle_ntf(decoded) 709 continue 710 else: 711 print('Unexpected message: ' + repr(decoded)) 712 continue 713 714 rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name) 715 if op.fixed_header: 716 rsp_msg.update(self._decode_fixed_header(decoded, op.fixed_header)) 717 rsp.append(rsp_msg) 718 719 if not rsp: 720 return None 721 if not dump and len(rsp) == 1: 722 return rsp[0] 723 return rsp 724 725 def do(self, method, vals, flags): 726 return self._op(method, vals, flags) 727 728 def dump(self, method, vals): 729 return self._op(method, vals, [], dump=True) 730