def __init__(self, nsp_name: str, ipdb: IPDB): """ Creats a namespace for a specific vlan_iface :param nsp_name: :param vlan_iface_name: :param ipdb: IPDB is a transactional database, containing records, representing network stack objects. Any change in the database is not reflected immidiately in OS, but waits until commit() is called. """ Logger().debug("Create Namespace ...", 2) self.nsp_name = nsp_name self.id = id self.vlan_iface_name = "" self.vlan_iface_ip = "0.0.0.0" self.ipdb = ipdb self.ipdb_netns = None try: self.ipdb_netns = IPDB(nl=NetNS(nsp_name)) netns.setns(nsp_name) self.ipdb_netns.interfaces['lo'].up().commit() Logger().debug("[+] Namespace(" + nsp_name + ") successfully created", 3) # self.encapsulate_interface() except Exception as e: Logger().debug("[-] Couldn't create Namespace(" + nsp_name + ")", 3) for tb in traceback.format_tb(sys.exc_info()[2]): Logger().error(tb, 3) Logger().error(str(e), 3) self.remove()
def __init__(self, ipdb: IPDB, nsp_name: str): """ Creats a namespace for a specific vlan_iface :param nsp_name: :param ipdb: IPDB is a transactional database, containing records, representing network stack objects. Any change in the database is not reflected immidiately in OS, but waits until commit() is called. """ logging.debug("%sCreate Namespace ...", LoggerSetup.get_log_deep(2)) self.ipdb = ipdb if ipdb else IPDB() self.ipdb_netns = None self.nsp_name = nsp_name try: self.ipdb_netns = IPDB(nl=NetNS(nsp_name)) self.ipdb_netns.interfaces['lo'].up().commit() logging.debug( "%s[+] Namespace(" + nsp_name + ") successfully created", LoggerSetup.get_log_deep(3)) # self.encapsulate_interface() except Exception as e: logging.debug("%s[-] Couldn't create Namespace(" + nsp_name + ")", LoggerSetup.get_log_deep(3)) for tb in traceback.format_tb(sys.exc_info()[2]): logging.error("%s" + tb, LoggerSetup.get_log_deep(3)) logging.error("%s" + str(e), LoggerSetup.get_log_deep(3)) self.remove()
def start(self) -> None: """Start host Raises: HostUpException: If host is already running. """ if self.__ns is not None: raise HostUpException() try: self.__ns = NetNS(self.name) except FileExistsError: raise HostUpException() self.__files = _setup_etc(self.name) self.__ipdb = pyroute2.ipdb.main.IPDB(nl=self.__ns) self.__ipdb.interfaces["lo"].up().commit() if self.__manager is not None: self.__manager.register(self)
class Host(InterfaceContainer): """Host in a network container. This is a host in a networked container. Args: name: Name for the host, which is the name for the network namespace. Attributes: name: Name of the host, which is also the name of the network namespace. """ def __init__(self, name: str, manager: Manager = None) -> None: self.__ns = None self.__ipdb = None self.__manager = manager self.__files = {} self.__hostnames = [] super().__init__(name) def add_hostname(self, name: str) -> None: self.__hostnames.append(name) @property def running(self) -> bool: """True if host is running""" return self.__ns is not None @property def ipdb(self) -> pyroute2.ipdb.main.IPDB: """Return host IPDB""" if not self.running: raise HostDownException() return self.__ipdb def start(self) -> None: """Start host Raises: HostUpException: If host is already running. """ if self.__ns is not None: raise HostUpException() try: self.__ns = NetNS(self.name) except FileExistsError: raise HostUpException() self.__files = _setup_etc(self.name) self.__ipdb = pyroute2.ipdb.main.IPDB(nl=self.__ns) self.__ipdb.interfaces["lo"].up().commit() if self.__manager is not None: self.__manager.register(self) def Popen(self, *args, **kwargs): #pylint: disable=invalid-name """Popen inside the host""" mounts = tuple( (bytes(src), bytes(dst)) for src, dst in self.__files.values()) def change_ns(): """Setup the namespace""" # This is borrowed from iproute2 and looks more sane than pyroute2 # Let's move ourselves to the target network namespace try: # Change to network namespace setns(self.name, flags=0) # Unshare the mount namespace (preparation for following steps) # Unshare UTS namespace for hostname syscalls.unshare(syscalls.CLONE_NEWNS | syscalls.CLONE_NEWUTS) # Make our mounts slave (otherwise unshare doesn't help with shared mounts) syscalls.mount(b"none", b"/", None, syscalls.MS_REC | syscalls.MS_SLAVE, None) # Mount sysfs that belongs to this network namespace syscalls.umount2(b"/sys", syscalls.MNT_DETACH) syscalls.mount(b"none", b"/sys", b"sysfs", 0, None) # Set the hostname socket.sethostname(self.name) # fake hosts files etc for src, dst in mounts: syscalls.mount(src, dst, b"none", syscalls.MS_BIND, None) except Exception as err: print(err) raise return subprocess.Popen(*args, preexec_fn=change_ns, **kwargs) def stop(self) -> None: """Stop host Raises: HostDownException: If host is already stopped. """ if self.__ns is None: raise HostDownException() self.__ipdb.release() self.__ipdb = None self.__ns.close() self.__ns.remove() _remove_etc(self.name) self.__ns = None if self.__manager is not None: self.__manager.unregister(self) def set_hosts(self, hosts): with self.__files["hosts"][0].open('wb') as hostfile: hostfile.write(DEFAULT_HOSTS) for host, address, hostnames in hosts: hostfile.write("{}\t{}\t{}\n".format( address, host, " ".join(hostnames)).encode()) def get_hostnames(self): return [(self.name, address.ip, self.__hostnames) for interface in self.interfaces.values() for address in interface.addresses] def _find_gateway(self, addrtype): # search the network for any router and use it as default gateway for intf in self.interfaces.values(): if not isinstance(intf, VirtualInterface): continue addresses = list( filter(lambda x: isinstance(x, addrtype), intf.addresses)) intf_list = set([intf]) visited = set([intf]) while intf_list: peer, peerintf = intf_list.pop().peer() if peer.router: gateway = _compatible_address(addresses, peerintf.addresses) if gateway: self.ipdb.routes.add({ 'dst': 'default', 'gateway': str(gateway) }).commit() return if peer.switch: new_intfs = set( filter( lambda x: x not in visited and isinstance( x, VirtualInterface), peer.interfaces.values())) intf_list.extend(new_intfs) visited.extend(new_intfs) def find_routes(self): if { 'dst': 'default', 'family': socket.AF_INET } not in self.ipdb.routes: self._find_gateway(ipaddress.IPv4Interface) if { 'dst': 'default', 'family': socket.AF_INET6 } not in self.ipdb.routes: self._find_gateway(ipaddress.IPv6Interface)