Beispiel #1
0
def proxy_linkinfo(data, nl):

    marshal = MarshalRtnl()
    inbox = marshal.parse(data)
    data = b''
    for msg in inbox:
        if msg['event'] == 'NLMSG_ERROR':
            data += msg.data
            continue
        # Sysfs operations can require root permissions,
        # but the script can be run under a normal user
        # Bug-Url: https://github.com/svinota/pyroute2/issues/113
        try:
            compat_fix_attrs(msg, nl)
        except OSError:
            # We can safely ignore here any OSError.
            # In the worst case, we just return what we have got
            # from the kernel via netlink
            pass

        msg.reset()
        msg.encode()
        data += msg.data

    return {'verdict': 'forward', 'data': data}
Beispiel #2
0
 def __init__(self, *argv, **kwarg):
     if 'family' in kwarg:
         kwarg.pop('family')
     super(IPRSocketMixin, self).__init__(NETLINK_ROUTE, *argv[1:], **kwarg)
     self.marshal = MarshalRtnl()
     self._s_channel = None
     if sys.platform.startswith('linux'):
         self._gate = self._gate_linux
         self.sendto_gate = self._gate_linux
         send_ns = Namespace(self, {'addr_pool': AddrPool(0x10000,
                                                          0x1ffff),
                                    'monitor': False})
         self._sproxy = NetlinkProxy(policy='return', nl=send_ns)
         self._sproxy.pmap = {rtnl.RTM_NEWLINK: proxy_newlink,
                              rtnl.RTM_SETLINK: proxy_setlink}
         if config.kernel < [3, 3, 0]:
             self._recv_ns = Namespace(self,
                                       {'addr_pool': AddrPool(0x20000,
                                                              0x2ffff),
                                        'monitor': False})
             self._sproxy.pmap[rtnl.RTM_DELLINK] = proxy_dellink
             # inject proxy hooks into recv() and...
             self.__recv = self._recv
             self._recv = self._p_recv
             # ... recv_into()
             self._recv_ft = self.recv_ft
             self.recv_ft = self._p_recv_ft
Beispiel #3
0
 def __init__(self, *argv, **kwarg):
     self.marshal = MarshalRtnl()
     send_ns = Namespace(self, {
         'addr_pool': AddrPool(0x10000, 0x1ffff),
         'monitor': False
     })
     self._sproxy = NetlinkProxy(policy='return', nl=send_ns)
Beispiel #4
0
 def __init__(self, *argv, **kwarg):
     if 'ssh' in kwarg:
         self._ssh = ['ssh', kwarg.pop('ssh')]
     else:
         self._ssh = []
     async_qsize = kwarg.get('async_qsize')
     self._ifc = Ifconfig(cmd=self._ssh + ['ifconfig', '-a'])
     self._arp = ARP(cmd=self._ssh + ['arp', '-an'])
     self._route = Route(cmd=self._ssh + ['netstat', '-rn'])
     self.marshal = MarshalRtnl()
     send_ns = Namespace(self, {
         'addr_pool': AddrPool(0x10000, 0x1ffff),
         'monitor': False
     })
     self._sproxy = NetlinkProxy(policy='return', nl=send_ns)
     self._mon_th = None
     self._rtm = None
     self._brd_socket = None
     self._pfdr, self._pfdw = os.pipe()  # notify external poll/select
     self._ctlr, self._ctlw = os.pipe()  # notify monitoring thread
     self._outq = queue.Queue(maxsize=async_qsize or config.async_qsize)
     self._system_lock = threading.Lock()
     self.closed = threading.Event()
