Beispiel #1
0
class NICMonitor(threading.Thread):
    def __init__(self, context):
        threading.Thread.__init__(self)
        self.logger = logging.getLogger(__name__)
        self.context = context
        self.ip = IPRSocket()
        self.ip.bind(rtnl.RTNLGRP_LINK | rtnl.RTNLGRP_NOTIFY)
        self.control = None
        self.state = ''
        self.carrier = -1

    def setup(self):
        self.logger.info('NICMon:setup()')
        self.control = self.context.socket(zmq.PAIR)
        self.control.bind(const.fmNICMonitorEndpoint)
        return self.control

    def run(self):
        self.command = self.context.socket(zmq.PAIR)
        self.command.connect(const.fmNICMonitorEndpoint)
        self.poller = zmq.Poller()
        self.poller.register(self.ip._sock, zmq.POLLIN)
        self.poller.register(self.command, zmq.POLLIN)
        while True:
            sockets = dict(self.poller.poll())
            if self.ip._sock.fileno() in sockets:
                infos = self.ip.get()
                for info in infos:
                    attrs = dict(info['attrs'])
                    ifname = attrs['IFLA_IFNAME']
                    state = attrs['IFLA_OPERSTATE']
                    carrier = attrs['IFLA_CARRIER']
                    if ifname == Config.NIC_NAME and state != self.state and carrier != self.carrier:
                        self.logger.info(
                            "NICMonitor: state has changed (%s,%d)" %
                            (state, carrier))
                        if state != 'UP' or carrier != 1:  # NIC down and/or carrier lost
                            info = ('nic-', )
                            self.command.send_pyobj(info)
                        elif state == 'UP' and carrier == 1:  # NIC UP and carrier is on
                            info = ('nic+', )
                            self.command.send_pyobj(info)
                        self.state, self.carrier = state, carrier
                del sockets[self.ip._sock.fileno()]
            elif self.command in sockets:
                msg = self.command.recv_pyobj()
                if msg == 'stop':
                    break
                del sockets[self.command]
        self.command.close()
        self.ip.close()

    def terminate(self):
        self.control.send_pyobj('stop')
Beispiel #2
0
def scan_netdevs():
    scan = []
    nl_socket = IPRSocket()
    msg = ifinfmsg()
    msg["family"] = socket.AF_UNSPEC
    msg["header"]["type"] = RTM_GETLINK
    msg["header"]["flags"] = NLM_F_REQUEST | NLM_F_DUMP
    msg["header"]["pid"] = os.getpid()
    msg["header"]["sequence_number"] = 1
    msg.encode()

    nl_socket.sendto(msg.buf.getvalue(), (0,0))

    finished = False
    while not finished:
        parts = nl_socket.get()
        for part in parts:
            if part["header"]["type"] in [NLMSG_DONE, NLMSG_ERROR]:
                finished = True
                continue
            if part["header"]["sequence_number"] != 1:
                continue

            if part["header"]["type"] == RTM_NEWLINK:
                new_link = {}
                new_link["netlink_msg"] = part
                new_link["index"] = part["index"]
                new_link["name"] = part.get_attr("IFLA_IFNAME")

                hwaddr = part.get_attr("IFLA_ADDRESS")
                new_link["hwaddr"] = normalize_hwaddr(hwaddr)

                scan.append(new_link)

    nl_socket.close()
    return scan
