예제 #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}
예제 #2
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}
예제 #3
0
class IPRoute(object):
    def __init__(self, *argv, **kwarg):
        self._ifc = Ifconfig()
        self._arp = ARP()
        self._route = Route()
        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._pfdr, self._pfdw = os.pipe()  # notify external poll/select
        self._ctlr, self._ctlw = os.pipe()  # notify monitoring thread
        self._outq = queue.Queue()
        self._system_lock = threading.Lock()

    def __enter__(self):
        return self

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

    def close(self):
        with self._system_lock:
            if self._mon_th is not None:
                os.write(self._ctlw, b'\0')
                self._mon_th.join()
                self._rtm.close()
                for ep in (self._pfdr, self._pfdw, self._ctlr, self._ctlw):
                    try:
                        os.close(ep)
                    except OSError:
                        pass

    def bind(self, *argv, **kwarg):
        with self._system_lock:
            if self._mon_th is not None:
                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')

    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)
            del msg['value']
            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)
                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:
            spec['ifindex'] = ifc['links'][spec['ifname']]['index']
            msg = ndmsg().load(spec)
            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:
            idx = ifc['links'][spec['ifname']]['index']
            spec['attrs'].append(['RTA_OIF', idx])
            msg = rtmsg().load(spec)
            del msg['value']
            ret.append(msg)
        return ret
예제 #4
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
예제 #5
0
파일: bsd.py 프로젝트: svinota/pyroute2
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')

    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
예제 #6
0
파일: nldecap.py 프로젝트: ffourcot/nldecap
def main(args):
    """Parse arguments, read the pcap and parse nl packets with pyroute2"""
    psr = ArgumentParser(description=__doc__.splitlines()[0])
    psr.add_argument("pcap",
                     type=FileType("rb"),
                     help="The pcap file to read, or - for stdin")
    psr.add_argument("-p",
                     "--pprint",
                     help="use pprint() for pretty-printing messages instead "
                     "of the builtin tree-like display",
                     default=False,
                     action="store_true")
    psr.add_argument("-l",
                     "--log-level",
                     choices=LOG_LEVELS,
                     default="info",
                     help="Log level. 'info' (the default) prints a header "
                     "for each packet, 'debug' prints information about "
                     "skipped packets, 'warn' only prints packet or "
                     "message decoding errors.")
    psr.add_argument("filter",
                     nargs="*",
                     help="Only display messages of this type. "
                     "Can be specified multiple times.")
    args = psr.parse_args(args)
    for i in args.filter:
        if i not in MSG_TYPES:
            psr.error("Invalid filter '%s' (choose from %s)" % (i, MSG_TYPES))

    LOG.setLevel(args.log_level.upper())

    # Open the pcap file (read its header)
    try:
        pcap_file = NLPcap(args.pcap)
    except PcapError as pce:
        psr.error("%r: %s" % (args.pcap.name, pce))

    # Use the built in marshal for decoding
    marshal = MarshalRtnl()

    # The function that will be used for printing
    print_func = pprint if args.pprint else nl_pprint

    # Loop over the pcap file
    for packet in pcap_file:
        # Parse the packet content and display all contained messages
        try:
            messages = marshal.parse(packet)
        except StructError:
            LOG.warn("[packet %d] could not parse %r", pcap_file.pkt_count,
                     packet)
            continue

        for msg_num, msg in enumerate(messages, start=1):
            msg_type = MSG_MAP.get(msg["header"]["type"], "unknown type")
            if args.filter and msg_type not in args.filter:
                continue

            LOG.info("[packet %d] message %d (%s)", pcap_file.pkt_count,
                     msg_num, msg_type)
            print_func(msg)
    return 0