示例#1
0
class TestIW(object):
    def setup(self):
        try:
            self.iw = IW()
        except NetlinkError as e:
            if e.code == errno.ENOENT:
                raise SkipTest('nl80211 not supported')
            else:
                raise
        ifaces = self.iw.get_interfaces_dump()
        if not ifaces:
            raise SkipTest('no wireless interfaces found')
        for i in ifaces:
            self.ifname = i.get_attr('NL80211_ATTR_IFNAME')
            self.ifindex = i.get_attr('NL80211_ATTR_IFINDEX')
            self.wiphy = i.get_attr('NL80211_ATTR_WIPHY')
            if self.ifindex:
                return
        raise Exception('can not detect the interface to use')

    def teardown(self):
        self.iw.close()

    def test_list_wiphy(self):
        self.iw.list_wiphy()

    def test_list_dev(self):
        self.iw.list_dev()

    def test_scan(self):
        require_user('root')
        with IPRoute() as ipr:
            ipr.link('set', index=self.ifindex, state='up')
        self.iw.scan(self.ifindex)
示例#2
0
class TestIW(object):

    def setup(self):
        try:
            self.iw = IW()
        except NetlinkError as e:
            if e.code == errno.ENOENT:
                raise SkipTest('nl80211 not supported')
            else:
                raise
        ifaces = self.iw.get_interfaces_dump()
        if not ifaces:
            raise SkipTest('no wireless interfaces found')
        for i in ifaces:
            self.ifname = i.get_attr('NL80211_ATTR_IFNAME')
            self.ifindex = i.get_attr('NL80211_ATTR_IFINDEX')
            self.wiphy = i.get_attr('NL80211_ATTR_WIPHY')
            if self.ifindex:
                return
        raise Exception('can not detect the interface to use')

    def teardown(self):
        self.iw.close()

    def test_list_wiphy(self):
        self.iw.list_wiphy()

    def test_list_dev(self):
        self.iw.list_dev()

    def test_scan(self):
        require_user('root')
        with IPRoute() as ipr:
            ipr.link('set', index=self.ifindex, state='up')
        self.iw.scan(self.ifindex)
示例#3
0
class TestIW(object):

    def setup(self):
        try:
            self.iw = IW()
        except NetlinkError as e:
            if e.code == errno.ENOENT:
                raise SkipTest('nl80211 not supported')
            else:
                raise
        ifaces = self.iw.get_interfaces_dump()
        if not ifaces:
            raise SkipTest('no wireless interfaces found')
        self.ifname = ifaces[0].get_attr('NL80211_ATTR_IFNAME')
        self.ifindex = ifaces[0].get_attr('NL80211_ATTR_IFINDEX')
        self.wiphy = ifaces[0].get_attr('NL80211_ATTR_WIPHY')

    def teardown(self):
        self.iw.close()

    def test_list_wiphy(self):
        self.iw.list_wiphy()

    def test_list_dev(self):
        self.iw.list_dev()

    def test_scan(self):
        require_user('root')
        self.iw.scan(self.ifindex)
示例#4
0
class TestIW(object):
    def setup(self):
        try:
            self.iw = IW()
        except NetlinkError as e:
            if e.code == errno.ENOENT:
                raise SkipTest('nl80211 not supported')
            else:
                raise
        ifaces = self.iw.get_interfaces_dump()
        if not ifaces:
            raise SkipTest('no wireless interfaces found')
        self.ifname = ifaces[0].get_attr('NL80211_ATTR_IFNAME')
        self.ifindex = ifaces[0].get_attr('NL80211_ATTR_IFINDEX')
        self.wiphy = ifaces[0].get_attr('NL80211_ATTR_WIPHY')

    def teardown(self):
        self.iw.close()

    def test_list_wiphy(self):
        self.iw.list_wiphy()

    def test_list_dev(self):
        self.iw.list_dev()

    def test_scan(self):
        require_user('root')
        self.iw.scan(self.ifindex)
示例#5
0
 def setup(self):
     try:
         self.iw = IW()
     except NetlinkError as e:
         if e.code == errno.ENOENT:
             raise SkipTest('nl80211 not supported')
         else:
             raise
     ifaces = self.iw.get_interfaces_dump()
     if not ifaces:
         raise SkipTest('no wireless interfaces found')
     self.ifname = ifaces[0].get_attr('NL80211_ATTR_IFNAME')
     self.ifindex = ifaces[0].get_attr('NL80211_ATTR_IFINDEX')
     self.wiphy = ifaces[0].get_attr('NL80211_ATTR_WIPHY')
示例#6
0
    def _associate(self):

        """
        Send an Association request.
        """

        err_occurred = False

        # Prepare the RSN IEs
        information_elements = bytes()
        if self._rsn:
            information_elements = bytes(self._rsn)

        if self._ms_wpa_ie:
            information_elements += bytes(self._ms_wpa_ie)

        try:
            with IW() as iw: # pylint: disable-msg=C0103
                g_default_logger.info("Trying to associate ...")
                iw.associate(
                    self._iface_index,
                    self._bssid,
                    self._ssid,
                    self._frequency,
                    info_elements=information_elements
                )
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
            err_occurred = True

            # At this point, the association process probably failed
            while err_occurred:
                time.sleep(0.05)
                try:
                    with IW() as iw: # pylint: disable-msg=C0103
                        g_default_logger.info("Trying to associate ...")
                        iw.associate(
                            self._iface_index,
                            self._bssid,
                            self._ssid,
                            self._frequency,
                            info_elements=information_elements
                        )
                        err_occurred = False
                except NetlinkError as netlink_err_:
                    g_exception_logger.exception(netlink_err_)
                    err_occurred = True

        return err_occurred
