1# 2# Netlink interface based on libnl 3# 4# Copyright (c) 2011 Thomas Graf <[email protected]> 5# 6 7"""netlink library based on libnl 8 9This module provides an interface to netlink sockets 10 11The module contains the following public classes: 12 - Socket -- The netlink socket 13 - Message -- The netlink message 14 - Callback -- The netlink callback handler 15 - Object -- Abstract object (based on struct nl_obect in libnl) used as 16 base class for all object types which can be put into a Cache 17 - Cache -- A collection of objects which are derived from the base 18 class Object. Used for netlink protocols which maintain a list 19 or tree of objects. 20 - DumpParams -- 21 22The following exceptions are defined: 23 - NetlinkError -- Base exception for all general purpose exceptions raised. 24 - KernelError -- Raised when the kernel returns an error as response to a 25 request. 26 27All other classes or functions in this module are considered implementation 28details. 29""" 30from __future__ import absolute_import 31 32 33from . import capi 34import sys 35import socket 36 37__all__ = [ 38 "Socket", 39 "Message", 40 "Callback", 41 "DumpParams", 42 "Object", 43 "Cache", 44 "KernelError", 45 "NetlinkError", 46] 47 48__version__ = "0.1" 49 50# netlink protocols 51NETLINK_ROUTE = 0 52# NETLINK_UNUSED = 1 53NETLINK_USERSOCK = 2 54NETLINK_FIREWALL = 3 55NETLINK_INET_DIAG = 4 56NETLINK_NFLOG = 5 57NETLINK_XFRM = 6 58NETLINK_SELINUX = 7 59NETLINK_ISCSI = 8 60NETLINK_AUDIT = 9 61NETLINK_FIB_LOOKUP = 10 62NETLINK_CONNECTOR = 11 63NETLINK_NETFILTER = 12 64NETLINK_IP6_FW = 13 65NETLINK_DNRTMSG = 14 66NETLINK_KOBJECT_UEVENT = 15 67NETLINK_GENERIC = 16 68NETLINK_SCSITRANSPORT = 18 69NETLINK_ECRYPTFS = 19 70 71NL_DONTPAD = 0 72NL_AUTO_PORT = 0 73NL_AUTO_SEQ = 0 74 75NL_DUMP_LINE = 0 76NL_DUMP_DETAILS = 1 77NL_DUMP_STATS = 2 78 79NLM_F_REQUEST = 1 80NLM_F_MULTI = 2 81NLM_F_ACK = 4 82NLM_F_ECHO = 8 83 84NLM_F_ROOT = 0x100 85NLM_F_MATCH = 0x200 86NLM_F_ATOMIC = 0x400 87NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH 88 89NLM_F_REPLACE = 0x100 90NLM_F_EXCL = 0x200 91NLM_F_CREATE = 0x400 92NLM_F_APPEND = 0x800 93 94 95class NetlinkError(Exception): 96 def __init__(self, error): 97 self._error = error 98 self._msg = capi.nl_geterror(error) 99 100 def __str__(self): 101 return self._msg 102 103 104class KernelError(NetlinkError): 105 def __str__(self): 106 return "Kernel returned: {0}".format(self._msg) 107 108 109class ImmutableError(NetlinkError): 110 def __init__(self, msg): 111 self._msg = msg 112 113 def __str__(self): 114 return "Immutable attribute: {0}".format(self._msg) 115 116 117class Message(object): 118 """Netlink message""" 119 120 def __init__(self, size=0): 121 if size == 0: 122 self._msg = capi.nlmsg_alloc() 123 else: 124 self._msg = capi.nlmsg_alloc_size(size) 125 126 if self._msg is None: 127 raise Exception("Message allocation returned NULL") 128 129 def __del__(self): 130 capi.nlmsg_free(self._msg) 131 132 def __len__(self): 133 return capi.nlmsg_len(capi.nlmsg_hdr(self._msg)) 134 135 @property 136 def protocol(self): 137 return capi.nlmsg_get_proto(self._msg) 138 139 @protocol.setter 140 def protocol(self, value): 141 capi.nlmsg_set_proto(self._msg, value) 142 143 @property 144 def maxSize(self): 145 return capi.nlmsg_get_max_size(self._msg) 146 147 @property 148 def hdr(self): 149 return capi.nlmsg_hdr(self._msg) 150 151 @property 152 def data(self): 153 return capi.nlmsg_data(self._msg) 154 155 @property 156 def attrs(self): 157 return capi.nlmsg_attrdata(self._msg) 158 159 def send(self, sock): 160 sock.send(self) 161 162 163class Callback(object): 164 """Netlink callback""" 165 166 def __init__(self, kind=capi.NL_CB_DEFAULT): 167 if isinstance(kind, Callback): 168 self._cb = capi.py_nl_cb_clone(kind._cb) 169 else: 170 self._cb = capi.nl_cb_alloc(kind) 171 172 def __del__(self): 173 capi.py_nl_cb_put(self._cb) 174 175 def set_type(self, t, k, handler, obj): 176 return capi.py_nl_cb_set(self._cb, t, k, handler, obj) 177 178 def set_all(self, k, handler, obj): 179 return capi.py_nl_cb_set_all(self._cb, k, handler, obj) 180 181 def set_err(self, k, handler, obj): 182 return capi.py_nl_cb_err(self._cb, k, handler, obj) 183 184 def clone(self): 185 return Callback(self) 186 187 188class Socket(object): 189 """Netlink socket""" 190 191 def __init__(self, cb=None): 192 if isinstance(cb, Callback): 193 self._sock = capi.nl_socket_alloc_cb(cb._cb) 194 elif cb is None: 195 self._sock = capi.nl_socket_alloc() 196 else: 197 raise Exception("'cb' parameter has wrong type") 198 199 if self._sock is None: 200 raise Exception("NULL pointer returned while allocating socket") 201 202 def __del__(self): 203 capi.nl_socket_free(self._sock) 204 205 def __str__(self): 206 return "nlsock<{0}>".format(self.local_port) 207 208 @property 209 def local_port(self): 210 return capi.nl_socket_get_local_port(self._sock) 211 212 @local_port.setter 213 def local_port(self, value): 214 capi.nl_socket_set_local_port(self._sock, int(value)) 215 216 @property 217 def peer_port(self): 218 return capi.nl_socket_get_peer_port(self._sock) 219 220 @peer_port.setter 221 def peer_port(self, value): 222 capi.nl_socket_set_peer_port(self._sock, int(value)) 223 224 @property 225 def peer_groups(self): 226 return capi.nl_socket_get_peer_groups(self._sock) 227 228 @peer_groups.setter 229 def peer_groups(self, value): 230 capi.nl_socket_set_peer_groups(self._sock, value) 231 232 def set_bufsize(self, rx, tx): 233 capi.nl_socket_set_buffer_size(self._sock, rx, tx) 234 235 def connect(self, proto): 236 capi.nl_connect(self._sock, proto) 237 return self 238 239 def disconnect(self): 240 capi.nl_close(self._sock) 241 242 def sendto(self, buf): 243 ret = capi.nl_sendto(self._sock, buf, len(buf)) 244 if ret < 0: 245 raise Exception("Failed to send") 246 else: 247 return ret 248 249 def send_auto_complete(self, msg): 250 if not isinstance(msg, Message): 251 raise Exception("must provide Message instance") 252 ret = capi.nl_send_auto_complete(self._sock, msg._msg) 253 if ret < 0: 254 raise Exception("send_auto_complete failed: ret=%d" % ret) 255 return ret 256 257 def recvmsgs(self, recv_cb): 258 if not isinstance(recv_cb, Callback): 259 raise Exception("must provide Callback instance") 260 ret = capi.nl_recvmsgs(self._sock, recv_cb._cb) 261 if ret < 0: 262 raise Exception("recvmsg failed: ret=%d" % ret) 263 264 265_sockets = {} 266 267 268def lookup_socket(protocol): 269 try: 270 sock = _sockets[protocol] 271 except KeyError: 272 sock = Socket() 273 sock.connect(protocol) 274 _sockets[protocol] = sock 275 276 return sock 277 278 279class DumpParams(object): 280 """Dumping parameters""" 281 282 def __init__(self, type_=NL_DUMP_LINE): 283 self._dp = capi.alloc_dump_params() 284 if not self._dp: 285 raise Exception("Unable to allocate struct nl_dump_params") 286 287 self._dp.dp_type = type_ 288 289 def __del__(self): 290 capi.free_dump_params(self._dp) 291 292 @property 293 def type(self): 294 return self._dp.dp_type 295 296 @type.setter 297 def type(self, value): 298 self._dp.dp_type = value 299 300 @property 301 def prefix(self): 302 return self._dp.dp_prefix 303 304 @prefix.setter 305 def prefix(self, value): 306 self._dp.dp_prefix = value 307 308 309# underscore this to make sure it is deleted first upon module deletion 310_defaultDumpParams = DumpParams(NL_DUMP_LINE) 311 312 313class Object(object): 314 """Cacheable object (base class)""" 315 316 def __init__(self, obj_name, name, obj=None): 317 self._obj_name = obj_name 318 self._name = name 319 self._modules = [] 320 321 if not obj: 322 obj = capi.object_alloc_name(self._obj_name) 323 324 self._nl_object = obj 325 326 # Create a clone which stores the original state to notice 327 # modifications 328 clone_obj = capi.nl_object_clone(self._nl_object) 329 self._orig = self._obj2type(clone_obj) 330 331 def __del__(self): 332 if not self._nl_object: 333 raise ValueError() 334 335 capi.nl_object_put(self._nl_object) 336 337 def __str__(self): 338 if hasattr(self, "format"): 339 return self.format() 340 else: 341 return capi.nl_object_dump_buf(self._nl_object, 4096).rstrip() 342 343 def _new_instance(self): 344 raise NotImplementedError() 345 346 def clone(self): 347 """Clone object""" 348 return self._new_instance(capi.nl_object_clone(self._nl_object)) 349 350 def _module_lookup(self, path, constructor=None): 351 """Lookup object specific module and load it 352 353 Object implementations consisting of multiple types may 354 offload some type specific code to separate modules which 355 are loadable on demand, e.g. a VLAN link or a specific 356 queueing discipline implementation. 357 358 Loads the module `path` and calls the constructor if 359 supplied or `module`.init() 360 361 The constructor/init function typically assigns a new 362 object covering the type specific implementation aspects 363 to the new object, e.g. link.vlan = VLANLink() 364 """ 365 try: 366 __import__(path) 367 except ImportError: 368 return 369 370 module = sys.modules[path] 371 372 if constructor: 373 ret = getattr(module, constructor)(self) 374 else: 375 ret = module.init(self) 376 377 if ret: 378 self._modules.append(ret) 379 380 def _module_brief(self): 381 ret = "" 382 383 for module in self._modules: 384 if hasattr(module, "brief"): 385 ret += module.brief() 386 387 return ret 388 389 def dump(self, params=None): 390 """Dump object as human readable text""" 391 if params is None: 392 params = _defaultDumpParams 393 394 capi.nl_object_dump(self._nl_object, params._dp) 395 396 @property 397 def mark(self): 398 return bool(capi.nl_object_is_marked(self._nl_object)) 399 400 @mark.setter 401 def mark(self, value): 402 if value: 403 capi.nl_object_mark(self._nl_object) 404 else: 405 capi.nl_object_unmark(self._nl_object) 406 407 @property 408 def shared(self): 409 return capi.nl_object_shared(self._nl_object) != 0 410 411 @property 412 def attrs(self): 413 attr_list = capi.nl_object_attr_list(self._nl_object, 1024) 414 return attr_list[0].split() 415 416 @property 417 def refcnt(self): 418 return capi.nl_object_get_refcnt(self._nl_object) 419 420 # this method resolves multiple levels of sub types to allow 421 # accessing properties of subclass/subtypes (e.g. link.vlan.id) 422 def _resolve(self, attr): 423 obj = self 424 lst = attr.split(".") 425 while len(lst) > 1: 426 obj = getattr(obj, lst.pop(0)) 427 return (obj, lst.pop(0)) 428 429 def _setattr(self, attr, val): 430 obj, attr = self._resolve(attr) 431 return setattr(obj, attr, val) 432 433 def _hasattr(self, attr): 434 obj, attr = self._resolve(attr) 435 return hasattr(obj, attr) 436 437 438class ObjIterator(object): 439 def __init__(self, cache, obj): 440 self._cache = cache 441 self._nl_object = None 442 443 if not obj: 444 self._end = 1 445 else: 446 capi.nl_object_get(obj) 447 self._nl_object = obj 448 self._first = 1 449 self._end = 0 450 451 def __del__(self): 452 if self._nl_object: 453 capi.nl_object_put(self._nl_object) 454 455 def __iter__(self): 456 return self 457 458 def get_next(self): 459 return capi.nl_cache_get_next(self._nl_object) 460 461 def next(self): 462 return self.__next__() 463 464 def __next__(self): 465 if self._end: 466 raise StopIteration() 467 468 if self._first: 469 ret = self._nl_object 470 self._first = 0 471 else: 472 ret = self.get_next() 473 if not ret: 474 self._end = 1 475 raise StopIteration() 476 477 # return ref of previous element and acquire ref of current 478 # element to have object stay around until we fetched the 479 # next ptr 480 capi.nl_object_put(self._nl_object) 481 capi.nl_object_get(ret) 482 self._nl_object = ret 483 484 # reference used inside object 485 capi.nl_object_get(ret) 486 return self._cache._new_object(ret) 487 488 489class ReverseObjIterator(ObjIterator): 490 def get_next(self): 491 return capi.nl_cache_get_prev(self._nl_object) 492 493 494class Cache(object): 495 """Collection of netlink objects""" 496 497 def __init__(self): 498 if self.__class__ is Cache: 499 raise NotImplementedError() 500 self.arg1 = None 501 self.arg2 = None 502 503 def __del__(self): 504 capi.nl_cache_free(self._nl_cache) 505 506 def __len__(self): 507 return capi.nl_cache_nitems(self._nl_cache) 508 509 def __iter__(self): 510 obj = capi.nl_cache_get_first(self._nl_cache) 511 return ObjIterator(self, obj) 512 513 def __reversed__(self): 514 obj = capi.nl_cache_get_last(self._nl_cache) 515 return ReverseObjIterator(self, obj) 516 517 def __contains__(self, item): 518 obj = capi.nl_cache_search(self._nl_cache, item._nl_object) 519 if obj is None: 520 return False 521 else: 522 capi.nl_object_put(obj) 523 return True 524 525 # called by sub classes to allocate type specific caches by name 526 @staticmethod 527 def _alloc_cache_name(name): 528 return capi.alloc_cache_name(name) 529 530 # implemented by sub classes, must return new instasnce of cacheable 531 # object 532 @staticmethod 533 def _new_object(obj): 534 raise NotImplementedError() 535 536 # implemented by sub classes, must return instance of sub class 537 def _new_cache(self, cache): 538 raise NotImplementedError() 539 540 def subset(self, filter_): 541 """Return new cache containing subset of cache 542 543 Cretes a new cache containing all objects which match the 544 specified filter. 545 """ 546 if not filter_: 547 raise ValueError() 548 549 c = capi.nl_cache_subset(self._nl_cache, filter_._nl_object) 550 return self._new_cache(cache=c) 551 552 def dump(self, params=None, filter_=None): 553 """Dump (print) cache as human readable text""" 554 if not params: 555 params = _defaultDumpParams 556 557 if filter_: 558 filter_ = filter_._nl_object 559 560 capi.nl_cache_dump_filter(self._nl_cache, params._dp, filter_) 561 562 def clear(self): 563 """Remove all cache entries""" 564 capi.nl_cache_clear(self._nl_cache) 565 566 # Called by sub classes to set first cache argument 567 def _set_arg1(self, arg): 568 self.arg1 = arg 569 capi.nl_cache_set_arg1(self._nl_cache, arg) 570 571 # Called by sub classes to set second cache argument 572 def _set_arg2(self, arg): 573 self.arg2 = arg 574 capi.nl_cache_set_arg2(self._nl_cache, arg) 575 576 def refill(self, socket=None): 577 """Clear cache and refill it""" 578 if socket is None: 579 socket = lookup_socket(self._protocol) 580 581 capi.nl_cache_refill(socket._sock, self._nl_cache) 582 return self 583 584 def resync(self, socket=None, cb=None, args=None): 585 """Synchronize cache with content in kernel""" 586 if socket is None: 587 socket = lookup_socket(self._protocol) 588 589 capi.nl_cache_resync(socket._sock, self._nl_cache, cb, args) 590 591 def provide(self): 592 """Provide this cache to others 593 594 Caches which have been "provided" are made available 595 to other users (of the same application context) which 596 "require" it. F.e. a link cache is generally provided 597 to allow others to translate interface indexes to 598 link names 599 """ 600 601 capi.nl_cache_mngt_provide(self._nl_cache) 602 603 def unprovide(self): 604 """Unprovide this cache 605 606 No longer make the cache available to others. If the cache 607 has been handed out already, that reference will still 608 be valid. 609 """ 610 capi.nl_cache_mngt_unprovide(self._nl_cache) 611 612 613# Cache Manager (Work in Progress) 614NL_AUTO_PROVIDE = 1 615 616 617class CacheManager(object): 618 def __init__(self, protocol, flags=None): 619 620 self._sock = Socket() 621 self._sock.connect(protocol) 622 623 if not flags: 624 flags = NL_AUTO_PROVIDE 625 626 self._mngr = capi.cache_mngr_alloc(self._sock._sock, protocol, flags) 627 628 def __del__(self): 629 if self._sock: 630 self._sock.disconnect() 631 632 if self._mngr: 633 capi.nl_cache_mngr_free(self._mngr) 634 635 def add(self, name): 636 capi.cache_mngr_add(self._mngr, name, None, None) 637 638 639class AddressFamily(object): 640 """Address family representation 641 642 af = AddressFamily('inet6') 643 # raises: 644 # - ValueError if family name is not known 645 # - TypeError if invalid type is specified for family 646 647 print af # => 'inet6' (string representation) 648 print int(af) # => 10 (numeric representation) 649 print repr(af) # => AddressFamily('inet6') 650 """ 651 652 def __init__(self, family=socket.AF_UNSPEC): 653 if isinstance(family, str): 654 family = capi.nl_str2af(family) 655 if family < 0: 656 raise ValueError("Unknown family name") 657 elif not isinstance(family, int): 658 raise TypeError() 659 660 self._family = family 661 662 def __str__(self): 663 return capi.nl_af2str(self._family, 32)[0] 664 665 def __int__(self): 666 return self._family 667 668 def __repr__(self): 669 return "AddressFamily({0!r})".format(str(self)) 670 671 672class AbstractAddress(object): 673 """Abstract address object 674 675 addr = AbstractAddress('127.0.0.1/8') 676 print addr # => '127.0.0.1/8' 677 print addr.prefixlen # => '8' 678 print addr.family # => 'inet' 679 print len(addr) # => '4' (32bit ipv4 address) 680 681 a = AbstractAddress('10.0.0.1/24') 682 b = AbstractAddress('10.0.0.2/24') 683 print a == b # => False 684 685 686 """ 687 688 def __init__(self, addr): 689 self._nl_addr = None 690 691 if isinstance(addr, str): 692 # returns None on success I guess 693 # TO CORRECT 694 addr = capi.addr_parse(addr, socket.AF_UNSPEC) 695 if addr is None: 696 raise ValueError("Invalid address format") 697 elif addr: 698 capi.nl_addr_get(addr) 699 700 self._nl_addr = addr 701 702 def __del__(self): 703 if self._nl_addr: 704 capi.nl_addr_put(self._nl_addr) 705 706 def __cmp__(self, other): 707 if isinstance(other, str): 708 other = AbstractAddress(other) 709 710 diff = self.prefixlen - other.prefixlen 711 if diff == 0: 712 diff = capi.nl_addr_cmp(self._nl_addr, other._nl_addr) 713 714 return diff 715 716 def contains(self, item): 717 diff = int(self.family) - int(item.family) 718 if diff: 719 return False 720 721 if item.prefixlen < self.prefixlen: 722 return False 723 724 diff = capi.nl_addr_cmp_prefix(self._nl_addr, item._nl_addr) 725 return diff == 0 726 727 def __nonzero__(self): 728 if self._nl_addr: 729 return not capi.nl_addr_iszero(self._nl_addr) 730 else: 731 return False 732 733 def __len__(self): 734 if self._nl_addr: 735 return capi.nl_addr_get_len(self._nl_addr) 736 else: 737 return 0 738 739 def __str__(self): 740 if self._nl_addr: 741 return capi.nl_addr2str(self._nl_addr, 64)[0] 742 else: 743 return "none" 744 745 @property 746 def shared(self): 747 """True if address is shared (multiple users)""" 748 if self._nl_addr: 749 return capi.nl_addr_shared(self._nl_addr) != 0 750 else: 751 return False 752 753 @property 754 def prefixlen(self): 755 """Length of prefix (number of bits)""" 756 if self._nl_addr: 757 return capi.nl_addr_get_prefixlen(self._nl_addr) 758 else: 759 return 0 760 761 @prefixlen.setter 762 def prefixlen(self, value): 763 if not self._nl_addr: 764 raise TypeError() 765 766 capi.nl_addr_set_prefixlen(self._nl_addr, int(value)) 767 768 @property 769 def family(self): 770 """Address family""" 771 f = 0 772 if self._nl_addr: 773 f = capi.nl_addr_get_family(self._nl_addr) 774 775 return AddressFamily(f) 776 777 @family.setter 778 def family(self, value): 779 if not self._nl_addr: 780 raise TypeError() 781 782 if not isinstance(value, AddressFamily): 783 value = AddressFamily(value) 784 785 capi.nl_addr_set_family(self._nl_addr, int(value)) 786 787 788# keyword: 789# type = { int | str } 790# immutable = { True | False } 791# fmt = func (formatting function) 792# title = string 793 794 795def nlattr(**kwds): 796 """netlink object attribute decorator 797 798 decorator used to mark mutable and immutable properties 799 of netlink objects. All properties marked as such are 800 regarded to be accessable. 801 802 @property 803 @netlink.nlattr(type=int) 804 def my_attr(self): 805 return self._my_attr 806 807 """ 808 809 def wrap_fn(func): 810 func.formatinfo = kwds 811 return func 812 813 return wrap_fn 814