daemon: updated linkconfig to calculate a limit when bw/delay are present, updated and simplified logic as well, leveraging code from outstanding pull request, updated code to factor in the mtu of the iface being configured
This commit is contained in:
parent
83ba6cea70
commit
380d411833
9 changed files with 65 additions and 78 deletions
|
@ -788,14 +788,18 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
||||||
options = LinkOptions()
|
options = LinkOptions()
|
||||||
options.delay = message.get_tlv(LinkTlvs.DELAY.value)
|
options.delay = message.get_tlv(LinkTlvs.DELAY.value)
|
||||||
options.bandwidth = message.get_tlv(LinkTlvs.BANDWIDTH.value)
|
options.bandwidth = message.get_tlv(LinkTlvs.BANDWIDTH.value)
|
||||||
options.loss = message.get_tlv(LinkTlvs.LOSS.value)
|
|
||||||
options.dup = message.get_tlv(LinkTlvs.DUP.value)
|
|
||||||
options.jitter = message.get_tlv(LinkTlvs.JITTER.value)
|
options.jitter = message.get_tlv(LinkTlvs.JITTER.value)
|
||||||
options.mer = message.get_tlv(LinkTlvs.MER.value)
|
options.mer = message.get_tlv(LinkTlvs.MER.value)
|
||||||
options.burst = message.get_tlv(LinkTlvs.BURST.value)
|
options.burst = message.get_tlv(LinkTlvs.BURST.value)
|
||||||
options.mburst = message.get_tlv(LinkTlvs.MBURST.value)
|
options.mburst = message.get_tlv(LinkTlvs.MBURST.value)
|
||||||
options.unidirectional = message.get_tlv(LinkTlvs.UNIDIRECTIONAL.value)
|
options.unidirectional = message.get_tlv(LinkTlvs.UNIDIRECTIONAL.value)
|
||||||
options.key = message.get_tlv(LinkTlvs.KEY.value)
|
options.key = message.get_tlv(LinkTlvs.KEY.value)
|
||||||
|
loss = message.get_tlv(LinkTlvs.LOSS.value)
|
||||||
|
dup = message.get_tlv(LinkTlvs.DUP.value)
|
||||||
|
if loss is not None:
|
||||||
|
options.loss = float(loss)
|
||||||
|
if dup is not None:
|
||||||
|
options.dup = int(dup)
|
||||||
|
|
||||||
if message.flags & MessageFlags.ADD.value:
|
if message.flags & MessageFlags.ADD.value:
|
||||||
self.session.add_link(
|
self.session.add_link(
|
||||||
|
|
|
@ -5,7 +5,7 @@ from core.config import Configuration
|
||||||
from core.configservice.base import ConfigService, ConfigServiceMode
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import WlanNode
|
from core.nodes.network import WlanNode
|
||||||
|
|
||||||
GROUP: str = "FRR"
|
GROUP: str = "FRR"
|
||||||
|
@ -18,7 +18,7 @@ def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
||||||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
"""
|
"""
|
||||||
if iface.mtu != 1500:
|
if iface.mtu != DEFAULT_MTU:
|
||||||
return True
|
return True
|
||||||
if not iface.net:
|
if not iface.net:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -6,7 +6,7 @@ from core.config import Configuration
|
||||||
from core.configservice.base import ConfigService, ConfigServiceMode
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import WlanNode
|
from core.nodes.network import WlanNode
|
||||||
|
|
||||||
GROUP: str = "Quagga"
|
GROUP: str = "Quagga"
|
||||||
|
@ -19,7 +19,7 @@ def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
||||||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
"""
|
"""
|
||||||
if iface.mtu != 1500:
|
if iface.mtu != DEFAULT_MTU:
|
||||||
return True
|
return True
|
||||||
if not iface.net:
|
if not iface.net:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -30,8 +30,6 @@ if TYPE_CHECKING:
|
||||||
CoreServices = List[Union[CoreService, Type[CoreService]]]
|
CoreServices = List[Union[CoreService, Type[CoreService]]]
|
||||||
ConfigServiceType = Type[ConfigService]
|
ConfigServiceType = Type[ConfigService]
|
||||||
|
|
||||||
_DEFAULT_MTU = 1500
|
|
||||||
|
|
||||||
|
|
||||||
class NodeBase(abc.ABC):
|
class NodeBase(abc.ABC):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,6 +19,8 @@ if TYPE_CHECKING:
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
from core.nodes.base import CoreNetworkBase, CoreNode
|
from core.nodes.base import CoreNetworkBase, CoreNode
|
||||||
|
|
||||||
|
DEFAULT_MTU: int = 1500
|
||||||
|
|
||||||
|
|
||||||
class CoreInterface:
|
class CoreInterface:
|
||||||
"""
|
"""
|
||||||
|
@ -338,7 +340,7 @@ class Veth(CoreInterface):
|
||||||
node: "CoreNode",
|
node: "CoreNode",
|
||||||
name: str,
|
name: str,
|
||||||
localname: str,
|
localname: str,
|
||||||
mtu: int = 1500,
|
mtu: int = DEFAULT_MTU,
|
||||||
server: "DistributedServer" = None,
|
server: "DistributedServer" = None,
|
||||||
start: bool = True,
|
start: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -403,7 +405,7 @@ class TunTap(CoreInterface):
|
||||||
node: "CoreNode",
|
node: "CoreNode",
|
||||||
name: str,
|
name: str,
|
||||||
localname: str,
|
localname: str,
|
||||||
mtu: int = 1500,
|
mtu: int = DEFAULT_MTU,
|
||||||
server: "DistributedServer" = None,
|
server: "DistributedServer" = None,
|
||||||
start: bool = True,
|
start: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -3,6 +3,7 @@ Defines network nodes used within core.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
|
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
|
||||||
|
@ -447,77 +448,59 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:param iface2: interface two
|
:param iface2: interface two
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
devname = iface.localname
|
# determine if any settings have changed
|
||||||
tc = f"{TC} qdisc replace dev {devname}"
|
changed = any(
|
||||||
parent = "root"
|
[
|
||||||
changed = False
|
iface.setparam("bw", options.bandwidth),
|
||||||
bw = options.bandwidth
|
iface.setparam("delay", options.delay),
|
||||||
if iface.setparam("bw", bw):
|
iface.setparam("loss", options.loss),
|
||||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
iface.setparam("duplicate", options.dup),
|
||||||
burst = max(2 * iface.mtu, int(bw / 1000))
|
iface.setparam("jitter", options.jitter),
|
||||||
# max IP payload
|
]
|
||||||
limit = 0xFFFF
|
)
|
||||||
tbf = f"tbf rate {bw} burst {burst} limit {limit}"
|
|
||||||
if bw > 0:
|
|
||||||
if self.up:
|
|
||||||
cmd = f"{tc} {parent} handle 1: {tbf}"
|
|
||||||
iface.host_cmd(cmd)
|
|
||||||
iface.setparam("has_tbf", True)
|
|
||||||
changed = True
|
|
||||||
elif iface.getparam("has_tbf") and bw <= 0:
|
|
||||||
if self.up:
|
|
||||||
cmd = f"{TC} qdisc delete dev {devname} {parent}"
|
|
||||||
iface.host_cmd(cmd)
|
|
||||||
iface.setparam("has_tbf", False)
|
|
||||||
# removing the parent removes the child
|
|
||||||
iface.setparam("has_netem", False)
|
|
||||||
changed = True
|
|
||||||
if iface.getparam("has_tbf"):
|
|
||||||
parent = "parent 1:1"
|
|
||||||
netem = "netem"
|
|
||||||
delay = options.delay
|
|
||||||
changed = max(changed, iface.setparam("delay", delay))
|
|
||||||
loss = options.loss
|
|
||||||
if loss is not None:
|
|
||||||
loss = float(loss)
|
|
||||||
changed = max(changed, iface.setparam("loss", loss))
|
|
||||||
duplicate = options.dup
|
|
||||||
if duplicate is not None:
|
|
||||||
duplicate = int(duplicate)
|
|
||||||
changed = max(changed, iface.setparam("duplicate", duplicate))
|
|
||||||
jitter = options.jitter
|
|
||||||
changed = max(changed, iface.setparam("jitter", jitter))
|
|
||||||
if not changed:
|
if not changed:
|
||||||
return
|
return
|
||||||
# jitter and delay use the same delay statement
|
|
||||||
if delay is not None:
|
|
||||||
netem += f" delay {delay}us"
|
|
||||||
if jitter is not None:
|
|
||||||
if delay is None:
|
|
||||||
netem += f" delay 0us {jitter}us 25%"
|
|
||||||
else:
|
|
||||||
netem += f" {jitter}us 25%"
|
|
||||||
|
|
||||||
if loss is not None and loss > 0:
|
# delete tc configuration or create and add it
|
||||||
netem += f" loss {min(loss, 100)}%"
|
devname = iface.localname
|
||||||
if duplicate is not None and duplicate > 0:
|
if all(
|
||||||
netem += f" duplicate {min(duplicate, 100)}%"
|
[
|
||||||
|
options.delay is None or options.delay <= 0,
|
||||||
delay_check = delay is None or delay <= 0
|
options.jitter is None or options.jitter <= 0,
|
||||||
jitter_check = jitter is None or jitter <= 0
|
options.loss is None or options.loss <= 0,
|
||||||
loss_check = loss is None or loss <= 0
|
options.dup is None or options.dup <= 0,
|
||||||
duplicate_check = duplicate is None or duplicate <= 0
|
options.bandwidth is None or options.bandwidth <= 0,
|
||||||
if all([delay_check, jitter_check, loss_check, duplicate_check]):
|
]
|
||||||
# possibly remove netem if it exists and parent queue wasn't removed
|
):
|
||||||
if not iface.getparam("has_netem"):
|
if not iface.getparam("has_netem"):
|
||||||
return
|
return
|
||||||
if self.up:
|
if self.up:
|
||||||
cmd = f"{TC} qdisc delete dev {devname} {parent} handle 10:"
|
cmd = f"{TC} qdisc delete dev {devname} root handle 10:"
|
||||||
iface.host_cmd(cmd)
|
iface.host_cmd(cmd)
|
||||||
iface.setparam("has_netem", False)
|
iface.setparam("has_netem", False)
|
||||||
elif len(netem) > 1:
|
else:
|
||||||
|
netem = ""
|
||||||
|
if options.bandwidth is not None:
|
||||||
|
limit = 1000
|
||||||
|
bw = options.bandwidth / 1000
|
||||||
|
if options.delay and options.bandwidth:
|
||||||
|
delay = options.delay / 1000
|
||||||
|
limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu)))
|
||||||
|
netem += f" rate {bw}kbit"
|
||||||
|
netem += f" limit {limit}"
|
||||||
|
if options.delay is not None:
|
||||||
|
netem += f" delay {options.delay}us"
|
||||||
|
if options.jitter is not None:
|
||||||
|
if options.delay is None:
|
||||||
|
netem += f" delay 0us {options.jitter}us 25%"
|
||||||
|
else:
|
||||||
|
netem += f" {options.jitter}us 25%"
|
||||||
|
if options.loss is not None and options.loss > 0:
|
||||||
|
netem += f" loss {min(options.loss, 100)}%"
|
||||||
|
if options.dup is not None and options.dup > 0:
|
||||||
|
netem += f" duplicate {min(options.dup, 100)}%"
|
||||||
if self.up:
|
if self.up:
|
||||||
cmd = f"{TC} qdisc replace dev {devname} {parent} handle 10: {netem}"
|
cmd = f"{TC} qdisc replace dev {devname} root handle 10: netem {netem}"
|
||||||
iface.host_cmd(cmd)
|
iface.host_cmd(cmd)
|
||||||
iface.setparam("has_netem", True)
|
iface.setparam("has_netem", True)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from core.emulator.enumerations import NodeTypes, TransportType
|
||||||
from core.errors import CoreCommandError, CoreError
|
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 CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import CoreNetwork, GreTap
|
from core.nodes.network import CoreNetwork, GreTap
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -252,7 +252,7 @@ class Rj45Node(CoreNodeBase):
|
||||||
session: "Session",
|
session: "Session",
|
||||||
_id: int = None,
|
_id: int = None,
|
||||||
name: str = None,
|
name: str = None,
|
||||||
mtu: int = 1500,
|
mtu: int = DEFAULT_MTU,
|
||||||
server: DistributedServer = None,
|
server: DistributedServer = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,7 @@ import netaddr
|
||||||
|
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.nodes.physical import Rj45Node
|
from core.nodes.physical import Rj45Node
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
@ -384,7 +384,7 @@ class FRROspfv2(FrrService):
|
||||||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
"""
|
"""
|
||||||
if iface.mtu != 1500:
|
if iface.mtu != DEFAULT_MTU:
|
||||||
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
||||||
# the other nodes/nets
|
# the other nodes/nets
|
||||||
return " ip ospf mtu-ignore\n"
|
return " ip ospf mtu-ignore\n"
|
||||||
|
|
|
@ -8,7 +8,7 @@ import netaddr
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.enumerations import LinkTypes
|
from core.emulator.enumerations import LinkTypes
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.nodes.physical import Rj45Node
|
from core.nodes.physical import Rj45Node
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
@ -301,7 +301,7 @@ class Ospfv2(QuaggaService):
|
||||||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
"""
|
"""
|
||||||
if iface.mtu != 1500:
|
if iface.mtu != DEFAULT_MTU:
|
||||||
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
||||||
# the other nodes/nets
|
# the other nodes/nets
|
||||||
return " ip ospf mtu-ignore\n"
|
return " ip ospf mtu-ignore\n"
|
||||||
|
|
Loading…
Add table
Reference in a new issue