示例#7
0
    def _detect_association_loss(self):
        association_loss = False

        if self._previously_associated:
            with IW() as iw: # pylint: disable-msg=C0103
                data = iw.get_stations(self._iface_index)
            if data:
                for attr in data[0]["attrs"]:
                    if attr[0] == 'NL80211_ATTR_STA_INFO':
                        for sta_info in attr[1]["attrs"]:
                            if sta_info[0] == 'NL80211_STA_INFO_STA_FLAGS':
                                if not sta_info[1]["AUTHENTICATED"]:
                                    association_loss = True
                                    self._authentication_status = \
                                        WiFiTrafficHandler.\
AUTH_STATUS_NOT_AUTHENTICATED
                                if not sta_info[1]["ASSOCIATED"]:
                                    association_loss = True
                                    self._association_status = \
                                        WiFiTrafficHandler.\
ASSO_STATUS_NOT_ASSOCIATED

            else:
                # Assume that we're no longer associated
                association_loss = True
                self._association_status = \
                    WiFiTrafficHandler.ASSO_STATUS_NOT_ASSOCIATED
                self._authentication_status = \
                    WiFiTrafficHandler.AUTH_STATUS_NOT_AUTHENTICATED

        return association_loss
示例#8
0
 def setup(self):
     try:
         self.iw = IW()
     except NetlinkError as e:
         if e.code == errno.ENOENT:
             raise SkipTest('nl80211 not supported')
         else:
             raise
     ifaces = self.iw.get_interfaces_dump()
     if not ifaces:
         raise SkipTest('no wireless interfaces found')
     for i in ifaces:
         self.ifname = i.get_attr('NL80211_ATTR_IFNAME')
         self.ifindex = i.get_attr('NL80211_ATTR_IFINDEX')
         self.wiphy = i.get_attr('NL80211_ATTR_WIPHY')
         if self.ifindex:
             return
     raise Exception('can not detect the interface to use')
示例#9
0
def remove_monitor_interface(monitor_iface_name):
    """
    Remove the virtual monitor interface that should have been added at
    startup.
    """

    index = get_interface_index(monitor_iface_name)
    if index:
        try:
            with IW() as iw:
                iw.del_interface(index)
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
示例#10
0
 def setup(self):
     try:
         self.iw = IW()
     except NetlinkError as e:
         if e.code == errno.ENOENT:
             raise SkipTest('nl80211 not supported')
         else:
             raise
     ifaces = self.iw.get_interfaces_dump()
     if not ifaces:
         raise SkipTest('no wireless interfaces found')
     self.ifname = ifaces[0].get_attr('NL80211_ATTR_IFNAME')
     self.ifindex = ifaces[0].get_attr('NL80211_ATTR_IFINDEX')
     self.wiphy = ifaces[0].get_attr('NL80211_ATTR_WIPHY')
    def add_interface(self, ifaceName, mode, **kwargs):
        if ifaceName in pyw.winterfaces():
            return False

        iw = IW()
        mode2int = {
            'adhoc': 1,
            'sta': 2,
            'station': 2,
            'managed': 2,
            'ap': 3,
            'ap_vlan': 4,
            'wds': 5,
            'monitor': 6,
            'mesh_point': 7,
            'p2p_client': 8,
            'p2p_go': 9,
            'p2p_device': 10,
            'ocb': 11
        }

        modeInt = mode2int.get(mode, None)
        if modeInt is None:
            return False

        iw.add_interface(ifaceName, modeInt, None, 0)
        ''' Other option:
        w0 = pyw.getcard('wlan1‘)
        if 'monitor' in pyw.devmodes(w0):
            m0 = pyw.devadd(w0,'mon0','monitor’)

           pyw.winterfaces()
           pyw.up(m0) # bring the new card up to use
           pyw.chset(m0,6,None)
        '''

        return True
示例#12
0
def is_wireless_interface(interface_index):
    """
    Return True if the interface which index is interface_index is a wireless
    interface, False otherwise.
    """

    is_valid = True

    try:
        with IW() as iw:
            iw.get_interface_by_ifindex(interface_index)
    except NetlinkError:
        is_valid = False

    return is_valid
示例#13
0
def get_wireless_interface_mac_addr(interface_index):
    """
    Return the MAC address of a wireless interface.
    """

    mac_address = None

    try:
        with IW() as iw:
            data = iw.get_interface_by_ifindex(interface_index)
            if data:
                mac_address = data[0].get_attr("NL80211_ATTR_MAC")
    except NetlinkError as netlink_err:
        g_exception_logger.exception(netlink_err)

    return mac_address
示例#14
0
    def _authenticate(self):
        """
        Send an Authentication frame.
        """

        err_occurred = False

        try:
            with IW() as iw:  # pylint: disable-msg=C0103
                iw.authenticate(self._iface_index, self._bssid, self._ssid,
                                self._frequency)
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
            err_occurred = True

        return err_occurred
示例#15
0
    def _scan(self):

        """
        Perform a scan and retrieve information related to the target SSID.
        """

        err_occurred = False
        bss_status_attr = None
        found = False
        rsn_ie_data = None
        vendor_ie_data = None

        try:
            with IW() as iw: # pylint: disable-msg=C0103
                scan_results = iw.scan(
                    self._iface_index,
                    [self._ssid],
                    flush_cache=True
                )
                bss_attr = None
                for result in scan_results:
                    bss_attr = result.get_attr('NL80211_ATTR_BSS')
                    if bss_attr is not None:
                        attrs = bss_attr["attrs"]
                        bss_ie = [x[1] for x in attrs if x[0] == \
                            "NL80211_BSS_INFORMATION_ELEMENTS"]
                        if bss_ie:
                            # Get SSID
                            ssid = bss_ie[0].get("SSID", None)
                            if ssid.decode("ascii") == self._ssid:
                                # Get RSN IEs
                                rsn_ie_data = bss_ie[0].get("RSN", None)
                                vendor_ie_data = bss_ie[0].get("VENDOR", None)
                                found = True
                                break
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
            err_occurred = True

        if found:
            self._bssid = bss_attr.get_attr("NL80211_BSS_BSSID")
            self._frequency = bss_attr.get_attr("NL80211_BSS_FREQUENCY")
            from_presp = bss_attr.get_attr('NL80211_BSS_PRESP_DATA')
            self._ssid_info_learned_in_presp = True if from_presp else False
            bss_status_attr = bss_attr.get_attr("NL80211_BSS_STATUS")

        return err_occurred, bss_status_attr, rsn_ie_data, vendor_ie_data
