Home | History | Annotate | Download | only in route
      1 #
      2 # Copyright (c) 2011 Thomas Graf <tgraf (at] suug.ch>
      3 #
      4 
      5 """Module providing access to network addresses
      6 """
      7 
      8 from __future__ import absolute_import
      9 
     10 
     11 __version__ = '1.0'
     12 __all__ = [
     13     'AddressCache',
     14     'Address']
     15 
     16 import datetime
     17 from .. import core as netlink
     18 from .  import capi as capi
     19 from .  import link as Link
     20 from .. import util as util
     21 
     22 class AddressCache(netlink.Cache):
     23     """Cache containing network addresses"""
     24 
     25     def __init__(self, cache=None):
     26         if not cache:
     27             cache = self._alloc_cache_name('route/addr')
     28 
     29         self._protocol = netlink.NETLINK_ROUTE
     30         self._nl_cache = cache
     31 
     32     def __getitem__(self, key):
     33         # Using ifindex=0 here implies that the local address itself
     34         # is unique, otherwise the first occurence is returned.
     35         return self.lookup(0, key)
     36 
     37     def lookup(self, ifindex, local):
     38         if type(local) is str:
     39             local = netlink.AbstractAddress(local)
     40 
     41         addr = capi.rtnl_addr_get(self._nl_cache, ifindex,
     42                       local._nl_addr)
     43         if addr is None:
     44             raise KeyError()
     45 
     46         return Address._from_capi(addr)
     47 
     48     @staticmethod
     49     def _new_object(obj):
     50         return Address(obj)
     51 
     52     @staticmethod
     53     def _new_cache(cache):
     54         return AddressCache(cache=cache)
     55 
     56 class Address(netlink.Object):
     57     """Network address"""
     58 
     59     def __init__(self, obj=None):
     60         netlink.Object.__init__(self, 'route/addr', 'address', obj)
     61         self._rtnl_addr = self._obj2type(self._nl_object)
     62 
     63     @classmethod
     64     def _from_capi(cls, obj):
     65         return cls(capi.addr2obj(obj))
     66 
     67     @staticmethod
     68     def _obj2type(obj):
     69         return capi.obj2addr(obj)
     70 
     71     def __cmp__(self, other):
     72         # sort by:
     73         #    1. network link
     74         #    2. address family
     75         #    3. local address (including prefixlen)
     76         diff = self.ifindex - other.ifindex
     77 
     78         if diff == 0:
     79             diff = self.family - other.family
     80             if diff == 0:
     81                 diff = capi.nl_addr_cmp(self.local, other.local)
     82 
     83         return diff
     84 
     85     @staticmethod
     86     def _new_instance(obj):
     87         return Address(obj)
     88 
     89     @property
     90     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
     91     def ifindex(self):
     92         """interface index"""
     93         return capi.rtnl_addr_get_ifindex(self._rtnl_addr)
     94 
     95     @ifindex.setter
     96     def ifindex(self, value):
     97         link = Link.resolve(value)
     98         if not link:
     99             raise ValueError()
    100 
    101         self.link = link
    102 
    103     @property
    104     @netlink.nlattr(type=str, fmt=util.string)
    105     def link(self):
    106         link = capi.rtnl_addr_get_link(self._rtnl_addr)
    107         if not link:
    108             return None
    109 
    110         return Link.Link.from_capi(link)
    111 
    112     @link.setter
    113     def link(self, value):
    114         if type(value) is str:
    115             try:
    116                 value = Link.resolve(value)
    117             except KeyError:
    118                 raise ValueError()
    119 
    120         capi.rtnl_addr_set_link(self._rtnl_addr, value._rtnl_link)
    121 
    122         # ifindex is immutable but we assume that if _orig does not
    123         # have an ifindex specified, it was meant to be given here
    124         if capi.rtnl_addr_get_ifindex(self._orig) == 0:
    125             capi.rtnl_addr_set_ifindex(self._orig, value.ifindex)
    126 
    127     @property
    128     @netlink.nlattr(type=str, fmt=util.string)
    129     def label(self):
    130         """address label"""
    131         return capi.rtnl_addr_get_label(self._rtnl_addr)
    132 
    133     @label.setter
    134     def label(self, value):
    135         capi.rtnl_addr_set_label(self._rtnl_addr, value)
    136 
    137     @property
    138     @netlink.nlattr(type=str, fmt=util.string)
    139     def flags(self):
    140         """Flags
    141 
    142         Setting this property will *Not* reset flags to value you supply in
    143 
    144         Examples:
    145         addr.flags = '+xxx' # add xxx flag
    146         addr.flags = 'xxx'  # exactly the same
    147         addr.flags = '-xxx' # remove xxx flag
    148         addr.flags = [ '+xxx', '-yyy' ] # list operation
    149         """
    150         flags = capi.rtnl_addr_get_flags(self._rtnl_addr)
    151         return capi.rtnl_addr_flags2str(flags, 256)[0].split(',')
    152 
    153     def _set_flag(self, flag):
    154         if flag.startswith('-'):
    155             i = capi.rtnl_addr_str2flags(flag[1:])
    156             capi.rtnl_addr_unset_flags(self._rtnl_addr, i)
    157         elif flag.startswith('+'):
    158             i = capi.rtnl_addr_str2flags(flag[1:])
    159             capi.rtnl_addr_set_flags(self._rtnl_addr, i)
    160         else:
    161             i = capi.rtnl_addr_str2flags(flag)
    162             capi.rtnl_addr_set_flags(self._rtnl_addr, i)
    163 
    164     @flags.setter
    165     def flags(self, value):
    166         if type(value) is list:
    167             for flag in value:
    168                 self._set_flag(flag)
    169         else:
    170             self._set_flag(value)
    171 
    172     @property
    173     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
    174     def family(self):
    175         """Address family"""
    176         fam = capi.rtnl_addr_get_family(self._rtnl_addr)
    177         return netlink.AddressFamily(fam)
    178 
    179     @family.setter
    180     def family(self, value):
    181         if not isinstance(value, netlink.AddressFamily):
    182             value = netlink.AddressFamily(value)
    183 
    184         capi.rtnl_addr_set_family(self._rtnl_addr, int(value))
    185 
    186     @property
    187     @netlink.nlattr(type=int, fmt=util.num)
    188     def scope(self):
    189         """Address scope"""
    190         scope = capi.rtnl_addr_get_scope(self._rtnl_addr)
    191         return capi.rtnl_scope2str(scope, 32)[0]
    192 
    193     @scope.setter
    194     def scope(self, value):
    195         if type(value) is str:
    196             value = capi.rtnl_str2scope(value)
    197         capi.rtnl_addr_set_scope(self._rtnl_addr, value)
    198 
    199     @property
    200     @netlink.nlattr(type=str, immutable=True, fmt=util.addr)
    201     def local(self):
    202         """Local address"""
    203         a = capi.rtnl_addr_get_local(self._rtnl_addr)
    204         return netlink.AbstractAddress(a)
    205 
    206     @local.setter
    207     def local(self, value):
    208         a = netlink.AbstractAddress(value)
    209         capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr)
    210 
    211         # local is immutable but we assume that if _orig does not
    212         # have a local address specified, it was meant to be given here
    213         if capi.rtnl_addr_get_local(self._orig) is None:
    214             capi.rtnl_addr_set_local(self._orig, a._nl_addr)
    215 
    216     @property
    217     @netlink.nlattr(type=str, fmt=util.addr)
    218     def peer(self):
    219         """Peer address"""
    220         a = capi.rtnl_addr_get_peer(self._rtnl_addr)
    221         return netlink.AbstractAddress(a)
    222 
    223     @peer.setter
    224     def peer(self, value):
    225         a = netlink.AbstractAddress(value)
    226         capi.rtnl_addr_set_peer(self._rtnl_addr, a._nl_addr)
    227 
    228     @property
    229     @netlink.nlattr(type=str, fmt=util.addr)
    230     def broadcast(self):
    231         """Broadcast address"""
    232         a = capi.rtnl_addr_get_broadcast(self._rtnl_addr)
    233         return netlink.AbstractAddress(a)
    234 
    235     @broadcast.setter
    236     def broadcast(self, value):
    237         a = netlink.AbstractAddress(value)
    238         capi.rtnl_addr_set_broadcast(self._rtnl_addr, a._nl_addr)
    239 
    240     @property
    241     @netlink.nlattr(type=str, fmt=util.addr)
    242     def multicast(self):
    243         """multicast address"""
    244         a = capi.rtnl_addr_get_multicast(self._rtnl_addr)
    245         return netlink.AbstractAddress(a)
    246 
    247     @multicast.setter
    248     def multicast(self, value):
    249         try:
    250             a = netlink.AbstractAddress(value)
    251         except ValueError as err:
    252             raise AttributeError('multicast', err)
    253 
    254         capi.rtnl_addr_set_multicast(self._rtnl_addr, a._nl_addr)
    255 
    256     @property
    257     @netlink.nlattr(type=str, fmt=util.addr)
    258     def anycast(self):
    259         """anycast address"""
    260         a = capi.rtnl_addr_get_anycast(self._rtnl_addr)
    261         return netlink.AbstractAddress(a)
    262 
    263     @anycast.setter
    264     def anycast(self, value):
    265         a = netlink.AbstractAddress(value)
    266         capi.rtnl_addr_set_anycast(self._rtnl_addr, a._nl_addr)
    267 
    268     @property
    269     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
    270     def valid_lifetime(self):
    271         """Valid lifetime"""
    272         msecs = capi.rtnl_addr_get_valid_lifetime(self._rtnl_addr)
    273         if msecs == 0xFFFFFFFF:
    274             return None
    275         else:
    276             return datetime.timedelta(seconds=msecs)
    277 
    278     @valid_lifetime.setter
    279     def valid_lifetime(self, value):
    280         capi.rtnl_addr_set_valid_lifetime(self._rtnl_addr, int(value))
    281 
    282     @property
    283     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
    284     def preferred_lifetime(self):
    285         """Preferred lifetime"""
    286         msecs = capi.rtnl_addr_get_preferred_lifetime(self._rtnl_addr)
    287         if msecs == 0xFFFFFFFF:
    288             return None
    289         else:
    290             return datetime.timedelta(seconds=msecs)
    291 
    292     @preferred_lifetime.setter
    293     def preferred_lifetime(self, value):
    294         capi.rtnl_addr_set_preferred_lifetime(self._rtnl_addr, int(value))
    295 
    296     @property
    297     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
    298     def create_time(self):
    299         """Creation time"""
    300         hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr)
    301         return datetime.timedelta(milliseconds=10*hsec)
    302 
    303     @property
    304     @netlink.nlattr(type=int, immutable=True, fmt=util.num)
    305     def last_update(self):
    306         """Last update"""
    307         hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr)
    308         return datetime.timedelta(milliseconds=10*hsec)
    309 
    310     def add(self, socket=None, flags=None):
    311         if not socket:
    312             socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
    313 
    314         if not flags:
    315             flags = netlink.NLM_F_CREATE
    316 
    317         ret = capi.rtnl_addr_add(socket._sock, self._rtnl_addr, flags)
    318         if ret < 0:
    319             raise netlink.KernelError(ret)
    320 
    321     def delete(self, socket, flags=0):
    322         """Attempt to delete this address in the kernel"""
    323         ret = capi.rtnl_addr_delete(socket._sock, self._rtnl_addr, flags)
    324         if ret < 0:
    325             raise netlink.KernelError(ret)
    326 
    327     ###################################################################
    328     # private properties
    329     #
    330     # Used for formatting output. USE AT OWN RISK
    331     @property
    332     def _flags(self):
    333         return ','.join(self.flags)
    334 
    335     def format(self, details=False, stats=False, nodev=False, indent=''):
    336         """Return address as formatted text"""
    337         fmt = util.MyFormatter(self, indent)
    338 
    339         buf = fmt.format('{a|local!b}')
    340 
    341         if not nodev:
    342             buf += fmt.format(' {a|ifindex}')
    343 
    344         buf += fmt.format(' {a|scope}')
    345 
    346         if self.label:
    347             buf += fmt.format(' "{a|label}"')
    348 
    349         buf += fmt.format(' <{a|_flags}>')
    350 
    351         if details:
    352             buf += fmt.nl('\t{t|broadcast} {t|multicast}') \
    353                  + fmt.nl('\t{t|peer} {t|anycast}')
    354 
    355             if self.valid_lifetime:
    356                 buf += fmt.nl('\t{s|valid-lifetime!k} '\
    357                        '{a|valid_lifetime}')
    358 
    359             if self.preferred_lifetime:
    360                 buf += fmt.nl('\t{s|preferred-lifetime!k} '\
    361                        '{a|preferred_lifetime}')
    362 
    363         if stats and (self.create_time or self.last_update):
    364             buf += self.nl('\t{s|created!k} {a|create_time}'\
    365                    ' {s|last-updated!k} {a|last_update}')
    366 
    367         return buf
    368