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