示例#16
0
    def _disassociate(self):
        """
        Send a Disassociation request.
        """

        err_occurred = False

        try:
            with IW() as iw:  # pylint: disable-msg=C0103
                iw.disassociate(self._iface_index,
                                self._bssid,
                                reason_code=0x08)
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
            err_occurred = True

        return err_occurred
示例#17
0
    def _deauthenticate(self):
        """
        Send a Deauthentication frame.
        """

        err_occurred = False

        try:
            with IW() as iw:  # pylint: disable-msg=C0103
                iw.deauthenticate(self._iface_index,
                                  self._bssid,
                                  reason_code=0x01)
        except NetlinkError as netlink_err:
            g_exception_logger.exception(netlink_err)
            err_occurred = True

        return err_occurred
示例#18
0
 def setup(self):
     try:
         self.iw = IW()
     except NetlinkError as e:
         if e.code == errno.ENOENT:
             raise SkipTest('nl80211 not supported')
         else:
             raise
     ifaces = self.iw.get_interfaces_dump()
     if not ifaces:
         raise SkipTest('no wireless interfaces found')
     for i in ifaces:
         self.ifname = i.get_attr('NL80211_ATTR_IFNAME')
         self.ifindex = i.get_attr('NL80211_ATTR_IFINDEX')
         self.wiphy = i.get_attr('NL80211_ATTR_WIPHY')
         if self.ifindex:
             return
     raise Exception('can not detect the interface to use')
示例#19
0
def setup_wireless_interfaces(wireless_iface_name):
    """
    Create a monitor interface ("ra0"), if it does not exist already.
    Bring the wireless interface up if necessary.
    """

    setup_complete = True

    # Get interface index
    iface_index = get_interface_index(wireless_iface_name)

    # Check if it is a provided interface
    if not is_wireless_interface(iface_index):
        setup_complete = False
        g_exception_logger.error("%s is not a wireless interface.",\
wireless_iface_name)
    else:
        if not get_interface_index(MONITOR_INTERFACE_NAME):
            # Create a monitor interface
            try:
                with IW() as iw:
                    iw.add_interface(MONITOR_INTERFACE_NAME, 6, iface_index)
            except NetlinkError as netlink_err:
                g_exception_logger.exception(netlink_err)
                return False

        # Set the monitor interface "administratively" up
        if not set_interface_admin_state(MONITOR_INTERFACE_NAME,
                                         admin_up=True):
            g_exception_logger.error("Could not set %s administratively up",\
MONITOR_INTERFACE_NAME)
            setup_complete = False

        # Set the interface "administratively" up
        if not set_interface_admin_state(wireless_iface_name, admin_up=True):
            g_exception_logger.error("Could not set %s administratively up",\
wireless_iface_name)
            setup_complete = False

    return setup_complete
示例#20
0
from pyroute2 import IW
from pyroute2 import IPRoute
from pyroute2.netlink import NetlinkError

# interface name to check
ifname = 'lo'

ip = IPRoute()
iw = IW()
index = ip.link_lookup(ifname=ifname)[0]
try:
    iw.get_interface_by_ifindex(index)
    print("wireless interface")
except NetlinkError as e:
    if e.code == 19:  # 19 'No such device'
        print("not a wireless interface")
finally:
    iw.close()
    ip.close()
示例#21
0
from pyroute2 import IW


# register IW to get all the messages
iw = IW(groups=~0)
print(iw.get())
iw.close()
示例#22
0
from pyroute2 import IW


