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.error = struct.unpack("i", self.raw[0:4])[0] 205 self.done = 1 206 extack_off = 4 207 208 self.extack = None 209 if self.nl_flags & Netlink.NLM_F_ACK_TLVS and extack_off: 210 self.extack = dict() 211 extack_attrs = NlAttrs(self.raw[extack_off:]) 212 for extack in extack_attrs: 213 if extack.type == Netlink.NLMSGERR_ATTR_MSG: 214 self.extack['msg'] = extack.as_strz() 215 elif extack.type == Netlink.NLMSGERR_ATTR_MISS_TYPE: 216 self.extack['miss-type'] = extack.as_scalar('u32') 217 elif extack.type == Netlink.NLMSGERR_ATTR_MISS_NEST: 218 self.extack['miss-nest'] = extack.as_scalar('u32') 219 elif extack.type == Netlink.NLMSGERR_ATTR_OFFS: 220 self.extack['bad-attr-offs'] = extack.as_scalar('u32') 221 else: 222 if 'unknown' not in self.extack: 223 self.extack['unknown'] = [] 224 self.extack['unknown'].append(extack) 225 226 if attr_space: 227 # We don't have the ability to parse nests yet, so only do global 228 if 'miss-type' in self.extack and 'miss-nest' not in self.extack: 229 miss_type = self.extack['miss-type'] 230 if miss_type in attr_space.attrs_by_val: 231 spec = attr_space.attrs_by_val[miss_type] 232 desc = spec['name'] 233 if 'doc' in spec: 234 desc += f" ({spec['doc']})" 235 self.extack['miss-type'] = desc 236 237 def cmd(self): 238 return self.nl_type 239 240 def __repr__(self): 241 msg = f"nl_len = {self.nl_len} ({len(self.raw)}) nl_flags = 0x{self.nl_flags:x} nl_type = {self.nl_type}\n" 242 if self.error: 243 msg += '\terror: ' + str(self.error) 244 if self.extack: 245 msg += '\textack: ' + repr(self.extack) 246 return msg 247 248 249class NlMsgs: 250 def __init__(self, data, attr_space=None): 251 self.msgs = [] 252 253 offset = 0 254 while offset < len(data): 255 msg = NlMsg(data, offset, attr_space=attr_space) 256 offset += msg.nl_len 257 self.msgs.append(msg) 258 259 def __iter__(self): 260 yield from self.msgs 261 262 263genl_family_name_to_id = None 264 265 266def _genl_msg(nl_type, nl_flags, genl_cmd, genl_version, seq=None): 267 # we prepend length in _genl_msg_finalize() 268 if seq is None: 269 seq = random.randint(1, 1024) 270 nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 271 genlmsg = struct.pack("BBH", genl_cmd, genl_version, 0) 272 return nlmsg + genlmsg 273 274 275def _genl_msg_finalize(msg): 276 return struct.pack("I", len(msg) + 4) + msg 277 278 279def _genl_load_families(): 280 with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, Netlink.NETLINK_GENERIC) as sock: 281 sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 282 283 msg = _genl_msg(Netlink.GENL_ID_CTRL, 284 Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK | Netlink.NLM_F_DUMP, 285 Netlink.CTRL_CMD_GETFAMILY, 1) 286 msg = _genl_msg_finalize(msg) 287 288 sock.send(msg, 0) 289 290 global genl_family_name_to_id 291 genl_family_name_to_id = dict() 292 293 while True: 294 reply = sock.recv(128 * 1024) 295 nms = NlMsgs(reply) 296 for nl_msg in nms: 297 if nl_msg.error: 298 print("Netlink error:", nl_msg.error) 299 return 300 if nl_msg.done: 301 return 302 303 gm = GenlMsg(nl_msg) 304 fam = dict() 305 for attr in NlAttrs(gm.raw): 306 if attr.type == Netlink.CTRL_ATTR_FAMILY_ID: 307 fam['id'] = attr.as_scalar('u16') 308 elif attr.type == Netlink.CTRL_ATTR_FAMILY_NAME: 309 fam['name'] = attr.as_strz() 310 elif attr.type == Netlink.CTRL_ATTR_MAXATTR: 311 fam['maxattr'] = attr.as_scalar('u32') 312 elif attr.type == Netlink.CTRL_ATTR_MCAST_GROUPS: 313 fam['mcast'] = dict() 314 for entry in NlAttrs(attr.raw): 315 mcast_name = None 316 mcast_id = None 317 for entry_attr in NlAttrs(entry.raw): 318 if entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_NAME: 319 mcast_name = entry_attr.as_strz() 320 elif entry_attr.type == Netlink.CTRL_ATTR_MCAST_GRP_ID: 321 mcast_id = entry_attr.as_scalar('u32') 322 if mcast_name and mcast_id is not None: 323 fam['mcast'][mcast_name] = mcast_id 324 if 'name' in fam and 'id' in fam: 325 genl_family_name_to_id[fam['name']] = fam 326 327 328class GenlMsg: 329 def __init__(self, nl_msg): 330 self.nl = nl_msg 331 self.genl_cmd, self.genl_version, _ = struct.unpack_from("BBH", nl_msg.raw, 0) 332 self.raw = nl_msg.raw[4:] 333 334 def cmd(self): 335 return self.genl_cmd 336 337 def __repr__(self): 338 msg = repr(self.nl) 339 msg += f"\tgenl_cmd = {self.genl_cmd} genl_ver = {self.genl_version}\n" 340 for a in self.raw_attrs: 341 msg += '\t\t' + repr(a) + '\n' 342 return msg 343 344 345class NetlinkProtocol: 346 def __init__(self, family_name, proto_num): 347 self.family_name = family_name 348 self.proto_num = proto_num 349 350 def _message(self, nl_type, nl_flags, seq=None): 351 if seq is None: 352 seq = random.randint(1, 1024) 353 nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0) 354 return nlmsg 355 356 def message(self, flags, command, version, seq=None): 357 return self._message(command, flags, seq) 358 359 def _decode(self, nl_msg): 360 return nl_msg 361 362 def decode(self, ynl, nl_msg): 363 msg = self._decode(nl_msg) 364 fixed_header_size = 0 365 if ynl: 366 op = ynl.rsp_by_value[msg.cmd()] 367 fixed_header_size = ynl._fixed_header_size(op) 368 msg.raw_attrs = NlAttrs(msg.raw[fixed_header_size:]) 369 return msg 370 371 def get_mcast_id(self, mcast_name, mcast_groups): 372 if mcast_name not in mcast_groups: 373 raise Exception(f'Multicast group "{mcast_name}" not present in the spec') 374 return mcast_groups[mcast_name].value 375 376 377class GenlProtocol(NetlinkProtocol): 378 def __init__(self, family_name): 379 super().__init__(family_name, Netlink.NETLINK_GENERIC) 380 381 global genl_family_name_to_id 382 if genl_family_name_to_id is None: 383 _genl_load_families() 384 385 self.genl_family = genl_family_name_to_id[family_name] 386 self.family_id = genl_family_name_to_id[family_name]['id'] 387 388 def message(self, flags, command, version, seq=None): 389 nlmsg = self._message(self.family_id, flags, seq) 390 genlmsg = struct.pack("BBH", command, version, 0) 391 return nlmsg + genlmsg 392 393 def _decode(self, nl_msg): 394 return GenlMsg(nl_msg) 395 396 def get_mcast_id(self, mcast_name, mcast_groups): 397 if mcast_name not in self.genl_family['mcast']: 398 raise Exception(f'Multicast group "{mcast_name}" not present in the family') 399 return self.genl_family['mcast'][mcast_name] 400 401 402# 403# YNL implementation details. 404# 405 406 407class YnlFamily(SpecFamily): 408 def __init__(self, def_path, schema=None): 409 super().__init__(def_path, schema) 410 411 self.include_raw = False 412 413 try: 414 if self.proto == "netlink-raw": 415 self.nlproto = NetlinkProtocol(self.yaml['name'], 416 self.yaml['protonum']) 417 else: 418 self.nlproto = GenlProtocol(self.yaml['name']) 419 except KeyError: 420 raise Exception(f"Family '{self.yaml['name']}' not supported by the kernel") 421 422 self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, self.nlproto.proto_num) 423 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1) 424 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1) 425 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_GET_STRICT_CHK, 1) 426 427 self.async_msg_ids = set() 428 self.async_msg_queue = [] 429 430 for msg in self.msgs.values(): 431 if msg.is_async: 432 self.async_msg_ids.add(msg.rsp_value) 433 434 for op_name, op in self.ops.items(): 435 bound_f = functools.partial(self._op, op_name) 436 setattr(self, op.ident_name, bound_f) 437 438 439 def ntf_subscribe(self, mcast_name): 440 mcast_id = self.nlproto.get_mcast_id(mcast_name, self.mcast_groups) 441 self.sock.bind((0, 0)) 442 self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_ADD_MEMBERSHIP, 443 mcast_id) 444 445 def _add_attr(self, space, name, value): 446 try: 447 attr = self.attr_sets[space][name] 448 except KeyError: 449 raise Exception(f"Space '{space}' has no attribute '{name}'") 450 nl_type = attr.value 451 if attr["type"] == 'nest': 452 nl_type |= Netlink.NLA_F_NESTED 453 attr_payload = b'' 454 for subname, subvalue in value.items(): 455 attr_payload += self._add_attr(attr['nested-attributes'], subname, subvalue) 456 elif attr["type"] == 'flag': 457 attr_payload = b'' 458 elif attr["type"] == 'string': 459 attr_payload = str(value).encode('ascii') + b'\x00' 460 elif attr["type"] == 'binary': 461 if isinstance(value, bytes): 462 attr_payload = value 463 elif isinstance(value, str): 464 attr_payload = bytes.fromhex(value) 465 else: 466 raise Exception(f'Unknown type for binary attribute, value: {value}') 467 elif attr['type'] in NlAttr.type_formats: 468 format = NlAttr.get_format(attr['type'], attr.byte_order) 469 attr_payload = format.pack(int(value)) 470 else: 471 raise Exception(f'Unknown type at {space} {name} {value} {attr["type"]}') 472 473 pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4) 474 return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad 475 476 def _decode_enum(self, raw, attr_spec): 477 enum = self.consts[attr_spec['enum']] 478 if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']: 479 i = 0 480 value = set() 481 while raw: 482 if raw & 1: 483 value.add(enum.entries_by_val[i].name) 484 raw >>= 1 485 i += 1 486 else: 487 value = enum.entries_by_val[raw].name 488 return value 489 490 def _decode_binary(self, attr, attr_spec): 491 if attr_spec.struct_name: 492 members = self.consts[attr_spec.struct_name] 493 decoded = attr.as_struct(members) 494 for m in members: 495 if m.enum: 496 decoded[m.name] = self._decode_enum(decoded[m.name], m) 497 elif attr_spec.sub_type: 498 decoded = attr.as_c_array(attr_spec.sub_type) 499 else: 500 decoded = attr.as_bin() 501 if attr_spec.display_hint: 502 decoded = NlAttr.formatted_string(decoded, attr_spec.display_hint) 503 return decoded 504 505 def _decode_array_nest(self, attr, attr_spec): 506 decoded = [] 507 offset = 0 508 while offset < len(attr.raw): 509 item = NlAttr(attr.raw, offset) 510 offset += item.full_len 511 512 subattrs = self._decode(NlAttrs(item.raw), attr_spec['nested-attributes']) 513 decoded.append({ item.type: subattrs }) 514 return decoded 515 516 def _decode(self, attrs, space): 517 attr_space = self.attr_sets[space] 518 rsp = dict() 519 for attr in attrs: 520 try: 521 attr_spec = attr_space.attrs_by_val[attr.type] 522 except KeyError: 523 raise Exception(f"Space '{space}' has no attribute with value '{attr.type}'") 524 if attr_spec["type"] == 'nest': 525 subdict = self._decode(NlAttrs(attr.raw), attr_spec['nested-attributes']) 526 decoded = subdict 527 elif attr_spec["type"] == 'string': 528 decoded = attr.as_strz() 529 elif attr_spec["type"] == 'binary': 530 decoded = self._decode_binary(attr, attr_spec) 531 elif attr_spec["type"] == 'flag': 532 decoded = True 533 elif attr_spec["type"] in NlAttr.type_formats: 534 decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order) 535 elif attr_spec["type"] == 'array-nest': 536 decoded = self._decode_array_nest(attr, attr_spec) 537 else: 538 raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}') 539 540 if 'enum' in attr_spec: 541 decoded = self._decode_enum(decoded, attr_spec) 542 543 if not attr_spec.is_multi: 544 rsp[attr_spec['name']] = decoded 545 elif attr_spec.name in rsp: 546 rsp[attr_spec.name].append(decoded) 547 else: 548 rsp[attr_spec.name] = [decoded] 549 550 return rsp 551 552 def _decode_extack_path(self, attrs, attr_set, offset, target): 553 for attr in attrs: 554 try: 555 attr_spec = attr_set.attrs_by_val[attr.type] 556 except KeyError: 557 raise Exception(f"Space '{attr_set.name}' has no attribute with value '{attr.type}'") 558 if offset > target: 559 break 560 if offset == target: 561 return '.' + attr_spec.name 562 563 if offset + attr.full_len <= target: 564 offset += attr.full_len 565 continue 566 if attr_spec['type'] != 'nest': 567 raise Exception(f"Can't dive into {attr.type} ({attr_spec['name']}) for extack") 568 offset += 4 569 subpath = self._decode_extack_path(NlAttrs(attr.raw), 570 self.attr_sets[attr_spec['nested-attributes']], 571 offset, target) 572 if subpath is None: 573 return None 574 return '.' + attr_spec.name + subpath 575 576 return None 577 578 def _decode_extack(self, request, op, extack): 579 if 'bad-attr-offs' not in extack: 580 return 581 582 msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) 583 offset = 20 + self._fixed_header_size(op) 584 path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, 585 extack['bad-attr-offs']) 586 if path: 587 del extack['bad-attr-offs'] 588 extack['bad-attr'] = path 589 590 def _fixed_header_size(self, op): 591 if op.fixed_header: 592 fixed_header_members = self.consts[op.fixed_header].members 593 size = 0 594 for m in fixed_header_members: 595 format = NlAttr.get_format(m.type, m.byte_order) 596 size += format.size 597 return size 598 else: 599 return 0 600 601 def _decode_fixed_header(self, msg, name): 602 fixed_header_members = self.consts[name].members 603 fixed_header_attrs = dict() 604 offset = 0 605 for m in fixed_header_members: 606 format = NlAttr.get_format(m.type, m.byte_order) 607 [ value ] = format.unpack_from(msg.raw, offset) 608 offset += format.size 609 if m.enum: 610 value = self._decode_enum(value, m) 611 fixed_header_attrs[m.name] = value 612 return fixed_header_attrs 613 614 def handle_ntf(self, decoded): 615 msg = dict() 616 if self.include_raw: 617 msg['raw'] = decoded 618 op = self.rsp_by_value[decoded.cmd()] 619 attrs = self._decode(decoded.raw_attrs, op.attr_set.name) 620 if op.fixed_header: 621 attrs.update(self._decode_fixed_header(decoded, op.fixed_header)) 622 623 msg['name'] = op['name'] 624 msg['msg'] = attrs 625 self.async_msg_queue.append(msg) 626 627 def check_ntf(self): 628 while True: 629 try: 630 reply = self.sock.recv(128 * 1024, socket.MSG_DONTWAIT) 631 except BlockingIOError: 632 return 633 634 nms = NlMsgs(reply) 635 for nl_msg in nms: 636 if nl_msg.error: 637 print("Netlink error in ntf!?", os.strerror(-nl_msg.error)) 638 print(nl_msg) 639 continue 640 if nl_msg.done: 641 print("Netlink done while checking for ntf!?") 642 continue 643 644 decoded = self.nlproto.decode(self, nl_msg) 645 if decoded.cmd() not in self.async_msg_ids: 646 print("Unexpected msg id done while checking for ntf", decoded) 647 continue 648 649 self.handle_ntf(decoded) 650 651 def operation_do_attributes(self, name): 652 """ 653 For a given operation name, find and return a supported 654 set of attributes (as a dict). 655 """ 656 op = self.find_operation(name) 657 if not op: 658 return None 659 660 return op['do']['request']['attributes'].copy() 661 662 def _op(self, method, vals, flags, dump=False): 663 op = self.ops[method] 664 665 nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK 666 for flag in flags or []: 667 nl_flags |= flag 668 if dump: 669 nl_flags |= Netlink.NLM_F_DUMP 670 671 req_seq = random.randint(1024, 65535) 672 msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) 673 fixed_header_members = [] 674 if op.fixed_header: 675 fixed_header_members = self.consts[op.fixed_header].members 676 for m in fixed_header_members: 677 value = vals.pop(m.name) if m.name in vals else 0 678 format = NlAttr.get_format(m.type, m.byte_order) 679 msg += format.pack(value) 680 for name, value in vals.items(): 681 msg += self._add_attr(op.attr_set.name, name, value) 682 msg = _genl_msg_finalize(msg) 683 684 self.sock.send(msg, 0) 685 686 done = False 687 rsp = [] 688 while not done: 689 reply = self.sock.recv(128 * 1024) 690 nms = NlMsgs(reply, attr_space=op.attr_set) 691 for nl_msg in nms: 692 if nl_msg.extack: 693 self._decode_extack(msg, op, nl_msg.extack) 694 695 if nl_msg.error: 696 raise NlError(nl_msg) 697 if nl_msg.done: 698 if nl_msg.extack: 699 print("Netlink warning:") 700 print(nl_msg) 701 done = True 702 break 703 704 decoded = self.nlproto.decode(self, nl_msg) 705 706 # Check if this is a reply to our request 707 if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value: 708 if decoded.cmd() in self.async_msg_ids: 709 self.handle_ntf(decoded) 710 continue 711 else: 712 print('Unexpected message: ' + repr(decoded)) 713 continue 714 715 rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name) 716 if op.fixed_header: 717 rsp_msg.update(self._decode_fixed_header(decoded, op.fixed_header)) 718 rsp.append(rsp_msg) 719 720 if not rsp: 721 return None 722 if not dump and len(rsp) == 1: 723 return rsp[0] 724 return rsp 725 726 def do(self, method, vals, flags): 727 return self._op(method, vals, flags) 728 729 def dump(self, method, vals): 730 return self._op(method, vals, [], dump=True) 731