Beispiel #3
0
class InterfaceManager(object):
    def __init__(self, server_handler):
        self._device_classes = {}

        self._devices = {}  #ifindex to device

        self._nl_socket = IPRSocket()
        self._nl_socket.bind(groups=NL_GROUPS)

        self._msg_queue = deque()

        #TODO split DevlinkManager away from the InterfaceManager
        #self._dl_manager = DevlinkManager()

        self._server_handler = server_handler

    def clear_dev_classes(self):
        self._device_classes = {}

    def add_device_class(self, name, cls):
        if name in self._device_classes:
            raise InterfaceManagerError("Device class name conflict %s" % name)

        self._device_classes[name] = cls
        return cls

    def reconnect_netlink(self):
        if self._nl_socket != None:
            self._nl_socket.close()
            self._nl_socket = None
        self._nl_socket = IPRSocket()
        self._nl_socket.bind(groups=NL_GROUPS)

        self.rescan_devices()

    def get_nl_socket(self):
        return self._nl_socket

    def pull_netlink_messages_into_queue(self):
        try:
            while True:
                rl, wl, xl = select.select([self._nl_socket], [], [], 0)
                if not len(rl):
                    break
                self._msg_queue.extend(self._nl_socket.get())
        except socket.error:
            self.reconnect_netlink()
            return []

    def rescan_devices(self):
        self.request_netlink_dump()
        self.handle_netlink_msgs()

    def request_netlink_dump(self):
        self._nl_socket.put(None,
                            RTM_GETLINK,
                            msg_flags=NLM_F_REQUEST | NLM_F_DUMP)
        self._nl_socket.put(None,
                            RTM_GETADDR,
                            msg_flags=NLM_F_REQUEST | NLM_F_DUMP)

    def handle_netlink_msgs(self):
        self.pull_netlink_messages_into_queue()

        while len(self._msg_queue):
            msg = self._msg_queue.popleft()
            self._handle_netlink_msg(msg)

        # self._dl_manager.rescan_ports()
        # for device in self._devices.values():
        # dl_port = self._dl_manager.get_port(device.name)
        # device._set_devlink(dl_port)

    def _handle_netlink_msg(self, msg):
        if msg['header']['type'] in [RTM_NEWLINK, RTM_NEWADDR, RTM_DELADDR]:
            if msg['index'] in self._devices:
                self._devices[msg['index']]._update_netlink(msg)
            elif msg['header']['type'] == RTM_NEWLINK:
                dev = self._device_classes["Device"](self)
                dev._init_netlink(msg)
                self._devices[msg['index']] = dev

                update_msg = {
                    "type": "dev_created",
                    "dev_data": dev._get_if_data()
                }
                self._server_handler.send_data_to_ctl(update_msg)

                dev._disable()
        elif msg['header']['type'] == RTM_DELLINK:
            if msg['family'] == PF_BRIDGE:
                return

            if msg['index'] in self._devices:
                dev = self._devices[msg['index']]
                dev._deleted = True

                del self._devices[msg['index']]

                del_msg = {"type": "dev_deleted", "ifindex": msg['index']}
                self._server_handler.send_data_to_ctl(del_msg)
        else:
            return

    def untrack_device(self, dev):
        if dev.ifindex in self._devices:
            del self._devices[dev.ifindex]

    def get_device(self, ifindex):
        self.rescan_devices()
        if ifindex in self._devices:
            return self._devices[ifindex]
        else:
            raise DeviceNotFound()

    def get_devices(self):
        self.rescan_devices()
        return list(self._devices.values())

    def get_device_by_hwaddr(self, hwaddr):
        self.rescan_devices()
        for dev in list(self._devices.values()):
            if dev.hwaddr == hwaddr:
                return dev
        raise DeviceNotFound()

    def get_device_by_name(self, name):
        self.rescan_devices()
        for dev in list(self._devices.values()):
            if dev.name == name:
                return dev
        raise DeviceNotFound()

    def get_device_by_params(self, params):
        self.rescan_devices()
        matched = None
        for dev in list(self._devices.values()):
            matched = dev
            dev_data = dev.get_if_data()
            for key, value in params.items():
                if key not in dev_data or dev_data[key] != value:
                    matched = None
                    break

            if matched:
                break

        return matched

    def deconfigure_all(self):
        for dev in self._devices.values():
            pass
            # dev.clear_configuration()

    def create_device(self, clsname, args=[], kwargs={}):
        devcls = self._device_classes[clsname]

        try:
            device = devcls(self, *args, **kwargs)
        except KeyError as e:
            raise DeviceConfigError("%s is a mandatory argument" % e)
        device._create()
        device._bulk_enabled = False

        self.request_netlink_dump()
        self.pull_netlink_messages_into_queue()

        device_found = False
        while len(self._msg_queue):
            msg = self._msg_queue.popleft()
            if msg.get_attr("IFLA_IFNAME") == device.name:
                device_found = True
                device._init_netlink(msg)
                self._devices[msg['index']] = device
            else:
                self._handle_netlink_msg(msg)

        if device_found:
            return device
        else:
            raise DeviceError("Device creation failed")

    def replace_dev(self, if_id, dev):
        del self._devices[if_id]
        self._devices[if_id] = dev

    def _is_name_used(self, name):
        self.rescan_devices()
        for device in self._devices.values():
            if name == device.name:
                return True

        out, _ = exec_cmd("ovs-vsctl --columns=name list Interface",
                          log_outputs=False,
                          die_on_err=False)
        for line in out.split("\n"):
            m = re.match(r'.*: \"(.*)\"', line)
            if m is not None:
                if name == m.group(1):
                    return True
        return False

    def assign_name(self, prefix):
        index = 0
        while (self._is_name_used(prefix + str(index))):
            index += 1
        return prefix + str(index)

    def _assign_name_pair(self, prefix):
        index1 = 0
        index2 = 0
        while (self._is_name_used(prefix + str(index1))):
            index1 += 1
        index2 = index1 + 1
        while (self._is_name_used(prefix + str(index2))):
            index2 += 1
        return prefix + str(index1), prefix + str(index2)
Beispiel #4
0
    "10.200.0.2": "02:42:0a:00:00:02",
    "10.200.0.3": "02:42:0a:00:00:03"
}

