Merge pull request #529 from coreemu/enhancement/link-buffer-size

updates for link configuration to calculate a limit when bandwidth/delay are present
This commit is contained in:
bharnden 2020-11-20 17:06:35 -08:00 committed by GitHub
commit d95c2ec05f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 78 deletions

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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):
""" """

View file

@ -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:

View file

@ -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)

View file

@ -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:
""" """

View file

@ -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"

View file

@ -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"