Beispiel #5
0
class IPRSocketMixin(object):
    def __init__(self, *argv, **kwarg):
        if 'family' in kwarg:
            kwarg.pop('family')
        super(IPRSocketMixin, self).__init__(NETLINK_ROUTE, *argv[1:], **kwarg)
        self.marshal = MarshalRtnl()
        self._s_channel = None
        if sys.platform.startswith('linux'):
            self._gate = self._gate_linux
            self.sendto_gate = self._gate_linux
            send_ns = Namespace(
                self,
                {
                    'addr_pool': AddrPool(0x10000, 0x1FFFF),
                    'monitor': False
                },
            )
            self._sproxy = NetlinkProxy(policy='return', nl=send_ns)
            self._sproxy.pmap = {
                rtnl.RTM_NEWLINK: proxy_newlink,
                rtnl.RTM_SETLINK: proxy_setlink,
            }
            if config.kernel < [3, 3, 0]:
                self._recv_ns = Namespace(
                    self,
                    {
                        'addr_pool': AddrPool(0x20000, 0x2FFFF),
                        'monitor': False,
                    },
                )
                self._sproxy.pmap[rtnl.RTM_DELLINK] = proxy_dellink
                # inject proxy hooks into recv() and...
                self.__recv = self._recv
                self._recv = self._p_recv
                # ... recv_into()
                self._recv_ft = self.recv_ft
                self.recv_ft = self._p_recv_ft

    def bind(self, groups=rtnl.RTMGRP_DEFAULTS, **kwarg):
        super(IPRSocketMixin, self).bind(groups, **kwarg)

    def _gate_linux(self, msg, addr):
        msg.reset()
        msg.encode()
        ret = self._sproxy.handle(msg)
        if ret is not None:
            if ret['verdict'] == 'forward':
                return self._sendto(ret['data'], addr)
            elif ret['verdict'] in ('return', 'error'):
                if self._s_channel is not None:
                    return self._s_channel.send(ret['data'])
                else:
                    msgs = self.marshal.parse(ret['data'])
                    for msg in msgs:
                        seq = msg['header']['sequence_number']
                        if seq in self.backlog:
                            self.backlog[seq].append(msg)
                        else:
                            self.backlog[seq] = [msg]
                    return len(ret['data'])
            else:
                ValueError('Incorrect verdict')

        return self._sendto(msg.data, addr)

    def _p_recv_ft(self, bufsize, flags=0):
        data = self._recv_ft(bufsize, flags)
        ret = proxy_linkinfo(data, self._recv_ns)
        if ret is not None:
            if ret['verdict'] in ('forward', 'error'):
                return ret['data']
            else:
                ValueError('Incorrect verdict')

        return data

    def _p_recv(self, bufsize, flags=0):
        data = self.__recv(bufsize, flags)
        ret = proxy_linkinfo(data, self._recv_ns)
        if ret is not None:
            if ret['verdict'] in ('forward', 'error'):
                return ret['data']
            else:
                ValueError('Incorrect verdict')

        return data
