daemon: updates to support configuring and tracking mtu, along with over refactoring

This commit is contained in:
Blake Harnden 2022-01-07 16:03:45 -08:00
parent 0b531d7fd8
commit 871b1ae2af
7 changed files with 93 additions and 151 deletions

View file

@ -196,10 +196,10 @@ 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, mtu) self.create_gre_tunnel(node, server, mtu, True)
def create_gre_tunnel( def create_gre_tunnel(
self, node: CoreNetwork, server: DistributedServer, mtu: int self, node: CoreNetwork, server: DistributedServer, mtu: int, start: bool
) -> 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.
@ -207,6 +207,7 @@ class DistributedController:
:param node: node to create gre tunnel for :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 :param mtu: mtu for gre taps
:param start: True to start gre taps, False otherwise
:return: local and remote gre taps created for tunnel :return: local and remote gre taps created for tunnel
""" """
host = server.host host = server.host
@ -216,16 +217,18 @@ 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, mtu=mtu) local_tap = GreTap(self.session, host, key=key, mtu=mtu)
local_tap.net_client.set_iface_master(node.brname, local_tap.localname) if start:
local_tap.startup()
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(self.session, self.address, key=key, server=server, mtu=mtu)
session=self.session, remoteip=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) remote_tap.net_client.set_iface_master(node.brname, remote_tap.localname)
# save tunnels for shutdown # save tunnels for shutdown
tunnel = (local_tap, remote_tap) tunnel = (local_tap, remote_tap)
self.tunnels[key] = tunnel self.tunnels[key] = tunnel

View file

@ -1496,6 +1496,7 @@ class Session:
mac=utils.random_mac(), mac=utils.random_mac(),
ip4=ip4, ip4=ip4,
ip4_mask=ip4_mask, ip4_mask=ip4_mask,
mtu=DEFAULT_MTU,
) )
iface = node.new_iface(control_net, iface_data) iface = node.new_iface(control_net, iface_data)
iface.control = True iface.control = True

View file

@ -712,9 +712,7 @@ class CoreNode(CoreNodeBase):
with self.lock: with self.lock:
return super().next_iface_id() return super().next_iface_id()
def newveth( def newveth(self, iface_id: int = None, ifname: str = None, mtu: int = None) -> int:
self, iface_id: int = None, ifname: str = None, mtu: int = DEFAULT_MTU
) -> int:
""" """
Create a new interface. Create a new interface.
@ -724,6 +722,7 @@ class CoreNode(CoreNodeBase):
:return: nothing :return: nothing
""" """
with self.lock: 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() 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}" ifname = ifname if ifname is not None else f"eth{iface_id}"
sessionid = self.session.short_session_id() sessionid = self.session.short_session_id()
@ -732,32 +731,9 @@ class CoreNode(CoreNodeBase):
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:
raise CoreError(f"interface local name ({localname}) too long")
name = f"{localname}p" name = f"{localname}p"
if len(name) >= 16: veth = Veth(self.session, name, localname, mtu, self.server, self)
raise CoreError(f"interface name ({name}) too long") veth.adopt_node(iface_id, ifname, self.up)
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
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:
@ -774,12 +750,13 @@ class CoreNode(CoreNodeBase):
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, name, localname, node=self)
if self.up:
tuntap.startup()
try: try:
self.add_iface(tuntap, iface_id) self.add_iface(tuntap, iface_id)
except CoreError as e: except CoreError as e:
tuntap.shutdown() tuntap.shutdown()
del tuntap
raise e raise e
return iface_id return iface_id

View file