# register IW to get all the messages
iw = IW(groups=0xFFF)
print(iw.get())
iw.close()
示例#23
0
class EthernetManager:
    # RTNL interface
    ndb = NDB(log="on")
    # WIFI interface
    iw = IW()
    # IP abstraction interface
    ipr = IPRoute()

    result: List[EthernetInterface] = []

    def __init__(self, default_config: EthernetInterface,
                 dhcp_gateway: str) -> None:
        self.settings = settings.Settings()

        self._config_path = pathlib.Path(__file__).parent.absolute().joinpath(
            "settings", "dnsmasq.conf")
        self._server = Dnsmasq(self._config_path)
        self._dhcp_server_gateway = dhcp_gateway

        # Load settings and do the initial configuration
        if not self.settings.load():
            logger.error(
                f"Failed to load previous settings. Using default configuration: {default_config}"
            )
            self.set_configuration(default_config)
            return

        logger.info("Previous settings loaded:")
        for item in self.settings.root["content"]:
            logger.info(f"Configuration with: {item}")
            if not self.set_configuration(EthernetInterface(**item)):
                logger.error("Failed.")

    def save(self) -> None:
        """Save actual configuration"""
        try:
            self.get_interfaces()
        except Exception as exception:
            logger.error(
                f"Failed to fetch actual configuration, going to use the previous info: {exception}"
            )

        if not self.result:
            logger.error("Configuration is empty, aborting.")
            return

        result = [
            interface.dict(exclude={"info"}) for interface in self.result
        ]
        self.settings.save(result)

    def set_configuration(self, interface: EthernetInterface) -> bool:
        """Modify hardware based in the configuration

        Args:
            interface: EthernetInterface

        Returns:
            bool: Configuration was accepted
        """
        interfaces = self.get_interfaces()
        logger.debug(f"Found following ethernet interfaces: {interfaces}.")
        valid_names = [interface.name for interface in interfaces]

        name = interface.name
        ip = interface.configuration.ip
        mode = interface.configuration.mode

        if name not in valid_names:
            logger.error(
                f"Invalid interface name ('{name}'). Valid names are: {valid_names}"
            )
            return False

        if mode != InterfaceMode.Server and self._server.is_running():
            self._server.stop()

        if mode == InterfaceMode.Client:
            self.set_dynamic_ip(name)
            logger.info(f"Interface '{name}' configured with dynamic IP.")
            return True
        if mode == InterfaceMode.Server:
            self.set_static_ip(name, self._dhcp_server_gateway)
            if not self._server.is_running():
                self._server.start()
            logger.info(
                f"Interface '{name}' configured as DHCP server with static IP."
            )
            return True
        if mode == InterfaceMode.Unmanaged:
            self.set_static_ip(name, ip)
            logger.info(f"Interface '{name}' configured with static IP.")
            return True

        logger.error(f"Could not configure interface '{name}'.")
        return False

    def _get_wifi_interfaces(self) -> List[str]:
        """Get wifi interface list

        Returns:
            list: List with the name of the wifi interfaces
        """
        interfaces = self.iw.list_dev()
        result = []
        for interface in interfaces:
            for flag, value in interface["attrs"]:
                # Extract interface name from IFNAME flag
                if flag == "NL80211_ATTR_IFNAME":
                    result += [value]
        return result

    def is_valid_interface_name(self, interface_name: str) -> bool:
        """Check if an interface name is valid

        Args:
            interface_name (str): Network interface name

        Returns:
            bool: True if valid, False if not
        """
        blacklist = ["lo", "ham.*", "docker.*"]
        wifi_interfaces = self._get_wifi_interfaces()
        blacklist += wifi_interfaces

        if not interface_name:
            logger.error("Interface name cannot be blank or null.")
            return False

        for pattern in blacklist:
            if re.match(pattern, interface_name):
                return False

        return True

    def validate_interface_data(self, interface: EthernetInterface) -> bool:
        """Check if interface configuration is valid

        Args:
            interface: EthernetInterface instance

        Returns:
            bool: True if valid, False if not
        """
        return self.is_valid_interface_name(interface.name)

    @staticmethod
    def weak_is_ip_address(ip: str) -> bool:
        """Check if ip address is valid

        Args:
            ip (str): ip address

        Returns:
            bool: True if valid, False if not
        """
        return re.match(r"\d+.\d+.\d+.\d+", ip) is not None

    def is_static_ip(self, ip: str) -> bool:
        """Check if ip address is static or dynamic
            For more information: https://code.woboq.org/qt5/include/linux/if_addr.h.html
                https://www.systutorials.com/docs/linux/man/8-ip-address/

        Args:
            ip (str): ip address

        Returns:
            bool: true if static false if not
        """
        for address in list(self.ipr.get_addr()):

            def get_item(items: List[Tuple[str, Any]], name: str) -> Any:
                return [value for key, value in items if key == name][0]

            if get_item(address["attrs"], "IFA_ADDRESS") != ip:
                continue

            flags = get_item(address["attrs"], "IFA_FLAGS")
            result = "IFA_F_PERMANENT" in ifaddrmsg.flags2names(flags)
            return result
        return False

    def _get_interface_index(self, interface_name: str) -> int:
        """Get interface index for internal usage

        Args:
            interface_name (str): Interface name

        Returns:
            int: Interface index
        """
        interface_index = int(self.ipr.link_lookup(ifname=interface_name)[0])
        return interface_index

    def flush_interface(self, interface_name: str) -> None:
        """Flush all ip addresses in a specific interface

        Args:
            interface_name (str): Interface name
        """
        interface_index = self._get_interface_index(interface_name)
        self.ipr.flush_addr(index=interface_index)
        logger.info(f"Flushing IP addresses from interface {interface_name}.")

    def enable_interface(self,
                         interface_name: str,
                         enable: bool = True) -> None:
        """Enable interface

        Args:
            interface_name (str): Interface name
            enable (bool, optional): Set interface status. Defaults to True
        """
        interface_index = self._get_interface_index(interface_name)
        interface_state = "up" if enable else "down"
        self.ipr.link("set", index=interface_index, state=interface_state)
        logger.info(
            f"Setting interface {interface_name} to '{interface_state}' state."
        )

    async def _trigger_dhcp_service(self, interface_name: str) -> None:
        """Internal async trigger for dhcp service

        Args:
            interface_name (str): Interface name
        """
        self.enable_interface(interface_name, False)
        await asyncio.sleep(1)
        self.enable_interface(interface_name, True)

    def trigger_dhcp_service(self, interface_name: str) -> None:
        """Trigger DHCP service via async

        Args:
            interface_name (str): Interface name
        """
        asyncio.run(self._trigger_dhcp_service(interface_name))

    def set_ip(self, interface_name: str, ip: str) -> None:
        """Set ip address for a specific interface

        Args:
            interface_name (str): Interface name
            ip (str): Desired ip address
        """
        interface_index = self._get_interface_index(interface_name)
        self.ipr.addr("add", index=interface_index, address=ip, prefixlen=24)
        logger.info(f"Setting interface {interface_name} to IP '{ip}'.")

    def set_dynamic_ip(self, interface_name: str) -> None:
        """Set interface to use dynamic ip address

        Args:
            interface_name (str): Interface name
        """
        # Remove all address
        self.flush_interface(interface_name)
        # Trigger DHCP service to add a new dynamic ip address
        self.trigger_dhcp_service(interface_name)
        logger.info(f"Getting dynamic IP to interface {interface_name}.")

    def set_static_ip(self, interface_name: str, ip: str) -> None:
        """Set interface to use static ip address

        Args:
            interface_name (str): Interface name
            ip (str): ip address
        """
        # Remove all address
        self.flush_interface(interface_name)
        # Set new ip address
        self.set_ip(interface_name, ip)

    def get_interfaces(self) -> List[EthernetInterface]:
        """Get interfaces information

        Returns:
            List of EthernetInterface instances available
        """
        result = []
        for interface, addresses in psutil.net_if_addrs().items():
            # We don't care about virtual ethernet interfaces
            ## Virtual interfaces are created by programs such as docker
            ## and they are an abstraction of real interfaces, the ones that we want to configure.
            if str(interface).startswith("veth"):
                continue

            for address in addresses:
                # We don't care about ipv6
                if address.family == AddressFamily.AF_INET6:
                    continue

                # If there is no ip address the mac address will be provided (⊙_⊙')
                valid_ip = EthernetManager.weak_is_ip_address(address.address)
                ip = address.address if valid_ip else "undefined"

                is_static_ip = self.is_static_ip(ip)
                is_gateway_ip = ip == self._dhcp_server_gateway

                # Populate our output item
                if self._server.is_running() and is_gateway_ip:
                    mode = InterfaceMode.Server
                else:
                    mode = InterfaceMode.Unmanaged if is_static_ip and valid_ip else InterfaceMode.Client
                info = self.get_interface_info(interface)
                data = EthernetInterface(name=interface,
                                         configuration=InterfaceConfiguration(
                                             ip=ip, mode=mode),
                                         info=info)

                # Check if it's valid and add to the result
                if self.validate_interface_data(data):
                    result += [data]
                    break

        self.result = result
        return result

    def get_interface_ndb(self, interface_name: str) -> Any:
        """Get interface NDB information for interface

        Args:
            interface_name (str): Interface name

        Returns:
            pyroute2.ndb.objects.interface.Interface: pyroute2 interface object
        """
        return self.ndb.interfaces.dump().filter(ifname=interface_name)[0]

    def get_interface_info(self, interface_name: str) -> InterfaceInfo:
        """Get interface info field

        Args:
            interface_name (str): Interface name

        Returns:
            InterfaceInfo object
        """
        interface = self.get_interface_ndb(interface_name)
        return InterfaceInfo(
            connected=interface.carrier != 0,
            number_of_disconnections=interface.carrier_down_count)
