diff --git a/daemon/core/emulator/data.py b/daemon/core/emulator/data.py index f56ce569..4ce92d0b 100644 --- a/daemon/core/emulator/data.py +++ b/daemon/core/emulator/data.py @@ -142,6 +142,7 @@ class InterfaceData: ip4_mask: int = None ip6: str = None ip6_mask: int = None + mtu: int = None def get_ips(self) -> List[str]: """ diff --git a/daemon/core/emulator/distributed.py b/daemon/core/emulator/distributed.py index 147f524f..bf73dce3 100644 --- a/daemon/core/emulator/distributed.py +++ b/daemon/core/emulator/distributed.py @@ -187,6 +187,7 @@ class DistributedController: :return: nothing """ + mtu = self.session.options.get_config_int("mtu") for node_id in self.session.nodes: node = self.session.nodes[node_id] if not isinstance(node, CoreNetwork): @@ -195,17 +196,17 @@ class DistributedController: continue for name in self.servers: server = self.servers[name] - self.create_gre_tunnel(node, server) + self.create_gre_tunnel(node, server, mtu) def create_gre_tunnel( - self, node: CoreNetwork, server: DistributedServer + self, node: CoreNetwork, server: DistributedServer, mtu: int ) -> Tuple[GreTap, GreTap]: """ Create gre tunnel using a pair of gre taps between the local and remote server. :param node: node to create gre tunnel for - :param server: server to create - tunnel for + :param server: server to create tunnel for + :param mtu: mtu for gre taps :return: local and remote gre taps created for tunnel """ host = server.host @@ -215,14 +216,14 @@ 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) + local_tap = GreTap(session=self.session, remoteip=host, key=key, mtu=mtu) 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 + session=self.session, remoteip=self.address, key=key, server=server, mtu=mtu ) remote_tap.net_client.set_iface_master(node.brname, remote_tap.localname) # save tunnels for shutdown diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 6dad8e2d..c2affad3 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -46,7 +46,7 @@ from core.location.geo import GeoLocation from core.location.mobility import BasicRangeModel, MobilityManager from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase from core.nodes.docker import DockerNode -from core.nodes.interface import CoreInterface +from core.nodes.interface import DEFAULT_MTU, CoreInterface from core.nodes.lxd import LxcNode from core.nodes.network import ( CtrlNet, @@ -253,7 +253,12 @@ class Session: node2 = self.get_node(node2_id, NodeBase) iface1 = None iface2 = None - + # set mtu + mtu = self.options.get_config_int("mtu") or DEFAULT_MTU + if iface1_data: + iface1_data.mtu = mtu + if iface2_data: + iface2_data.mtu = mtu # wireless link if link_type == LinkTypes.WIRELESS: if isinstance(node1, CoreNodeBase) and isinstance(node2, CoreNodeBase): @@ -567,12 +572,18 @@ class Session: service_class = self.service_manager.get_service(name) node.add_config_service(service_class) + # set network mtu, if configured + mtu = self.options.get_config_int("mtu") + if isinstance(node, CoreNetworkBase) and mtu > 0: + node.mtu = mtu + # ensure default emane configuration if isinstance(node, EmaneNet) and options.emane: model_class = self.emane.get_model(options.emane) node.model = model_class(self, node.id) if self.state == EventTypes.RUNTIME_STATE: self.emane.add_node(node) + # set default wlan config if needed if isinstance(node, WlanNode): self.mobility.set_model_config(_id, BasicRangeModel.name) diff --git a/daemon/core/emulator/sessionconfig.py b/daemon/core/emulator/sessionconfig.py index 893f71c4..028d4e66 100644 --- a/daemon/core/emulator/sessionconfig.py +++ b/daemon/core/emulator/sessionconfig.py @@ -40,6 +40,7 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions): id="link_interval", default="1", label="EMANE Link Check Interval (sec)" ), ConfigInt(id="link_timeout", default="4", label="EMANE Link Timeout (sec)"), + ConfigInt(id="mtu", default="0", label="MTU for All Devices"), ] config_type: RegisterTlvs = RegisterTlvs.UTILITY diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index 2ec7fb7f..fcd1f978 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -18,7 +18,7 @@ from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes from core.errors import CoreCommandError, CoreError from core.executables import MOUNT, TEST, VNODED from core.nodes.client import VnodeClient -from core.nodes.interface import CoreInterface, TunTap, Veth +from core.nodes.interface import DEFAULT_MTU, CoreInterface, TunTap, Veth from core.nodes.netclient import LinuxNetClient, get_net_client logger = logging.getLogger(__name__) @@ -712,47 +712,37 @@ class CoreNode(CoreNodeBase): with self.lock: return super().next_iface_id() - def newveth(self, iface_id: int = None, ifname: str = None) -> int: + def newveth( + self, iface_id: int = None, ifname: str = None, mtu: int = DEFAULT_MTU + ) -> int: """ Create a new interface. :param iface_id: id for the new interface :param ifname: name for the new interface + :param mtu: mtu for interface :return: nothing """ with self.lock: - if iface_id is None: - iface_id = self.next_iface_id() - - if ifname is None: - ifname = f"eth{iface_id}" - + 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() - try: suffix = f"{self.id:x}.{iface_id}.{sessionid}" except TypeError: suffix = f"{self.id}.{iface_id}.{sessionid}" - localname = f"veth{suffix}" if len(localname) >= 16: - raise ValueError(f"interface local name ({localname}) too long") - - name = localname + "p" + raise CoreError(f"interface local name ({localname}) too long") + name = f"{localname}p" if len(name) >= 16: - raise ValueError(f"interface name ({name}) too long") - - veth = Veth( - self.session, self, name, localname, start=self.up, server=self.server - ) - + 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) @@ -760,16 +750,14 @@ class CoreNode(CoreNodeBase): 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 ValueError as e: + except CoreError as e: veth.shutdown() del veth raise e - return iface_id def newtuntap(self, iface_id: int = None, ifname: str = None) -> int: @@ -781,24 +769,18 @@ class CoreNode(CoreNodeBase): :return: interface index """ with self.lock: - if iface_id is None: - iface_id = self.next_iface_id() - - if ifname is None: - ifname = f"eth{iface_id}" - + 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() localname = f"tap{self.id}.{iface_id}.{sessionid}" name = ifname tuntap = TunTap(self.session, self, name, localname, start=self.up) - try: self.add_iface(tuntap, iface_id) - except ValueError as e: + except CoreError as e: tuntap.shutdown() del tuntap raise e - return iface_id def set_mac(self, iface_id: int, mac: str) -> None: @@ -879,7 +861,7 @@ class CoreNode(CoreNodeBase): raise CoreError( f"node({self.name}) already has interface({iface_id})" ) - iface_id = self.newveth(iface_id, iface_data.name) + iface_id = self.newveth(iface_id, iface_data.name, iface_data.mtu) self.attachnet(iface_id, net) if iface_data.mac: self.set_mac(iface_id, iface_data.mac) @@ -1001,13 +983,14 @@ class CoreNetworkBase(NodeBase): """ Create a CoreNetworkBase instance. - :param session: CORE session object + :param session: session object :param _id: object id :param name: object name :param server: remote server node will run on, default is None for localhost """ super().__init__(session, _id, name, server) + self.mtu: int = DEFAULT_MTU self.brname: Optional[str] = None self.linked: Dict[CoreInterface, Dict[CoreInterface, bool]] = {} self.linked_lock: threading.Lock = threading.Lock() diff --git a/daemon/core/nodes/interface.py b/daemon/core/nodes/interface.py index 2ad1d9be..d5750d21 100644 --- a/daemon/core/nodes/interface.py +++ b/daemon/core/nodes/interface.py @@ -378,6 +378,9 @@ class Veth(CoreInterface): :raises CoreCommandError: when there is a command exception """ self.net_client.create_veth(self.localname, self.name) + if self.mtu > 0: + self.net_client.set_mtu(self.name, self.mtu) + self.net_client.set_mtu(self.localname, self.mtu) self.net_client.device_up(self.localname) self.up = True @@ -455,12 +458,10 @@ class TunTap(CoreInterface): """ if not self.up: return - try: self.node.node_net_client.device_flush(self.name) except CoreCommandError: logger.exception("error shutting down tunnel tap") - self.up = False def waitfor( @@ -584,7 +585,7 @@ class GreTap(CoreInterface): node: "CoreNode" = None, name: str = None, session: "Session" = None, - mtu: int = 1458, + mtu: int = DEFAULT_MTU, remoteip: str = None, _id: int = None, localip: str = None, @@ -622,6 +623,8 @@ class GreTap(CoreInterface): 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) + if self.mtu > 0: + self.net_client.set_mtu(self.localname, self.mtu) self.net_client.device_up(self.localname) self.up = True diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 2f87f5dd..1b02cc59 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -296,6 +296,16 @@ class LinuxNetClient: """ self.run(f"{IP} link set {name} type bridge ageing_time {value}") + def set_mtu(self, name: str, value: int) -> None: + """ + Sets the mtu value for a device. + + :param name: name of device to set value for + :param value: mtu value to set + :return: nothing + """ + self.run(f"{IP} link set {name} mtu {value}") + class OvsNetClient(LinuxNetClient): """ diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index f9511924..e8f0f4c2 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -290,12 +290,14 @@ class CoreNetwork(CoreNetworkBase): def startup(self) -> None: """ - Linux bridge starup logic. + Linux bridge startup logic. :return: nothing :raises CoreCommandError: when there is a command exception """ self.net_client.create_bridge(self.brname) + if self.mtu > 0: + self.net_client.set_mtu(self.brname, self.mtu) self.has_nftables_chain = False self.up = True nft_queue.start(self) @@ -584,6 +586,7 @@ class GreTapBridge(CoreNetwork): localip=localip, ttl=ttl, key=self.grekey, + mtu=self.mtu, ) def startup(self) -> None: @@ -619,7 +622,7 @@ class GreTapBridge(CoreNetwork): :return: nothing """ if self.gretap: - raise ValueError(f"gretap already exists for {self.name}") + raise CoreError(f"gretap already exists for {self.name}") remoteip = ips[0].split("/")[0] localip = None if len(ips) > 1: @@ -630,6 +633,7 @@ class GreTapBridge(CoreNetwork): localip=localip, ttl=self.ttl, key=self.grekey, + mtu=self.mtu, ) self.attach(self.gretap) diff --git a/daemon/core/nodes/physical.py b/daemon/core/nodes/physical.py index 18fa9d06..49cce9da 100644 --- a/daemon/core/nodes/physical.py +++ b/daemon/core/nodes/physical.py @@ -176,12 +176,20 @@ class PhysicalNode(CoreNodeBase): 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) + _, 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) + 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