ctn_fib = {
    "02:42:0a:00:00:02": "10.200.129.100",
    "02:42:0a:00:00:03": "10.200.128.218"
}

logging.basicConfig(format='%(levelname)s %(message)s', level=logging.INFO)
ipr = IPRoute()
s = IPRSocket()
s.bind()

while True:
    msg = s.get()
    for m in msg:
        logging.debug('Received an event: {}'.format(m['event']))
        if m['event'] != 'RTM_GETNEIGH':
            continue

        logging.debug("Received a Neighbor miss")

        ifindex = m['ifindex']
        ifname = ipr.get_links(ifindex)[0].get_attr("IFLA_IFNAME")
        logging.debug("Family: {}".format(
            if_family.get(m['family'], m['family'])))
        logging.debug("Interface: {} (index: {})".format(ifname, ifindex))
        logging.debug("NUD State: {}".format(
            nud_state.get(m['state'], m['state'])))
        logging.debug("Flags: {}".format(m['flags']))
Beispiel #5
0
'''
Simplest example to monitor Netlink events with a Python script.
'''

from pyroute2 import IPRSocket
from pprint import pprint

ip = IPRSocket()
ip.bind()
pprint(ip.get())
ip.close()
class NetLinkListener(Plugin):
    """This plugin reads kernel messages and is able to react to certain network events"""
    states = {"UP": True, "DOWN": False}

    def __init__(self, api, config, *args, **kw):

        super(NetLinkListener, self).__init__(api, config, *args, **kw)
        self.listen = True
        self.socket = IPRSocket()

    def _init(self, *args, **kw):
        # super(NetLinkListener, self).__init__(*args, **kw)
        self.queue = Queue.Queue()
        self.listen = False

        # add handlers here
        self.event_handlers = {
            "RTM_NEWLINK": self.rtm_newlink_handler,
            "RTM_NEWADDR": self.rtm_newaddr_handler,
            "RTM_DELADDR": self.rtm_deladdr_handler
        }

        self.api.register_connectivity_handler(self.connectivity_request)
        self.logger.debug("Connectivity request handler registered")

        self._initialized()

    def _start(self):
        self.api.run_task(self.start_listening)
        self.api.run_task(self.start_queue_handler)

        self._started()

    def _stop(self):
        self.stop_listening()
        self._stopped()

    def start_listening(self):

        if self.socket is None:
            self.socket = IPRSocket()

        self.socket.bind()

        self.logger.info("Starting listener...")
        self.listen = True
        while self.listen:
            r, _, _ = select((self.socket, ), (), (), 0.5)
            if r:
                msg_array = self.socket.get()
                for event_msg in msg_array:
                    try:
                        self.queue.put(event_msg)
                    except Queue.Full:
                        self.logger.warn(
                            "Message queue is full! it has %s elements",
                            self.queue.qsize())

        # stop after listening
        self.socket.close()
        self.logger.debug("Socket closed")

    def start_queue_handler(self):
        """Start reading the queue in a while loop until plugin stops listening to kernel messages"""

        self.logger.debug("Starting queue handler...")
        while self.listen or not self.queue.empty():
            try:
                self.handle_event(self.queue.get(timeout=0.5))
            except Queue.Empty:
                pass
        self.logger.debug("Queue handler finished")

    def stop_listening(self):
        """Stop listening to kernel messages -> shuts down queue handler"""
        self.logger.info("Stopping listener...")
        self.listen = False

    def handle_event(self, event_msg):
        """Find the handler matching the message event and run the handler with the message

        :param event_msg: event message from kernel message (kernel message is usually an array with 1 event message
        """
        event = event_msg["event"]
        # self.logger.debug("new event:\r\n%s", json.dumps(event_msg, indent=2, sort_keys=True))
        # self.logger.debug("new event: %s", event)

        handler = self.event_handlers.get(event)
        if handler is not None:
            self.logger.info("Handling event: %s", event)
            handler(event_msg)
        else:
            # self.logger.debug("No handler for event: %s", event)
            pass

    def rtm_newlink_handler(self, msg):
        """Handler parsing RTM_NEWLINK event messages

        :param msg: RTM_NEWLINK event message
        """
        name = str()
        state = str()
        for attr in msg["attrs"]:
            if attr[0] == "IFLA_IFNAME":
                name = attr[1]
            elif attr[0] == "IFLA_OPERSTATE":
                state = attr[1]

        if name == str():
            self.logger.debug(
                "No IFLA_IFNAME attribute found. Using index to assume interface"
            )
            name = self.get_interface_name_from_index(msg)

        if name != str() and state != str():
            try:
                interface = self.get_interface(name)
                if self.states[state]:
                    self.api.events.interface_created.fire(interface)
                else:
                    self.api.events.interface_removed.fire(interface)
            except InterfaceNotFoundException:
                self.logger.error(
                    "RTM_NEWLINK: Unable to fire event for interface state change: %s is an unknown interface",
                    name)
                self.logger.debug("Debug print of skipped message:\r\n%s",
                                  json.dumps(msg, indent=2, sort_keys=True))
        else:
            self.logger.warn(
                "RTM_NEWLINK: interface not found or unknown state in msg!")
            self.logger.debug("Debug print of skipped message:\r\n%s",
                              json.dumps(msg, indent=2, sort_keys=True))

    def rtm_newaddr_handler(self, msg):
        """Handler parsing RTM_NEWADDR event messages

        :param msg: RTM_NEWADDR event message
        """
        interface = str()
        address = str()
        for attr in msg["attrs"]:
            if attr[0] == "IFA_LABEL":
                interface = attr[1]
            elif attr[0] == "IFA_ADDRESS":
                address = attr[1]

        if interface == str():
            self.logger.debug(
                "No IFA_LABEL attribute found. Using index to assume interface"
            )
            interface = self.get_interface_name_from_index(msg)

        if interface != str() and address != str():
            try:
                self.api.events.address_created.fire(
                    self.get_interface(interface),
                    Address(address=address, family=msg["family"]))
            except InterfaceNotFoundException:
                self.logger.error(
                    "RTM_NEWADDR: Unable to fire address_created event: %s is an unknown interface",
                    interface)
                self.logger.debug("Debug print of skipped message:\r\n%s",
                                  json.dumps(msg, indent=2, sort_keys=True))
        else:
            self.logger.warn(
                "RTM_NEWADDR: interface and/or address not found in msg!")
            self.logger.debug("Debug print of skipped message:\r\n%s",
                              json.dumps(msg, indent=2, sort_keys=True))

    def rtm_deladdr_handler(self, msg):
        """Handler parsing RTM_DELADDR event messages

        :param msg: RTM_DELADDR event message
        """
        interface = str()
        address = str()
        for attr in msg["attrs"]:
            if attr[0] == "IFA_LABEL":
                interface = attr[1]
            elif attr[0] == "IFA_ADDRESS":
                address = attr[1]

        if interface == str():
            self.logger.debug(
                "No IFA_LABEL attribute found. Using index to assume interface"
            )
            interface = self.get_interface_name_from_index(msg)

        if interface != str() and address != str():
            try:
                self.api.events.address_removed.fire(
                    self.get_interface(interface),
                    Address(address=address, family=msg["family"]))
            except InterfaceNotFoundException:
                self.logger.error(
                    "RTM_DELADDR: Unable to fire address_removed event: %s is an unknown interface",
                    interface)
                self.logger.debug("Debug print of skipped message:\r\n%s",
                                  json.dumps(msg, indent=2, sort_keys=True))
        else:
            self.logger.warn(
                "RTM_DELADDR: interface and/or address not found in msg!")
            self.logger.debug("Debug print of skipped message:\r\n%s",
                              json.dumps(msg, indent=2, sort_keys=True))

    def get_addresses(self, interface):
        """Get addresses of a given interface

        :param interface: name of interface
        :return: list of addresses
        """
        n_addresses = netifaces.ifaddresses(interface)
        addresses = []
        for family in n_addresses:
            for addr in n_addresses[family]:
                addresses.append(Address(address=addr["addr"], family=family))

        return addresses

    def get_interface(self, name):
        """Returns an Interface object identified by name

        :param name: name of interface
        :return Interface: interface
        :raise UnknownInterface: if interface was not found
        """
        if name not in netifaces.interfaces():
            raise InterfaceNotFoundException("%s was not found" % name)
        else:
            addresses = self.get_addresses(name)
            hwaddress = [
                addr for addr in addresses if addr[1] == netifaces.AF_LINK
            ][0]
            return Interface(name=name,
                             addresses=addresses,
                             hwaddress=hwaddress)

    def get_interface_name_from_index(self, msg):
        """Parse event message and try to assume interface from index attribute

        :param msg: event message
        :return: interface name corresponding to index value, or empty string if unsuccessful
        """
        interface = str()
        try:
            interface = netifaces.interfaces()[msg["index"] - 1]
            self.logger.debug("Index: %s -> %s", msg["index"], interface)
        except IndexError:
            self.logger.debug("Unable to get interface from index")
        finally:
            return interface

    def connectivity_request(self, rcat=0):
        """Handles connectivity requests"""
        with Promise() as p:
            blacklist = ['lo']
            interfaces = netifaces.interfaces()

            interface = next((x for x in interfaces if (x not in blacklist)),
                             None)

            if interface is None:
                p.reject(
                    InterfaceNotFoundException(
                        "No interfaces found matching request"))
            else:
                p.fulfill((self.get_interface(interface), 0))

        return p