示例#24
0
class EthernetManager:
    # RTNL interface
    ndb = NDB(log="on")
    # WIFI interface
    iw = IW()
    # IP abstraction interface
    ipr = IPRoute()

    result: List[Dict[str, Any]] = []

    def __init__(self) -> None:
        self.settings = settings.Settings()

        # Load settings and do the initial configuration
        if not self.settings.load():
            print("Failed to load previous settings.")
            return

        print("Previous settings loaded:")
        for item in self.settings.root["content"]:
            print(f"Configuration with: {item}")
            if not self.set_configuration(item):
                print("Failed.")

    def save(self) -> None:
        """Save actual configuration"""
        try:
            self.get_interfaces()
        except Exception as exception:
            print(
                f"Failed to fetch actual configuration, going to use the previous info: {exception}"
            )

        if not self.result:
            print("Configuration is empty, aborting.")
            return

        for item in self.result:
            item.pop("info")
        self.settings.save(self.result)

    def set_configuration(self, configuration: Dict[str, Any]) -> bool:
        """Modify hardware based in the configuration

        Args:
            configuration (dict): Configuration struct
                {
                    'name': 'interface_name',
                    'configuration': {
                        'ip': 'ip_address',
                        'mode': 'mode' // [unmanaged, client, server],
                    }
                }

        Returns:
            bool: Configuration was accepted
        """
        interfaces = self.get_interfaces()
        valid_names = [interface["name"] for interface in interfaces]

        name = configuration["name"]
        ip = configuration["configuration"]["ip"]
        mode = configuration["configuration"]["mode"]

        if name not in valid_names:
            return False

        if mode == "client":
            self.set_dynamic_ip(name)
            return True
        if mode == "unmanaged":
            self.set_static_ip(name, ip)
            return True

        return False

    def _get_wifi_interfaces(self) -> List[str]:
        """Get wifi interface list

        Returns:
            list: List with the name of the wifi interfaces
        """
        interfaces = self.iw.list_dev()
        result = []
        for interface in interfaces:
            for flag, value in interface["attrs"]:
                # Extract interface name from IFNAME flag
                if flag == "NL80211_ATTR_IFNAME":
                    result += [value]
        return result

    def is_valid_interface(self, interface: str) -> bool:
        """Check if an interface is valid

        Args:
            interface (str): Network interface

        Returns:
            bool: True if valid, False if not
        """
        blacklist = ["lo", "ham.*", "docker.*"]
        blacklist += self._get_wifi_interfaces()

        if not interface:
            return False

        for pattern in blacklist:
            if re.match(pattern, interface):
                return False

        return True

    def validate_interface_data(self, data: Dict[str, Any]) -> bool:
        """Check if interface configuration is valid

        Args:
            data (dict): Interface dict structure

        Returns:
            bool: True if valid, False if not
        """
        name = data["name"]
        return self.is_valid_interface(name)

    @staticmethod
    def weak_is_ip_address(ip: str) -> bool:
        """Check if ip address is valid

        Args:
            ip (str): ip address

        Returns:
            bool: True if valid, False if not
        """
        return re.match(r"\d+.\d+.\d+.\d+", ip) is not None

    def is_static_ip(self, ip: str) -> bool:
        """Check if ip address is static or dynamic
            For more information: https://code.woboq.org/qt5/include/linux/if_addr.h.html
                https://www.systutorials.com/docs/linux/man/8-ip-address/

        Args:
            ip (str): ip address

        Returns:
            bool: true if static false if not
        """
        for address in list(self.ipr.get_addr()):

            def get_item(items: List[Tuple[str, Any]], name: str) -> Any:
                return [value for key, value in items if key == name][0]

            if get_item(address["attrs"], "IFA_ADDRESS") != ip:
                continue

            flags = get_item(address["attrs"], "IFA_FLAGS")
            result = "IFA_F_PERMANENT" in ifaddrmsg.flags2names(flags)
            return result
        return False

    def _get_interface_index(self, interface_name: str) -> int:
        """Get interface index for internal usage

        Args:
            interface_name (str): Interface name

        Returns:
            int: Interface index
        """
        interface_index = int(self.ipr.link_lookup(ifname=interface_name)[0])
        return interface_index

    def flush_interface(self, interface_name: str) -> None:
        """Flush all ip addresses in a specific interface

        Args:
            interface_name (str): Interface name
        """
        interface_index = self._get_interface_index(interface_name)
        self.ipr.flush_addr(index=interface_index)

    def enable_interface(self,
                         interface_name: str,
                         enable: bool = True) -> None:
        """Enable interface

        Args:
            interface_name (str): Interface name
            enable (bool, optional): Set interface status. Defaults to True
        """
        interface_index = self._get_interface_index(interface_name)
        interface_state = "up" if enable else "down"
        self.ipr.link("set", index=interface_index, state=interface_state)

    async def _trigger_dhcp_service(self, interface_name: str) -> None:
        """Internal async trigger for dhcp service

        Args:
            interface_name (str): Interface name
        """
        self.enable_interface(interface_name, False)
        await asyncio.sleep(1)
        self.enable_interface(interface_name, True)

    def trigger_dhcp_service(self, interface_name: str) -> None:
        """Trigger DHCP service via async

        Args:
            interface_name (str): Interface name
        """
        asyncio.run(self._trigger_dhcp_service(interface_name))

    def set_ip(self, interface_name: str, ip: str) -> None:
        """Set ip address for a specific interface

        Args:
            interface_name (str): Interface name
            ip (str): Desired ip address
        """
        interface_index = self._get_interface_index(interface_name)
        self.ipr.addr("add", index=interface_index, address=ip, prefixlen=24)

    def set_dynamic_ip(self, interface_name: str) -> None:
        """Set interface to use dynamic ip address

        Args:
            interface_name (str): Interface name
        """
        # Remove all address
        self.flush_interface(interface_name)
        # Trigger DHCP service to add a new dynamic ip address
        self.trigger_dhcp_service(interface_name)

    def set_static_ip(self, interface_name: str, ip: str) -> None:
        """Set interface to use static ip address

        Args:
            interface_name (str): Interface name
            ip (str): ip address
        """
        # Remove all address
        self.flush_interface(interface_name)
        # Set new ip address
        self.set_ip(interface_name, ip)

    def get_interfaces(self) -> List[Dict[str, Any]]:
        """Get interfaces information

        Returns:
            dict: Interface information that uses the following struct:
            [
                {
                    'name': 'interface_name',
                    'configuration': {
                        'ip': 'ip_address',
                        'mode': 'mode' // [unmanaged, client, server],
                    },
                    'info': {
                        'connected': True,
                        'number_of_disconnections': 4,
                    }
                },
                ...
            ]
        """
        result = []
        for interface, addresses in psutil.net_if_addrs().items():
            for address in addresses:
                # We don't care about ipv6
                if address.family == AddressFamily.AF_INET6:
                    continue

                # If there is no ip address the mac address will be provided (⊙_⊙')
                valid_ip = EthernetManager.weak_is_ip_address(address.address)
                ip = address.address if valid_ip else "undefined"

                is_static_ip = self.is_static_ip(ip)

                # Populate our output item
                mode = "unmanaged" if is_static_ip and valid_ip else "client"
                info = self.get_interface_info(interface)
                data = {
                    "name": interface,
                    "configuration": {
                        "ip": ip,
                        "mode": mode
                    },
                    "info": info,
                }

                # Check if it's valid and add to the result
                if self.validate_interface_data(data):
                    result += [data]
                    break

        self.result = result
        return result

    def get_interface_ndb(self, interface_name: str) -> Any:
        """Get interface NDB information for interface

        Args:
            interface_name (str): Interface name

        Returns:
            pyroute2.ndb.objects.interface.Interface: pyroute2 interface object
        """
        return self.ndb.interfaces.dump().filter(ifname=interface_name)[0]

    def get_interface_info(self, interface_name: str) -> Dict[str, Any]:
        """Get interface info field

        Args:
            interface_name (str): Interface name

        Returns:
            dict: Info field of `get_interfaces`
        """
        interface = self.get_interface_ndb(interface_name)
        return {
            "connected": interface.carrier != 0,
            "number_of_disconnections": interface.carrier_down_count,
        }