Beispiel #6
0
class IPRoute(object):
    def __init__(self, *argv, **kwarg):
        if 'ssh' in kwarg:
            self._ssh = ['ssh', kwarg.pop('ssh')]
        else:
            self._ssh = []
        async_qsize = kwarg.get('async_qsize')
        self._ifc = Ifconfig(cmd=self._ssh + ['ifconfig', '-a'])
        self._arp = ARP(cmd=self._ssh + ['arp', '-an'])
        self._route = Route(cmd=self._ssh + ['netstat', '-rn'])
        self.marshal = MarshalRtnl()
        send_ns = Namespace(self, {
            'addr_pool': AddrPool(0x10000, 0x1ffff),
            'monitor': False
        })
        self._sproxy = NetlinkProxy(policy='return', nl=send_ns)
        self._mon_th = None
        self._rtm = None
        self._brd_socket = None
        self._pfdr, self._pfdw = os.pipe()  # notify external poll/select
        self._ctlr, self._ctlw = os.pipe()  # notify monitoring thread
        self._outq = queue.Queue(maxsize=async_qsize or config.async_qsize)
        self._system_lock = threading.Lock()
        self.closed = threading.Event()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def clone(self):
        return self

    def close(self, code=errno.ECONNRESET):
        with self._system_lock:
            if self.closed.is_set():
                return

            if self._mon_th is not None:
                os.write(self._ctlw, b'\0')
                self._mon_th.join()
                self._rtm.close()

            if code > 0:
                self._outq.put(struct.pack('IHHQIQQ', 28, 2, 0, 0, code, 0, 0))
            os.write(self._pfdw, b'\0')
            for ep in (self._pfdr, self._pfdw, self._ctlr, self._ctlw):
                try:
                    os.close(ep)
                except OSError:
                    pass
            self.closed.set()

    def bind(self, *argv, **kwarg):
        with self._system_lock:
            if self._mon_th is not None:
                return

            if self._ssh:
                return

            self._mon_th = threading.Thread(target=self._monitor_thread,
                                            name='PF_ROUTE monitoring')
            self._mon_th.setDaemon(True)
            self._mon_th.start()

    def _monitor_thread(self):
        # Monitoring thread to convert arriving PF_ROUTE data into
        # the netlink format, enqueue it and notify poll/select.
        self._rtm = RTMSocket(output='netlink')
        inputs = [self._rtm.fileno(), self._ctlr]
        outputs = []
        while True:
            try:
                events, _, _ = select.select(inputs, outputs, inputs)
            except:
                continue
            for fd in events:
                if fd == self._ctlr:
                    # Main thread <-> monitor thread protocol is
                    # pretty simple: discard the data and terminate
                    # the monitor thread.
                    os.read(self._ctlr, 1)
                    return
                else:
                    # Read the data from the socket and queue it
                    msg = self._rtm.get()
                    if msg is not None:
                        msg.encode()
                        self._outq.put(msg.data)
                        # Notify external poll/select
                        os.write(self._pfdw, b'\0')

    def fileno(self):
        # Every time when some new data arrives, one should write
        # into self._pfdw one byte to kick possible poll/select.
        #
        # Resp. recv() discards one byte from self._pfdr each call.
        return self._pfdr

    def get(self):
        data = self.recv()
        return self.marshal.parse(data)

    def recv(self, bufsize=None):
        os.read(self._pfdr, 1)
        return self._outq.get()

    def getsockopt(self, *argv, **kwarg):
        return 1024 * 1024

    def sendto_gate(self, msg, addr):
        #
        # handle incoming netlink requests
        #
        # sendto_gate() receives single RTNL messages as objects
        #
        cmd = msg['header']['type']
        flags = msg['header']['flags']
        seq = msg['header']['sequence_number']

        # work only on dump requests for now
        if flags != NLM_F_REQUEST | NLM_F_DUMP:
            return

        #
        if cmd == RTM_GETLINK:
            rtype = RTM_NEWLINK
            ret = self.get_links()
        elif cmd == RTM_GETADDR:
            rtype = RTM_NEWADDR
            ret = self.get_addr()
        elif cmd == RTM_GETROUTE:
            rtype = RTM_NEWROUTE
            ret = self.get_routes()
        elif cmd == RTM_GETNEIGH:
            rtype = RTM_NEWNEIGH
            ret = self.get_neighbours()

        #
        # set response type and finalize the message
        for r in ret:
            r['header']['type'] = rtype
            r['header']['flags'] = NLM_F_MULTI
            r['header']['sequence_number'] = seq

        #
        r = type(msg)()
        r['header']['type'] = NLMSG_DONE
        r['header']['sequence_number'] = seq
        ret.append(r)

        data = b''
        for r in ret:
            r.encode()
            data += r.data
        self._outq.put(data)
        os.write(self._pfdw, b'\0')

    # 8<---------------------------------------------------------------
    #
    def dump(self):
        '''
        Iterate all the objects -- links, routes, addresses etc.
        '''
        for method in (self.get_links, self.get_addr, self.get_neighbours,
                       self.get_routes):
            for msg in method():
                yield msg

    # 8<---------------------------------------------------------------

    def get_links(self, *argv, **kwarg):
        ret = []
        data = self._ifc.run()
        parsed = self._ifc.parse(data)
        for name, spec in parsed['links'].items():
            msg = ifinfmsg().load(spec)
            msg['header']['type'] = RTM_NEWLINK
            del msg['value']
            flags = msg['flags']
            new_flags = 0
            for value, name in IFF_VALUES.items():
                if value & flags and name in IFF_NAMES:
                    new_flags |= IFF_NAMES[name]
            msg['flags'] = new_flags
            ret.append(msg)
        return ret

    def get_addr(self, *argv, **kwarg):
        ret = []
        data = self._ifc.run()
        parsed = self._ifc.parse(data)
        for name, specs in parsed['addrs'].items():
            for spec in specs:
                msg = ifaddrmsg().load(spec)
                msg['header']['type'] = RTM_NEWADDR
                del msg['value']
                ret.append(msg)
        return ret

    def get_neighbours(self, *argv, **kwarg):
        ifc = self._ifc.parse(self._ifc.run())
        arp = self._arp.parse(self._arp.run())
        ret = []
        for spec in arp:
            if spec['ifname'] not in ifc['links']:
                continue
            spec['ifindex'] = ifc['links'][spec['ifname']]['index']
            msg = ndmsg().load(spec)
            msg['header']['type'] = RTM_NEWNEIGH
            del msg['value']
            ret.append(msg)
        return ret

    def get_routes(self, *argv, **kwarg):
        ifc = self._ifc.parse(self._ifc.run())
        rta = self._route.parse(self._route.run())
        ret = []
        for spec in rta:
            if spec['ifname'] not in ifc['links']:
                continue
            idx = ifc['links'][spec['ifname']]['index']
            spec['attrs'].append(['RTA_OIF', idx])
            msg = rtmsg().load(spec)
            msg['header']['type'] = RTM_NEWROUTE
            del msg['value']
            ret.append(msg)
        return ret
Beispiel #7
0
 def __init__(self, fileno=None):
     super(RawIPRSocketMixin, self).__init__(NETLINK_ROUTE, fileno=fileno)
     self.marshal = MarshalRtnl()