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