diff --git a/daemon/core/emulator/distributed.py b/daemon/core/emulator/distributed.py index bf73dce3..2b4830ad 100644 --- a/daemon/core/emulator/distributed.py +++ b/daemon/core/emulator/distributed.py @@ -196,10 +196,10 @@ class DistributedController: continue for name in self.servers: server = self.servers[name] - self.create_gre_tunnel(node, server, mtu) + self.create_gre_tunnel(node, server, mtu, True) def create_gre_tunnel( - self, node: CoreNetwork, server: DistributedServer, mtu: int + self, node: CoreNetwork, server: DistributedServer, mtu: int, start: bool ) -> Tuple[GreTap, GreTap]: """ Create gre tunnel using a pair of gre taps between the local and remote server. @@ -207,6 +207,7 @@ class DistributedController: :param node: node to create gre tunnel for :param server: server to create tunnel for :param mtu: mtu for gre taps + :param start: True to start gre taps, False otherwise :return: local and remote gre taps created for tunnel """ host = server.host @@ -216,16 +217,18 @@ class DistributedController: return tunnel # local to server logger.info("local tunnel node(%s) to remote(%s) key(%s)", node.name, host, key) - local_tap = GreTap(session=self.session, remoteip=host, key=key, mtu=mtu) - local_tap.net_client.set_iface_master(node.brname, local_tap.localname) + local_tap = GreTap(self.session, host, key=key, mtu=mtu) + if start: + local_tap.startup() + local_tap.net_client.set_iface_master(node.brname, local_tap.localname) # server to local logger.info( "remote tunnel node(%s) to local(%s) key(%s)", node.name, self.address, key ) - remote_tap = GreTap( - session=self.session, remoteip=self.address, key=key, server=server, mtu=mtu - ) - remote_tap.net_client.set_iface_master(node.brname, remote_tap.localname) + remote_tap = GreTap(self.session, self.address, key=key, server=server, mtu=mtu) + if start: + remote_tap.startup() + remote_tap.net_client.set_iface_master(node.brname, remote_tap.localname) # save tunnels for shutdown tunnel = (local_tap, remote_tap) self.tunnels[key] = tunnel diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index c2affad3..4f41a5e5 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -1496,6 +1496,7 @@ class Session: mac=utils.random_mac(), ip4=ip4, ip4_mask=ip4_mask, + mtu=DEFAULT_MTU, ) iface = node.new_iface(control_net, iface_data) iface.control = True diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index fcd1f978..91c1fdcc 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -712,9 +712,7 @@ class CoreNode(CoreNodeBase): with self.lock: return super().next_iface_id() - def newveth( - self, iface_id: int = None, ifname: str = None, mtu: int = DEFAULT_MTU - ) -> int: + def newveth(self, iface_id: int = None, ifname: str = None, mtu: int = None) -> int: """ Create a new interface. @@ -724,6 +722,7 @@ class CoreNode(CoreNodeBase): :return: nothing """ with self.lock: + mtu = mtu if mtu is not None else DEFAULT_MTU iface_id = iface_id if iface_id is not None else self.next_iface_id() ifname = ifname if ifname is not None else f"eth{iface_id}" sessionid = self.session.short_session_id() @@ -732,32 +731,9 @@ class CoreNode(CoreNodeBase): except TypeError: suffix = f"{self.id}.{iface_id}.{sessionid}" localname = f"veth{suffix}" - if len(localname) >= 16: - raise CoreError(f"interface local name ({localname}) too long") name = f"{localname}p" - if len(name) >= 16: - raise CoreError(f"interface name ({name}) too long") - veth = Veth(self.session, self, name, localname, mtu, self.server, self.up) - if self.up: - self.net_client.device_ns(veth.name, str(self.pid)) - self.node_net_client.device_name(veth.name, ifname) - self.node_net_client.checksums_off(ifname) - veth.name = ifname - if self.up: - flow_id = self.node_net_client.get_ifindex(veth.name) - veth.flow_id = int(flow_id) - logger.debug("interface flow index: %s - %s", veth.name, veth.flow_id) - mac = self.node_net_client.get_mac(veth.name) - logger.debug("interface mac: %s - %s", veth.name, mac) - veth.set_mac(mac) - try: - # add network interface to the node. If unsuccessful, destroy the - # network interface and raise exception. - self.add_iface(veth, iface_id) - except CoreError as e: - veth.shutdown() - del veth - raise e + veth = Veth(self.session, name, localname, mtu, self.server, self) + veth.adopt_node(iface_id, ifname, self.up) return iface_id def newtuntap(self, iface_id: int = None, ifname: str = None) -> int: @@ -774,12 +750,13 @@ class CoreNode(CoreNodeBase): sessionid = self.session.short_session_id() localname = f"tap{self.id}.{iface_id}.{sessionid}" name = ifname - tuntap = TunTap(self.session, self, name, localname, start=self.up) + tuntap = TunTap(self.session, name, localname, node=self) + if self.up: + tuntap.startup() try: self.add_iface(tuntap, iface_id) except CoreError as e: tuntap.shutdown() - del tuntap raise e return iface_id diff --git a/daemon/core/nodes/interface.py b/daemon/core/nodes/interface.py index d5750d21..7fda18c7 100644 --- a/daemon/core/nodes/interface.py +++ b/daemon/core/nodes/interface.py @@ -33,25 +33,28 @@ class CoreInterface: def __init__( self, session: "Session", - node: "CoreNode", name: str, localname: str, - mtu: int, + mtu: int = DEFAULT_MTU, server: "DistributedServer" = None, + node: "CoreNode" = None, ) -> None: """ Creates a CoreInterface instance. :param session: core session instance - :param node: node for interface :param name: interface name :param localname: interface local name :param mtu: mtu value - :param server: remote server node - will run on, default is None for localhost + :param server: remote server node will run on, default is None for localhost + :param node: node for interface """ + if len(name) >= 16: + raise CoreError(f"interface name ({name}) too long, max 16") + if len(localname) >= 16: + raise CoreError(f"interface local name ({localname}) too long, max 16") self.session: "Session" = session - self.node: "CoreNode" = node + self.node: Optional["CoreNode"] = node self.name: str = name self.localname: str = localname self.up: bool = False @@ -128,7 +131,6 @@ class CoreInterface: if self.net: self.detachnet() self.net = None - net.attach(self) self.net = net @@ -279,11 +281,9 @@ class CoreInterface: logger.debug("setting param: %s - %s", key, value) if value is None or value < 0: return False - current_value = self._params.get(key) if current_value is not None and current_value == value: return False - self._params[key] = value return True @@ -342,33 +342,32 @@ class Veth(CoreInterface): Provides virtual ethernet functionality for core nodes. """ - def __init__( - self, - session: "Session", - node: "CoreNode", - name: str, - localname: str, - mtu: int = DEFAULT_MTU, - server: "DistributedServer" = None, - start: bool = True, - ) -> None: + def adopt_node(self, iface_id: int, name: str, start: bool) -> None: """ - Creates a VEth instance. + Adopt this interface to the provided node, configuring and associating + with the node as needed. - :param session: core session instance - :param node: related core node - :param name: interface name - :param localname: interface local name - :param mtu: interface mtu - :param server: remote server node - will run on, default is None for localhost - :param start: start flag - :raises CoreCommandError: when there is a command exception + :param iface_id: interface id for node + :param name: name of interface fo rnode + :param start: True to start interface, False otherwise + :return: nothing """ - # note that net arg is ignored - super().__init__(session, node, name, localname, mtu, server) if start: self.startup() + self.net_client.device_ns(self.name, str(self.node.pid)) + self.node.node_net_client.checksums_off(self.name) + self.flow_id = self.node.node_net_client.get_ifindex(self.name) + logger.debug("interface flow index: %s - %s", self.name, self.flow_id) + mac = self.node.node_net_client.get_mac(self.name) + logger.debug("interface mac: %s - %s", self.name, mac) + self.set_mac(mac) + self.node.node_net_client.device_name(self.name, name) + self.name = name + try: + self.node.add_iface(self, iface_id) + except CoreError as e: + self.shutdown() + raise e def startup(self) -> None: """ @@ -410,32 +409,6 @@ class TunTap(CoreInterface): TUN/TAP virtual device in TAP mode """ - def __init__( - self, - session: "Session", - node: "CoreNode", - name: str, - localname: str, - mtu: int = DEFAULT_MTU, - server: "DistributedServer" = None, - start: bool = True, - ) -> None: - """ - Create a TunTap instance. - - :param session: core session instance - :param node: related core node - :param name: interface name - :param localname: local interface name - :param mtu: interface mtu - :param server: remote server node - will run on, default is None for localhost - :param start: start flag - """ - super().__init__(session, node, name, localname, mtu, server) - if start: - self.startup() - def startup(self) -> None: """ Startup logic for a tunnel tap. @@ -582,47 +555,53 @@ class GreTap(CoreInterface): def __init__( self, + session: "Session", + remoteip: str, + key: int = None, node: "CoreNode" = None, - name: str = None, - session: "Session" = None, mtu: int = DEFAULT_MTU, - remoteip: str = None, _id: int = None, localip: str = None, ttl: int = 255, - key: int = None, - start: bool = True, server: "DistributedServer" = None, ) -> None: """ Creates a GreTap instance. - :param node: related core node - :param name: interface name :param session: core session instance - :param mtu: interface mtu :param remoteip: remote address + :param key: gre tap key + :param node: related core node + :param mtu: interface mtu :param _id: object id :param localip: local address :param ttl: ttl value - :param key: gre tap key - :param start: start flag :param server: remote server node will run on, default is None for localhost :raises CoreCommandError: when there is a command exception """ if _id is None: _id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF - self.id = _id + self.id: int = _id sessionid = session.short_session_id() localname = f"gt.{self.id}.{sessionid}" - super().__init__(session, node, name, localname, mtu, server) - self.transport_type = TransportType.RAW - if not start: - return - if remoteip is None: - raise CoreError("missing remote IP required for GRE TAP device") - self.net_client.create_gretap(self.localname, remoteip, localip, ttl, key) + name = f"{localname}p" + super().__init__(session, name, localname, mtu, server, node) + self.transport_type: TransportType = TransportType.RAW + self.remote_ip: str = remoteip + self.ttl: int = ttl + self.key: Optional[int] = key + self.local_ip: Optional[str] = localip + + def startup(self) -> None: + """ + Startup logic for a GreTap. + + :return: nothing + """ + self.net_client.create_gretap( + self.localname, self.remote_ip, self.local_ip, self.ttl, self.key + ) if self.mtu > 0: self.net_client.set_mtu(self.localname, self.mtu) self.net_client.device_up(self.localname) diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 1b02cc59..09cf94ec 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -95,14 +95,14 @@ class LinuxNetClient: """ return self.run(f"cat /sys/class/net/{device}/address") - def get_ifindex(self, device: str) -> str: + def get_ifindex(self, device: str) -> int: """ Retrieve ifindex for a given device. :param device: device to get ifindex for :return: ifindex """ - return self.run(f"cat /sys/class/net/{device}/ifindex") + return int(self.run(f"cat /sys/class/net/{device}/ifindex")) def device_ns(self, device: str, namespace: str) -> None: """ diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index e8f0f4c2..34f0f878 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -484,21 +484,15 @@ class CoreNetwork(CoreNetworkBase): _id = f"{self.id:x}" except TypeError: _id = str(self.id) - try: net_id = f"{net.id:x}" except TypeError: net_id = str(net.id) - localname = f"veth{_id}.{net_id}.{sessionid}" - if len(localname) >= 16: - raise ValueError(f"interface local name {localname} too long") - name = f"veth{net_id}.{_id}.{sessionid}" - if len(name) >= 16: - raise ValueError(f"interface name {name} too long") - - iface = Veth(self.session, None, name, localname, start=self.up) + iface = Veth(self.session, name, localname) + if self.up: + iface.startup() self.attach(iface) if net.up and net.brname: iface.net_client.set_iface_master(net.brname, iface.name) @@ -578,14 +572,14 @@ class GreTapBridge(CoreNetwork): self.localip: Optional[str] = localip self.ttl: int = ttl self.gretap: Optional[GreTap] = None - if remoteip is not None: + if self.remoteip is not None: self.gretap = GreTap( + session, + remoteip, + key=self.grekey, node=self, - session=session, - remoteip=remoteip, localip=localip, ttl=ttl, - key=self.grekey, mtu=self.mtu, ) @@ -597,6 +591,7 @@ class GreTapBridge(CoreNetwork): """ super().startup() if self.gretap: + self.gretap.startup() self.attach(self.gretap) def shutdown(self) -> None: @@ -628,13 +623,14 @@ class GreTapBridge(CoreNetwork): if len(ips) > 1: localip = ips[1].split("/")[0] self.gretap = GreTap( - session=self.session, - remoteip=remoteip, + self.session, + remoteip, + key=self.grekey, localip=localip, ttl=self.ttl, - key=self.grekey, mtu=self.mtu, ) + self.startup() self.attach(self.gretap) def setkey(self, key: int, iface_data: InterfaceData) -> None: diff --git a/daemon/core/nodes/physical.py b/daemon/core/nodes/physical.py index 49cce9da..dab2a954 100644 --- a/daemon/core/nodes/physical.py +++ b/daemon/core/nodes/physical.py @@ -14,7 +14,7 @@ from core.errors import CoreCommandError, CoreError from core.executables import MOUNT, TEST, UMOUNT from core.nodes.base import CoreNetworkBase, CoreNodeBase from core.nodes.interface import DEFAULT_MTU, CoreInterface -from core.nodes.network import CoreNetwork, GreTap +from core.nodes.network import CoreNetwork logger = logging.getLogger(__name__) @@ -173,25 +173,11 @@ class PhysicalNode(CoreNodeBase): name = iface_data.name if name is None: name = f"gt{iface_id}" - if self.up: - # this is reached when this node is linked to a network node - # tunnel to net not built yet, so build it now and adopt it - _, remote_tap = self.session.distributed.create_gre_tunnel( - net, self.server, iface_data.mtu - ) - self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips) - return remote_tap - else: - # this is reached when configuring services (self.up=False) - iface = GreTap( - node=self, - name=name, - session=self.session, - start=False, - mtu=iface_data.mtu, - ) - self.adopt_iface(iface, iface_id, iface_data.mac, ips) - return iface + _, remote_tap = self.session.distributed.create_gre_tunnel( + net, self.server, iface_data.mtu, self.up + ) + self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips) + return remote_tap def privatedir(self, dir_path: Path) -> None: if not str(dir_path).startswith("/"):