def watch_interfaces(self): """ Detects when interfaces appear, sending notifications to the update splitter. :returns: Never returns. """ # Create the netlink socket and bind to RTMGRP_LINK, s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((os.getpid(), RTMGRP_LINK)) # A dict that remembers the detailed flags of an interface # when we last signalled it as being up. We use this to avoid # sending duplicate interface_update signals. if_last_flags = {} while True: # Get the next set of data. data = s.recv(65535) # First 16 bytes is the message header; unpack it. hdr = data[:16] data = data[16:] msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", hdr) if msg_type == NLMSG_NOOP: # Noop - get some more data. continue elif msg_type == NLMSG_ERROR: # We have got an error. Raise an exception which brings the # process down. raise RTNetlinkError("Netlink error message, header : %s", futils.hex(hdr)) _log.debug("Netlink message type %s len %s", msg_type, msg_len) if msg_type in [RTM_NEWLINK, RTM_DELLINK]: # A new or removed interface. Read the struct # ifinfomsg, which is 16 bytes. hdr = data[:16] data = data[16:] _, _, _, index, flags, _ = struct.unpack("=BBHiII", hdr) _log.debug("Interface index %s flags %x", index, flags) # Bytes left is the message length minus the two headers of 16 # bytes each. remaining = msg_len - 32 # Loop through attributes, looking for the pieces of # information that we need. ifname = None operstate = None while remaining: # The data content is an array of RTA objects, each of # which has a 4 byte header and some data. rta_len, rta_type = struct.unpack("=HH", data[:4]) # This check comes from RTA_OK, and terminates a string of # routing attributes. if rta_len < 4: break rta_data = data[4:rta_len] # Remove the RTA object from the data. The length to jump # is the rta_len rounded up to the nearest 4 byte boundary. increment = int((rta_len + 3) / 4) * 4 data = data[increment:] remaining -= increment if rta_type == IFLA_IFNAME: ifname = rta_data[:-1] _log.debug("IFLA_IFNAME: %s", ifname) elif rta_type == IFLA_OPERSTATE: operstate, = struct.unpack("=B", rta_data[:1]) _log.debug("IFLA_OPERSTATE: %s", operstate) if (ifname and (msg_type == RTM_DELLINK or operstate != IF_OPER_UP)): # The interface is down; make sure the other actors know # about it. self.update_splitter.on_interface_update(ifname, iface_up=False) # Remove any record we had of the interface so that, when # it goes back up, we'll report that. if_last_flags.pop(ifname, None) if (ifname and msg_type == RTM_NEWLINK and operstate == IF_OPER_UP and (ifname not in if_last_flags or if_last_flags[ifname] != flags)): # We only care about notifying when a new # interface is usable, which - according to # https://www.kernel.org/doc/Documentation/networking/ # operstates.txt - is fully conveyed by the # operstate. (When an interface goes away, it # automatically takes its routes with it.) _log.debug("New network interface : %s %x", ifname, flags) if_last_flags[ifname] = flags self.update_splitter.on_interface_update(ifname, iface_up=True)
def watch_interfaces(self): """ Detects when interfaces appear, sending notifications to the update splitter. :returns: Never returns. """ # Create the netlink socket and bind to RTMGRP_LINK, s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((os.getpid(), RTMGRP_LINK)) while True: # Get the next set of data. data = s.recv(65535) # First 16 bytes is the message header; unpack it. hdr = data[:16] data = data[16:] msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", hdr) if msg_type == NLMSG_NOOP: # Noop - get some more data. continue elif msg_type == NLMSG_ERROR: # We have got an error. Raise an exception which brings the # process down. raise RTNetlinkError("Netlink error message, header : %s", futils.hex(hdr)) # Now 16 bytes of netlink header. hdr = data[:16] data = data[16:] family, _, if_type, index, flags, change = struct.unpack("=BBHiII", hdr) # Bytes left is the message length minus the two headers of 16 # bytes each. remaining = msg_len - 32 while remaining: # The data content is an array of RTA objects, each of which # has a 4 byte header and some data. rta_len, rta_type = struct.unpack("=HH", data[:4]) # This check comes from RTA_OK, and terminates a string of # routing attributes. if rta_len < 4: break rta_data = data[4:rta_len] # Remove the RTA object from the data. The length to jump is # the rta_len rounded up to the nearest 4 byte boundary. increment = int((rta_len + 3) / 4) * 4 data = data[increment:] remaining -= increment if rta_type == IFLA_IFNAME: # We only really care about NEWLINK messages; if an # interface goes away, we don't need to care (since it # takes its routes with it, and the interface will # presumably go away too). We do log though, just in case. rta_data = rta_data[:-1] if msg_type == RTM_NEWLINK: _log.debug("Detected new network interface : %s", rta_data) self.update_splitter.on_interface_update(rta_data, async=True) else: _log.debug("Network interface has gone away : %s", rta_data)
def watch_interfaces(self): """ Detects when interfaces appear, sending notifications to the update splitter. :returns: Never returns. """ # Create the netlink socket and bind to RTMGRP_LINK, s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) s.bind((os.getpid(), RTMGRP_LINK)) while True: # Get the next set of data. data = s.recv(65535) # First 16 bytes is the message header; unpack it. hdr = data[:16] data = data[16:] msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", hdr) if msg_type == NLMSG_NOOP: # Noop - get some more data. continue elif msg_type == NLMSG_ERROR: # We have got an error. Raise an exception which brings the # process down. raise RTNetlinkError("Netlink error message, header : %s", futils.hex(hdr)) # Now 16 bytes of netlink header. hdr = data[:16] data = data[16:] family, _, if_type, index, flags, change = struct.unpack( "=BBHiII", hdr) # Bytes left is the message length minus the two headers of 16 # bytes each. remaining = msg_len - 32 while remaining: # The data content is an array of RTA objects, each of which # has a 4 byte header and some data. rta_len, rta_type = struct.unpack("=HH", data[:4]) # This check comes from RTA_OK, and terminates a string of # routing attributes. if rta_len < 4: break rta_data = data[4:rta_len] # Remove the RTA object from the data. The length to jump is # the rta_len rounded up to the nearest 4 byte boundary. increment = int((rta_len + 3) / 4) * 4 data = data[increment:] remaining -= increment if rta_type == IFLA_IFNAME: # We only really care about NEWLINK messages; if an # interface goes away, we don't need to care (since it # takes its routes with it, and the interface will # presumably go away too). We do log though, just in case. rta_data = rta_data[:-1] if msg_type == RTM_NEWLINK: _log.debug("Detected new network interface : %s", rta_data) self.update_splitter.on_interface_update(rta_data, async=True) else: _log.debug("Network interface has gone away : %s", rta_data)