@ -33,25 +33,28 @@ class CoreInterface:
def __init__( def __init__(
self, self,
session: "Session", session: "Session",
node: "CoreNode",
name: str, name: str,
localname: str, localname: str,
mtu: int, mtu: int = DEFAULT_MTU,
server: "DistributedServer" = None, server: "DistributedServer" = None,
node: "CoreNode" = None,
) -> None: ) -> None:
""" """
Creates a CoreInterface instance. Creates a CoreInterface instance.
:param session: core session instance :param session: core session instance
:param node: node for interface
:param name: interface name :param name: interface name
:param localname: interface local name :param localname: interface local name
:param mtu: mtu value :param mtu: mtu value
: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 :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.session: "Session" = session
self.node: "CoreNode" = node self.node: Optional["CoreNode"] = node
self.name: str = name self.name: str = name
self.localname: str = localname self.localname: str = localname
self.up: bool = False self.up: bool = False
@ -128,7 +131,6 @@ class CoreInterface:
if self.net: if self.net:
self.detachnet() self.detachnet()
self.net = None self.net = None
net.attach(self) net.attach(self)
self.net = net self.net = net
@ -279,11 +281,9 @@ class CoreInterface:
logger.debug("setting param: %s - %s", key, value) logger.debug("setting param: %s - %s", key, value)
if value is None or value < 0: if value is None or value < 0:
return False return False
current_value = self._params.get(key) current_value = self._params.get(key)
if current_value is not None and current_value == value: if current_value is not None and current_value == value:
return False return False
self._params[key] = value self._params[key] = value
return True return True
@ -342,33 +342,32 @@ class Veth(CoreInterface):
Provides virtual ethernet functionality for core nodes. Provides virtual ethernet functionality for core nodes.
""" """
def __init__( def adopt_node(self, iface_id: int, name: str, start: bool) -> None:
self,
session: "Session",
node: "CoreNode",
name: str,
localname: str,
mtu: int = DEFAULT_MTU,
server: "DistributedServer" = None,
start: bool = True,
) -> 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 iface_id: interface id for node
:param node: related core node :param name: name of interface fo rnode
:param name: interface name :param start: True to start interface, False otherwise
:param localname: interface local name :return: nothing
: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
""" """
# note that net arg is ignored
super().__init__(session, node, name, localname, mtu, server)
if start: if start:
self.startup() 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: def startup(self) -> None:
""" """
@ -410,32 +409,6 @@ class TunTap(CoreInterface):
TUN/TAP virtual device in TAP mode 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: def startup(self) -> None:
""" """
Startup logic for a tunnel tap. Startup logic for a tunnel tap.
@ -582,47 +555,53 @@ class GreTap(CoreInterface):
def __init__( def __init__(
self, self,
session: "Session",
remoteip: str,
key: int = None,
node: "CoreNode" = None, node: "CoreNode" = None,
name: str = None,
session: "Session" = None,
mtu: int = DEFAULT_MTU, mtu: int = DEFAULT_MTU,
remoteip: str = None,
_id: int = None, _id: int = None,
localip: str = None, localip: str = None,
ttl: int = 255, ttl: int = 255,
key: int = None,
start: bool = True,
server: "DistributedServer" = None, server: "DistributedServer" = None,
) -> None: ) -> None:
""" """
Creates a GreTap instance. Creates a GreTap instance.
:param node: related core node
:param name: interface name
:param session: core session instance :param session: core session instance
:param mtu: interface mtu
:param remoteip: remote address :param remoteip: remote address
:param key: gre tap key
:param node: related core node
:param mtu: interface mtu
:param _id: object id :param _id: object id
:param localip: local address :param localip: local address
:param ttl: ttl value :param ttl: ttl value
:param key: gre tap key
:param start: start flag
: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
:raises CoreCommandError: when there is a command exception :raises CoreCommandError: when there is a command exception
""" """
if _id is None: if _id is None:
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF _id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
self.id = _id self.id: int = _id
sessionid = session.short_session_id() sessionid = session.short_session_id()
localname = f"gt.{self.id}.{sessionid}" localname = f"gt.{self.id}.{sessionid}"
super().__init__(session, node, name, localname, mtu, server) name = f"{localname}p"
self.transport_type = TransportType.RAW super().__init__(session, name, localname, mtu, server, node)
if not start: self.transport_type: TransportType = TransportType.RAW
return self.remote_ip: str = remoteip
if remoteip is None: self.ttl: int = ttl
raise CoreError("missing remote IP required for GRE TAP device") self.key: Optional[int] = key
self.net_client.create_gretap(self.localname, remoteip, localip, ttl, 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: if self.mtu > 0:
self.net_client.set_mtu(self.localname, 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)

View file

@ -95,14 +95,14 @@ class LinuxNetClient:
""" """
return self.run(f"cat /sys/class/net/{device}/address") 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. Retrieve ifindex for a given device.
:param device: device to get ifindex for :param device: device to get ifindex for
:return: ifindex :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: def device_ns(self, device: str, namespace: str) -> None:
""" """

View file

@ -484,21 +484,15 @@ class CoreNetwork(CoreNetworkBase):
_id = f"{self.id:x}" _id = f"{self.id:x}"
except TypeError: except TypeError:
_id = str(self.id) _id = str(self.id)
try: try:
net_id = f"{net.id:x}" net_id = f"{net.id:x}"
except TypeError: except TypeError:
net_id = str(net.id) net_id = str(net.id)
localname = f"veth{_id}.{net_id}.{sessionid}" 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}" name = f"veth{net_id}.{_id}.{sessionid}"
if len(name) >= 16: iface = Veth(self.session, name, localname)
raise ValueError(f"interface name {name} too long") if self.up:
iface.startup()
iface = Veth(self.session, None, name, localname, start=self.up)
self.attach(iface) self.attach(iface)
if net.up and net.brname: if net.up and net.brname:
iface.net_client.set_iface_master(net.brname, iface.name) iface.net_client.set_iface_master(net.brname, iface.name)
@ -578,14 +572,14 @@ class GreTapBridge(CoreNetwork):
self.localip: Optional[str] = localip self.localip: Optional[str] = localip
self.ttl: int = ttl self.ttl: int = ttl
self.gretap: Optional[GreTap] = None self.gretap: Optional[GreTap] = None
if remoteip is not None: if self.remoteip is not None:
self.gretap = GreTap( self.gretap = GreTap(
session,
remoteip,
key=self.grekey,
node=self, node=self,
session=session,
remoteip=remoteip,
localip=localip, localip=localip,
ttl=ttl, ttl=ttl,
key=self.grekey,
mtu=self.mtu, mtu=self.mtu,
) )
@ -597,6 +591,7 @@ class GreTapBridge(CoreNetwork):
""" """
super().startup() super().startup()
if self.gretap: if self.gretap:
self.gretap.startup()
self.attach(self.gretap) self.attach(self.gretap)
def shutdown(self) -> None: def shutdown(self) -> None:
@ -628,13 +623,14 @@ class GreTapBridge(CoreNetwork):
if len(ips) > 1: if len(ips) > 1:
localip = ips[1].split("/")[0] localip = ips[1].split("/")[0]
self.gretap = GreTap( self.gretap = GreTap(
session=self.session, self.session,
remoteip=remoteip, remoteip,
key=self.grekey,
localip=localip, localip=localip,
ttl=self.ttl, ttl=self.ttl,
key=self.grekey,
mtu=self.mtu, mtu=self.mtu,
) )
self.startup()
self.attach(self.gretap) self.attach(self.gretap)
def setkey(self, key: int, iface_data: InterfaceData) -> None: def setkey(self, key: int, iface_data: InterfaceData) -> None:

View file

@ -14,7 +14,7 @@ from core.errors import CoreCommandError, CoreError
from core.executables import MOUNT, TEST, UMOUNT from core.executables import MOUNT, TEST, UMOUNT
from core.nodes.base import CoreNetworkBase, CoreNodeBase from core.nodes.base import CoreNetworkBase, CoreNodeBase
from core.nodes.interface import DEFAULT_MTU, CoreInterface 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__) logger = logging.getLogger(__name__)
@ -173,25 +173,11 @@ class PhysicalNode(CoreNodeBase):
name = iface_data.name name = iface_data.name
if name is None: if name is None:
name = f"gt{iface_id}" name = f"gt{iface_id}"
if self.up: _, remote_tap = self.session.distributed.create_gre_tunnel(
# this is reached when this node is linked to a network node net, self.server, iface_data.mtu, self.up
# tunnel to net not built yet, so build it now and adopt it )
_, remote_tap = self.session.distributed.create_gre_tunnel( self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips)
net, self.server, iface_data.mtu return remote_tap
)
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
def privatedir(self, dir_path: Path) -> None: def privatedir(self, dir_path: Path) -> None:
if not str(dir_path).startswith("/"): if not str(dir_path).startswith("/"):