daemon: adjustments to support global configurable mtu values for node interfaces and bridges

This commit is contained in:
Blake Harnden 2021-12-21 08:59:48 -08:00
parent 5ff4447528
commit 0b531d7fd8
9 changed files with 72 additions and 50 deletions

View file

@ -142,6 +142,7 @@ class InterfaceData:
ip4_mask: int = None ip4_mask: int = None
ip6: str = None ip6: str = None
ip6_mask: int = None ip6_mask: int = None
mtu: int = None
def get_ips(self) -> List[str]: def get_ips(self) -> List[str]:
""" """

View file

@ -187,6 +187,7 @@ class DistributedController:
:return: nothing :return: nothing
""" """
mtu = self.session.options.get_config_int("mtu")
for node_id in self.session.nodes: for node_id in self.session.nodes:
node = self.session.nodes[node_id] node = self.session.nodes[node_id]
if not isinstance(node, CoreNetwork): if not isinstance(node, CoreNetwork):
@ -195,17 +196,17 @@ class DistributedController:
continue continue
for name in self.servers: for name in self.servers:
server = self.servers[name] server = self.servers[name]
self.create_gre_tunnel(node, server) self.create_gre_tunnel(node, server, mtu)
def create_gre_tunnel( def create_gre_tunnel(
self, node: CoreNetwork, server: DistributedServer self, node: CoreNetwork, server: DistributedServer, mtu: int
) -> Tuple[GreTap, GreTap]: ) -> Tuple[GreTap, GreTap]:
""" """
Create gre tunnel using a pair of gre taps between the local and remote server. Create gre tunnel using a pair of gre taps between the local and remote server.
:param node: node to create gre tunnel for :param node: node to create gre tunnel for
:param server: server to create :param server: server to create tunnel for
tunnel for :param mtu: mtu for gre taps
:return: local and remote gre taps created for tunnel :return: local and remote gre taps created for tunnel
""" """
host = server.host host = server.host
@ -215,14 +216,14 @@ class DistributedController:
return tunnel return tunnel
# local to server # local to server
logger.info("local tunnel node(%s) to remote(%s) key(%s)", node.name, host, key) 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) local_tap.net_client.set_iface_master(node.brname, local_tap.localname)
# server to local # server to local
logger.info( logger.info(
"remote tunnel node(%s) to local(%s) key(%s)", node.name, self.address, key "remote tunnel node(%s) to local(%s) key(%s)", node.name, self.address, key
) )
remote_tap = GreTap( 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) remote_tap.net_client.set_iface_master(node.brname, remote_tap.localname)
# save tunnels for shutdown # save tunnels for shutdown

View file