示例#25
0
from . import hal
import threading,time
try:
    from pyroute2 import IW
    from pyroute2 import IPRoute
    ip = IPRoute()
    #to be able to scan for networks python executable must have cap_net_admin flag
    #sudo setcap cap_net_admin,cap_net_raw+ep /usr/bin/python3.8
    iw = IW()
except:pass
class IPRoute2Interface(hal.NetworkInterface):
    def __init__(self, id, parent=None):
        hal.NetworkInterface.__init__(self,id,parent)
        self.idx = ip.link_lookup(ifname=id)[0]    
    def up(self,Up=True): 
        try:
            if Up:
                ip.link('set', index=self.idx, state='up')
            else:
                ip.link('set', index=self.idx, state='down')
            return True
        except:
            return False
    def down(self):
        return self.up(False)
    def addAddress(self,address,pefixlen=24):
        try:
            ip.addr('add', index=self.idx, address=address, prefixlen=prefixlen)
            return True
        except:
            return False
示例#26
0
from pyroute2 import IW

# register IW to get all the messages
iw = IW(groups=0xfff)
print(iw.get())
iw.close()
示例#27
0
class EthernetManager:
    # RTNL interface
    ndb = NDB(log="on")
    # WIFI interface
    iw = IW()
    # IP abstraction interface
    ipr = IPRoute()

    result: List[EthernetInterface] = []

    def __init__(self, default_config: EthernetInterface) -> None:
        self.settings = settings.Settings()

        self._dhcp_servers: List[DHCPServerManager] = []

        # Load settings and do the initial configuration
        if not self.settings.load():
            logger.error(
                f"Failed to load previous settings. Using default configuration: {default_config}"
            )
            try:
                self.set_configuration(default_config)
            except Exception as error:
                logger.error(f"Failed loading default configuration. {error}")
            return

        logger.info("Loading previous settings.")
        for item in self.settings.root["content"]:
            logger.info(f"Loading following configuration: {item}.")
            try:
                self.set_configuration(EthernetInterface(**item))
            except Exception as error:
                logger.error(f"Failed loading saved configuration. {error}")

    def save(self) -> None:
        """Save actual configuration"""
        try:
            self.get_interfaces()
        except Exception as exception:
            logger.error(
                f"Failed to fetch actual configuration, going to use the previous info: {exception}"
            )

        if not self.result:
            logger.error("Configuration is empty, aborting.")
            return

        result = [
            interface.dict(exclude={"info"}) for interface in self.result
        ]
        self.settings.save(result)

    def set_configuration(self, interface: EthernetInterface) -> None:
        """Modify hardware based in the configuration

        Args:
            interface: EthernetInterface
        """
        interfaces = self.get_interfaces()
        logger.debug(f"Found following ethernet interfaces: {interfaces}.")
        valid_names = [interface.name for interface in interfaces]

        if interface.name not in valid_names:
            raise ValueError(
                f"Invalid interface name ('{interface.name}'). Valid names are: {valid_names}"
            )

        # Reset the interface by removing all IPs and DHCP servers associated with it
        self.flush_interface(interface.name)
        self.remove_dhcp_server_from_interface(interface.name)

        # Even if it happened to receive more than one dynamic IP, only one trigger is necessary
        if any(address.mode == AddressMode.Client
               for address in interface.addresses):
            self.trigger_dynamic_ip_acquisition(interface.name)

        for address in interface.addresses:
            if address.mode == AddressMode.Unmanaged:
                self.add_static_ip(interface.name, address.ip)
            elif address.mode == AddressMode.Server:
                self.add_dhcp_server_to_interface(interface.name, address.ip)

    def _get_wifi_interfaces(self) -> List[str]:
        """Get wifi interface list

        Returns:
            list: List with the name of the wifi interfaces
        """
        interfaces = self.iw.list_dev()
        result = []
        for interface in interfaces:
            for flag, value in interface["attrs"]:
                # Extract interface name from IFNAME flag
                if flag == "NL80211_ATTR_IFNAME":
                    result += [value]
        return result

    def is_valid_interface_name(self, interface_name: str) -> bool:
        """Check if an interface name is valid

        Args:
            interface_name (str): Network interface name

        Returns:
            bool: True if valid, False if not
        """
        blacklist = ["lo", "ham.*", "docker.*", "veth.*"]
        wifi_interfaces = self._get_wifi_interfaces()
        blacklist += wifi_interfaces

        if not interface_name:
            logger.error("Interface name cannot be blank or null.")
            return False

        for pattern in blacklist:
            if re.match(pattern, interface_name):
                return False

        return True

    def validate_interface_data(self, interface: EthernetInterface) -> bool:
        """Check if interface configuration is valid

        Args:
            interface: EthernetInterface instance

        Returns:
            bool: True if valid, False if not
        """
        return self.is_valid_interface_name(interface.name)

    @staticmethod
    def _is_server_address_present(interface: EthernetInterface) -> bool:
        return any(address.mode == AddressMode.Server
                   for address in interface.addresses)

    @staticmethod
    def weak_is_ip_address(ip: str) -> bool:
        """Check if ip address is valid

        Args:
            ip (str): ip address

        Returns:
            bool: True if valid, False if not
        """
        return re.match(r"\d+.\d+.\d+.\d+", ip) is not None

    def is_static_ip(self, ip: str) -> bool:
        """Check if ip address is static or dynamic
            For more information: https://code.woboq.org/qt5/include/linux/if_addr.h.html
                https://www.systutorials.com/docs/linux/man/8-ip-address/

        Args:
            ip (str): ip address

        Returns:
            bool: true if static false if not
        """
        for address in list(self.ipr.get_addr()):

            def get_item(items: List[Tuple[str, Any]], name: str) -> Any:
                return [value for key, value in items if key == name][0]

            if get_item(address["attrs"], "IFA_ADDRESS") != ip:
                continue

            flags = get_item(address["attrs"], "IFA_FLAGS")
            result = "IFA_F_PERMANENT" in ifaddrmsg.flags2names(flags)
            return result
        return False

    def _get_interface_index(self, interface_name: str) -> int:
        """Get interface index for internal usage

        Args:
            interface_name (str): Interface name

        Returns:
            int: Interface index
        """
        interface_index = int(self.ipr.link_lookup(ifname=interface_name)[0])
        return interface_index

    def flush_interface(self, interface_name: str) -> None:
        """Flush all ip addresses in a specific interface

        Args:
            interface_name (str): Interface name
        """
        interface_index = self._get_interface_index(interface_name)
        self.ipr.flush_addr(index=interface_index)
        logger.info(f"Flushing IP addresses from interface {interface_name}.")

    def enable_interface(self,
                         interface_name: str,
                         enable: bool = True) -> None:
        """Enable interface

        Args:
            interface_name (str): Interface name
            enable (bool, optional): Set interface status. Defaults to True
        """
        interface_index = self._get_interface_index(interface_name)
        interface_state = "up" if enable else "down"
        self.ipr.link("set", index=interface_index, state=interface_state)
        logger.info(
            f"Setting interface {interface_name} to '{interface_state}' state."
        )

    def trigger_dynamic_ip_acquisition(self, interface_name: str) -> None:
        """Trigger external DHCP servers to possibly aquire a dynamic IP by restarting the interface.

        Args:
            interface_name (str): Interface name
        """
        logger.info(
            f"Restaring interface {interface_name} to trigger dynamic IP acquisition."
        )
        self.enable_interface(interface_name, False)
        time.sleep(1)
        self.enable_interface(interface_name, True)

    def add_static_ip(self, interface_name: str, ip: str) -> None:
        """Set ip address for a specific interface

        Args:
            interface_name (str): Interface name
            ip (str): Desired ip address
        """
        logger.info(
            f"Adding static IP '{ip}' to interface '{interface_name}'.")
        interface_index = self._get_interface_index(interface_name)
        self.ipr.addr("add", index=interface_index, address=ip, prefixlen=24)

    def remove_ip(self, interface_name: str, ip_address: str) -> None:
        """Delete IP address appended on the interface

        Args:
            interface_name (str): Interface name
            ip_address (str): IP address to be deleted
        """
        logger.info(
            f"Deleting IP {ip_address} from interface {interface_name}.")
        try:
            if (self._is_dhcp_server_running_on_interface(interface_name) and
                    self._dhcp_server_on_interface(interface_name).ipv4_gateway
                    == ip_address):
                self.remove_dhcp_server_from_interface(interface_name)
            interface_index = self._get_interface_index(interface_name)
            self.ipr.addr("del",
                          index=interface_index,
                          address=ip_address,
                          prefixlen=24)
        except Exception as error:
            raise RuntimeError(
                f"Cannot delete IP '{ip_address}' from interface {interface_name}."
            ) from error

    def get_interface_by_name(self, name: str) -> EthernetInterface:
        for interface in self.get_interfaces():
            if interface.name == name:
                return interface
        raise ValueError(f"No interface with name '{name}' is present.")

    def get_interfaces(self) -> List[EthernetInterface]:
        """Get interfaces information

        Returns:
            List of EthernetInterface instances available
        """
        result = []
        for interface, addresses in psutil.net_if_addrs().items():
            # We don't care about virtual ethernet interfaces
            ## Virtual interfaces are created by programs such as docker
            ## and they are an abstraction of real interfaces, the ones that we want to configure.
            if not self.is_valid_interface_name(interface):
                continue

            valid_addresses = []
            for address in addresses:
                # We just care about IPV4 addresses
                if not address.family == AddressFamily.AF_INET:
                    continue

                valid_ip = EthernetManager.weak_is_ip_address(address.address)
                ip = address.address if valid_ip else "undefined"

                is_static_ip = self.is_static_ip(ip)

                # Populate our output item
                if (self._is_dhcp_server_running_on_interface(interface) and
                        self._dhcp_server_on_interface(interface).ipv4_gateway
                        == ip):
                    mode = AddressMode.Server
                else:
                    mode = AddressMode.Unmanaged if is_static_ip and valid_ip else AddressMode.Client
                valid_addresses.append(InterfaceAddress(ip=ip, mode=mode))

            info = self.get_interface_info(interface)
            interface_data = EthernetInterface(name=interface,
                                               addresses=valid_addresses,
                                               info=info)
            # Check if it's valid and add to the result
            if self.validate_interface_data(interface_data):
                result += [interface_data]

        self.result = result
        return result

    def get_interface_ndb(self, interface_name: str) -> Any:
        """Get interface NDB information for interface

        Args:
            interface_name (str): Interface name

        Returns:
            pyroute2.ndb.objects.interface.Interface: pyroute2 interface object
        """
        return self.ndb.interfaces.dump().filter(ifname=interface_name)[0]

    def get_interface_info(self, interface_name: str) -> InterfaceInfo:
        """Get interface info field

        Args:
            interface_name (str): Interface name

        Returns:
            InterfaceInfo object
        """
        interface = self.get_interface_ndb(interface_name)
        return InterfaceInfo(
            connected=interface.carrier != 0,
            number_of_disconnections=interface.carrier_down_count)

    def _is_ip_on_interface(self, interface_name: str,
                            ip_address: str) -> bool:
        interface = self.get_interface_by_name(interface_name)
        return any(True for address in interface.addresses
                   if address.ip == ip_address)

    def _dhcp_server_on_interface(self,
                                  interface_name: str) -> DHCPServerManager:
        try:
            return next(dhcp_server for dhcp_server in self._dhcp_servers
                        if dhcp_server.interface == interface_name)
        except StopIteration as error:
            raise ValueError(
                f"No DHCP server running on interface {interface_name}."
            ) from error

    def _is_dhcp_server_running_on_interface(self,
                                             interface_name: str) -> bool:
        try:
            return bool(self._dhcp_server_on_interface(interface_name))
        except Exception:
            return False

    def remove_dhcp_server_from_interface(
            self, interface_name: str) -> DHCPServerManager:
        logger.info(f"Removing DHCP server from interface '{interface_name}'.")
        try:
            self._dhcp_servers.remove(
                self._dhcp_server_on_interface(interface_name))
        except ValueError:
            # If the interface does not have a DHCP server running on, no need to raise
            pass
        except Exception as error:
            raise RuntimeError(
                "Cannot remove DHCP server from interface.") from error

    def add_dhcp_server_to_interface(self, interface_name: str,
                                     ipv4_gateway: str) -> None:
        if self._is_dhcp_server_running_on_interface(interface_name):
            self.remove_dhcp_server_from_interface(interface_name)
        if self._is_ip_on_interface(interface_name, ipv4_gateway):
            self.remove_ip(interface_name, ipv4_gateway)
        self.add_static_ip(interface_name, ipv4_gateway)
        logger.info(
            f"Adding DHCP server with gateway '{ipv4_gateway}' to interface '{interface_name}'."
        )
        self._dhcp_servers.append(
            DHCPServerManager(interface_name, ipv4_gateway))

    def stop(self) -> None:
        """Perform steps necessary to properly stop the manager."""
        for dhcp_server in self._dhcp_servers:
            dhcp_server.stop()

    def __del__(self) -> None:
        self.stop()