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:
Blake Harnden 2020-10-17 08:18:49 -07:00
parent 83ba6cea70
commit 380d411833
9 changed files with 65 additions and 78 deletions

View file

@ -788,14 +788,18 @@ class CoreHandler(socketserver.BaseRequestHandler):
options = LinkOptions()
options.delay = message.get_tlv(LinkTlvs.DELAY.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.mer = message.get_tlv(LinkTlvs.MER.value)
options.burst = message.get_tlv(LinkTlvs.BURST.value)
options.mburst = message.get_tlv(LinkTlvs.MBURST.value)
options.unidirectional = message.get_tlv(LinkTlvs.UNIDIRECTIONAL.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:
self.session.add_link(

View file

@ -5,7 +5,7 @@ from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode
from core.emane.nodes import EmaneNet
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
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
GreTap device.
"""
if iface.mtu != 1500:
if iface.mtu != DEFAULT_MTU:
return True
if not iface.net:
return False

View file

@ -6,7 +6,7 @@ from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode
from core.emane.nodes import EmaneNet
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
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
GreTap device.
"""
if iface.mtu != 1500:
if iface.mtu != DEFAULT_MTU:
return True
if not iface.net:
return False

View file

@ -30,8 +30,6 @@ if TYPE_CHECKING:
CoreServices = List[Union[CoreService, Type[CoreService]]]
ConfigServiceType = Type[ConfigService]
_DEFAULT_MTU = 1500
class NodeBase(abc.ABC):
"""

View file

@ -19,6 +19,8 @@ if TYPE_CHECKING:
from core.emulator.session import Session
from core.nodes.base import CoreNetworkBase, CoreNode
DEFAULT_MTU: int = 1500
class CoreInterface:
"""
@ -338,7 +340,7 @@ class Veth(CoreInterface):
node: "CoreNode",
name: str,
localname: str,
mtu: int = 1500,
mtu: int = DEFAULT_MTU,
server: "DistributedServer" = None,
start: bool = True,
) -> None:
@ -403,7 +405,7 @@ class TunTap(CoreInterface):
node: "CoreNode",
name: str,
localname: str,
mtu: int = 1500,
mtu: int = DEFAULT_MTU,
server: "DistributedServer" = None,
start: bool = True,
) -> None:

View file

@ -3,6 +3,7 @@ Defines network nodes used within core.
"""
import logging
import math
import threading
import time
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
@ -447,77 +448,59 @@ class CoreNetwork(CoreNetworkBase):
:param iface2: interface two
:return: nothing
"""
devname = iface.localname
tc = f"{TC} qdisc replace dev {devname}"
parent = "root"
changed = False
bw = options.bandwidth
if iface.setparam("bw", bw):
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
burst = max(2 * iface.mtu, int(bw / 1000))
# 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))
# determine if any settings have changed
changed = any(
[
iface.setparam("bw", options.bandwidth),
iface.setparam("delay", options.delay),
iface.setparam("loss", options.loss),
iface.setparam("duplicate", options.dup),
iface.setparam("jitter", options.jitter),
]
)
if not changed:
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:
netem += f" loss {min(loss, 100)}%"
if duplicate is not None and duplicate > 0:
netem += f" duplicate {min(duplicate, 100)}%"
delay_check = delay is None or delay <= 0
jitter_check = jitter is None or jitter <= 0
loss_check = loss is None or loss <= 0
duplicate_check = duplicate is None or duplicate <= 0
if all([delay_check, jitter_check, loss_check, duplicate_check]):
# possibly remove netem if it exists and parent queue wasn't removed
# delete tc configuration or create and add it
devname = iface.localname
if all(
[
options.delay is None or options.delay <= 0,
options.jitter is None or options.jitter <= 0,
options.loss is None or options.loss <= 0,
options.dup is None or options.dup <= 0,
options.bandwidth is None or options.bandwidth <= 0,
]
):
if not iface.getparam("has_netem"):
return
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.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:
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.setparam("has_netem", True)

View file

@ -13,7 +13,7 @@ from core.emulator.enumerations import NodeTypes, TransportType
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 CoreInterface
from core.nodes.interface import DEFAULT_MTU, CoreInterface
from core.nodes.network import CoreNetwork, GreTap
if TYPE_CHECKING:
@ -252,7 +252,7 @@ class Rj45Node(CoreNodeBase):
session: "Session",
_id: int = None,
name: str = None,
mtu: int = 1500,
mtu: int = DEFAULT_MTU,
server: DistributedServer = None,
) -> None:
"""

View file

@ -8,7 +8,7 @@ import netaddr
from core.emane.nodes import EmaneNet
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.physical import Rj45Node
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
GreTap device.
"""
if iface.mtu != 1500:
if iface.mtu != DEFAULT_MTU:
# a workaround for PhysicalNode GreTap, which has no knowledge of
# the other nodes/nets
return " ip ospf mtu-ignore\n"

View file

@ -8,7 +8,7 @@ import netaddr
from core.emane.nodes import EmaneNet
from core.emulator.enumerations import LinkTypes
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.physical import Rj45Node
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
GreTap device.
"""
if iface.mtu != 1500:
if iface.mtu != DEFAULT_MTU:
# a workaround for PhysicalNode GreTap, which has no knowledge of
# the other nodes/nets
return " ip ospf mtu-ignore\n"