@ -46,7 +46,7 @@ from core.location.geo import GeoLocation
from core.location.mobility import BasicRangeModel, MobilityManager from core.location.mobility import BasicRangeModel, MobilityManager
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase
from core.nodes.docker import DockerNode 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.lxd import LxcNode
from core.nodes.network import ( from core.nodes.network import (
CtrlNet, CtrlNet,
@ -253,7 +253,12 @@ class Session:
node2 = self.get_node(node2_id, NodeBase) node2 = self.get_node(node2_id, NodeBase)
iface1 = None iface1 = None
iface2 = 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 # wireless link
if link_type == LinkTypes.WIRELESS: if link_type == LinkTypes.WIRELESS:
if isinstance(node1, CoreNodeBase) and isinstance(node2, CoreNodeBase): if isinstance(node1, CoreNodeBase) and isinstance(node2, CoreNodeBase):
@ -567,12 +572,18 @@ class Session:
service_class = self.service_manager.get_service(name) service_class = self.service_manager.get_service(name)
node.add_config_service(service_class) 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 # ensure default emane configuration
if isinstance(node, EmaneNet) and options.emane: if isinstance(node, EmaneNet) and options.emane:
model_class = self.emane.get_model(options.emane) model_class = self.emane.get_model(options.emane)
node.model = model_class(self, node.id) node.model = model_class(self, node.id)
if self.state == EventTypes.RUNTIME_STATE: if self.state == EventTypes.RUNTIME_STATE:
self.emane.add_node(node) self.emane.add_node(node)
# set default wlan config if needed # set default wlan config if needed
if isinstance(node, WlanNode): if isinstance(node, WlanNode):
self.mobility.set_model_config(_id, BasicRangeModel.name) self.mobility.set_model_config(_id, BasicRangeModel.name)

View file

@ -40,6 +40,7 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
id="link_interval", default="1", label="EMANE Link Check Interval (sec)" id="link_interval", default="1", label="EMANE Link Check Interval (sec)"
), ),
ConfigInt(id="link_timeout", default="4", label="EMANE Link Timeout (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 config_type: RegisterTlvs = RegisterTlvs.UTILITY

View file

@ -18,7 +18,7 @@ from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.executables import MOUNT, TEST, VNODED from core.executables import MOUNT, TEST, VNODED
from core.nodes.client import VnodeClient 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 from core.nodes.netclient import LinuxNetClient, get_net_client
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -712,47 +712,37 @@ class CoreNode(CoreNodeBase):
with self.lock: with self.lock:
return super().next_iface_id() 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. Create a new interface.
:param iface_id: id for the new interface :param iface_id: id for the new interface
:param ifname: name for the new interface :param ifname: name for the new interface
:param mtu: mtu for interface
:return: nothing :return: nothing
""" """
with self.lock: with self.lock:
if iface_id is None: iface_id = iface_id if iface_id is not None else self.next_iface_id()
iface_id = self.next_iface_id() ifname = ifname if ifname is not None else f"eth{iface_id}"
if ifname is None:
ifname = f"eth{iface_id}"
sessionid = self.session.short_session_id() sessionid = self.session.short_session_id()
try: try:
suffix = f"{self.id:x}.{iface_id}.{sessionid}" suffix = f"{self.id:x}.{iface_id}.{sessionid}"
except TypeError: except TypeError:
suffix = f"{self.id}.{iface_id}.{sessionid}" suffix = f"{self.id}.{iface_id}.{sessionid}"
localname = f"veth{suffix}" localname = f"veth{suffix}"
if len(localname) >= 16: if len(localname) >= 16:
raise ValueError(f"interface local name ({localname}) too long") raise CoreError(f"interface local name ({localname}) too long")
name = f"{localname}p"
name = localname + "p"
if len(name) >= 16: if len(name) >= 16:
raise ValueError(f"interface name ({name}) too long") raise CoreError(f"interface name ({name}) too long")
veth = Veth(self.session, self, name, localname, mtu, self.server, self.up)
veth = Veth(
self.session, self, name, localname, start=self.up, server=self.server
)
if self.up: if self.up:
self.net_client.device_ns(veth.name, str(self.pid)) self.net_client.device_ns(veth.name, str(self.pid))
self.node_net_client.device_name(veth.name, ifname) self.node_net_client.device_name(veth.name, ifname)
self.node_net_client.checksums_off(ifname) self.node_net_client.checksums_off(ifname)
veth.name = ifname veth.name = ifname
if self.up: if self.up:
flow_id = self.node_net_client.get_ifindex(veth.name) flow_id = self.node_net_client.get_ifindex(veth.name)
veth.flow_id = int(flow_id) veth.flow_id = int(flow_id)
@ -760,16 +750,14 @@ class CoreNode(CoreNodeBase):
mac = self.node_net_client.get_mac(veth.name) mac = self.node_net_client.get_mac(veth.name)
logger.debug("interface mac: %s - %s", veth.name, mac) logger.debug("interface mac: %s - %s", veth.name, mac)
veth.set_mac(mac) veth.set_mac(mac)
try: try:
# add network interface to the node. If unsuccessful, destroy the # add network interface to the node. If unsuccessful, destroy the
# network interface and raise exception. # network interface and raise exception.
self.add_iface(veth, iface_id) self.add_iface(veth, iface_id)
except ValueError as e: except CoreError as e:
veth.shutdown() veth.shutdown()
del veth del veth
raise e raise e
return iface_id return iface_id
def newtuntap(self, iface_id: int = None, ifname: str = None) -> int: def newtuntap(self, iface_id: int = None, ifname: str = None) -> int:
@ -781,24 +769,18 @@ class CoreNode(CoreNodeBase):
:return: interface index :return: interface index
""" """
with self.lock: with self.lock:
if iface_id is None: iface_id = iface_id if iface_id is not None else self.next_iface_id()
iface_id = self.next_iface_id() ifname = ifname if ifname is not None else f"eth{iface_id}"
if ifname is None:
ifname = f"eth{iface_id}"
sessionid = self.session.short_session_id() sessionid = self.session.short_session_id()
localname = f"tap{self.id}.{iface_id}.{sessionid}" localname = f"tap{self.id}.{iface_id}.{sessionid}"
name = ifname name = ifname
tuntap = TunTap(self.session, self, name, localname, start=self.up) tuntap = TunTap(self.session, self, name, localname, start=self.up)
try: try:
self.add_iface(tuntap, iface_id) self.add_iface(tuntap, iface_id)
except ValueError as e: except CoreError as e:
tuntap.shutdown() tuntap.shutdown()
del tuntap del tuntap
raise e raise e
return iface_id return iface_id
def set_mac(self, iface_id: int, mac: str) -> None: def set_mac(self, iface_id: int, mac: str) -> None:
@ -879,7 +861,7 @@ class CoreNode(CoreNodeBase):
raise CoreError( raise CoreError(
f"node({self.name}) already has interface({iface_id})" 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) self.attachnet(iface_id, net)
if iface_data.mac: if iface_data.mac:
self.set_mac(iface_id, iface_data.mac) self.set_mac(iface_id, iface_data.mac)
@ -1001,13 +983,14 @@ class CoreNetworkBase(NodeBase):
""" """
Create a CoreNetworkBase instance. Create a CoreNetworkBase instance.
:param session: CORE session object :param session: session object
:param _id: object id :param _id: object id
:param name: object name :param name: object name
:param server: remote server node :param server: remote server node
will run on, default is None for localhost will run on, default is None for localhost
""" """
super().__init__(session, _id, name, server) super().__init__(session, _id, name, server)
self.mtu: int = DEFAULT_MTU
self.brname: Optional[str] = None self.brname: Optional[str] = None
self.linked: Dict[CoreInterface, Dict[CoreInterface, bool]] = {} self.linked: Dict[CoreInterface, Dict[CoreInterface, bool]] = {}
self.linked_lock: threading.Lock = threading.Lock() self.linked_lock: threading.Lock = threading.Lock()

View file

@ -378,6 +378,9 @@ class Veth(CoreInterface):
:raises CoreCommandError: when there is a command exception :raises CoreCommandError: when there is a command exception
""" """
self.net_client.create_veth(self.localname, self.name) 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.net_client.device_up(self.localname)
self.up = True self.up = True
@ -455,12 +458,10 @@ class TunTap(CoreInterface):
""" """
if not self.up: if not self.up:
return return
try: try:
self.node.node_net_client.device_flush(self.name) self.node.node_net_client.device_flush(self.name)
except CoreCommandError: except CoreCommandError:
logger.exception("error shutting down tunnel tap") logger.exception("error shutting down tunnel tap")
self.up = False self.up = False
def waitfor( def waitfor(
@ -584,7 +585,7 @@ class GreTap(CoreInterface):
node: "CoreNode" = None, node: "CoreNode" = None,
name: str = None, name: str = None,
session: "Session" = None, session: "Session" = None,
mtu: int = 1458, mtu: int = DEFAULT_MTU,
remoteip: str = None, remoteip: str = None,
_id: int = None, _id: int = None,
localip: str = None, localip: str = None,
@ -622,6 +623,8 @@ class GreTap(CoreInterface):
if remoteip is None: if remoteip is None:
raise CoreError("missing remote IP required for GRE TAP device") raise CoreError("missing remote IP required for GRE TAP device")
self.net_client.create_gretap(self.localname, remoteip, localip, ttl, key) 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.net_client.device_up(self.localname)
self.up = True self.up = True

View file

@ -296,6 +296,16 @@ class LinuxNetClient:
""" """
self.run(f"{IP} link set {name} type bridge ageing_time {value}") 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): class OvsNetClient(LinuxNetClient):
""" """

View file

@ -290,12 +290,14 @@ class CoreNetwork(CoreNetworkBase):
def startup(self) -> None: def startup(self) -> None:
""" """
Linux bridge starup logic. Linux bridge startup logic.
:return: nothing :return: nothing
:raises CoreCommandError: when there is a command exception :raises CoreCommandError: when there is a command exception
""" """
self.net_client.create_bridge(self.brname) 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.has_nftables_chain = False
self.up = True self.up = True
nft_queue.start(self) nft_queue.start(self)
@ -584,6 +586,7 @@ class GreTapBridge(CoreNetwork):
localip=localip, localip=localip,
ttl=ttl, ttl=ttl,
key=self.grekey, key=self.grekey,
mtu=self.mtu,
) )
def startup(self) -> None: def startup(self) -> None:
@ -619,7 +622,7 @@ class GreTapBridge(CoreNetwork):
:return: nothing :return: nothing
""" """
if self.gretap: 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] remoteip = ips[0].split("/")[0]
localip = None localip = None
if len(ips) > 1: if len(ips) > 1:
@ -630,6 +633,7 @@ class GreTapBridge(CoreNetwork):
localip=localip, localip=localip,
ttl=self.ttl, ttl=self.ttl,
key=self.grekey, key=self.grekey,
mtu=self.mtu,
) )
self.attach(self.gretap) self.attach(self.gretap)

View file

@ -176,12 +176,20 @@ class PhysicalNode(CoreNodeBase):
if self.up: if self.up:
# this is reached when this node is linked to a network node # 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 # 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) self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips)
return remote_tap return remote_tap
else: else:
# this is reached when configuring services (self.up=False) # 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) self.adopt_iface(iface, iface_id, iface_data.mac, ips)
return iface return iface