daemon: updates to switch from using ebtables to nftables for wlan linking/unlinking

This commit is contained in:
Blake Harnden 2021-05-10 15:07:42 -07:00
parent 208c746b67
commit 30291a8438
14 changed files with 153 additions and 206 deletions

View file

@ -57,7 +57,7 @@ fpm -s dir -t deb -n core-distributed \
-d "procps" \ -d "procps" \
-d "libc6 >= 2.14" \ -d "libc6 >= 2.14" \
-d "bash >= 3.0" \ -d "bash >= 3.0" \
-d "ebtables" \ -d "nftables" \
-d "iproute2" \ -d "iproute2" \
-d "libev4" \ -d "libev4" \
-d "openssh-server" \ -d "openssh-server" \
@ -77,7 +77,7 @@ fpm -s dir -t rpm -n core-distributed \
-d "ethtool" \ -d "ethtool" \
-d "procps-ng" \ -d "procps-ng" \
-d "bash >= 3.0" \ -d "bash >= 3.0" \
-d "ebtables" \ -d "nftables" \
-d "iproute" \ -d "iproute" \
-d "libev" \ -d "libev" \
-d "net-tools" \ -d "net-tools" \

View file

@ -123,9 +123,9 @@ if test "x$enable_daemon" = "xyes"; then
AC_MSG_ERROR([Could not locate sysctl (from procps package).]) AC_MSG_ERROR([Could not locate sysctl (from procps package).])
fi fi
AC_CHECK_PROG(ebtables_path, ebtables, $as_dir, no, $SEARCHPATH) AC_CHECK_PROG(nftables_path, nft, $as_dir, no, $SEARCHPATH)
if test "x$ebtables_path" = "xno" ; then if test "x$nftables_path" = "xno" ; then
AC_MSG_ERROR([Could not locate ebtables (from ebtables package).]) AC_MSG_ERROR([Could not locate nftables (from nftables package).])
fi fi
AC_CHECK_PROG(ip_path, ip, $as_dir, no, $SEARCHPATH) AC_CHECK_PROG(ip_path, ip, $as_dir, no, $SEARCHPATH)

View file

@ -7,15 +7,15 @@ SYSCTL: str = "sysctl"
IP: str = "ip" IP: str = "ip"
ETHTOOL: str = "ethtool" ETHTOOL: str = "ethtool"
TC: str = "tc" TC: str = "tc"
EBTABLES: str = "ebtables"
MOUNT: str = "mount" MOUNT: str = "mount"
UMOUNT: str = "umount" UMOUNT: str = "umount"
OVS_VSCTL: str = "ovs-vsctl" OVS_VSCTL: str = "ovs-vsctl"
TEST: str = "test" TEST: str = "test"
NFTABLES: str = "nft"
COMMON_REQUIREMENTS: List[str] = [ COMMON_REQUIREMENTS: List[str] = [
BASH, BASH,
EBTABLES, NFTABLES,
ETHTOOL, ETHTOOL,
IP, IP,
MOUNT, MOUNT,

View file

@ -669,7 +669,7 @@ class CoreClient:
else: else:
services = self.session.default_services.get(model) services = self.session.default_services.get(model)
if services: if services:
node.config_services = services.copy() node.config_services = set(services)
logger.info( logger.info(
"add node(%s) to session(%s), coordinates(%s, %s)", "add node(%s) to session(%s), coordinates(%s, %s)",
node.name, node.name,

View file

@ -947,9 +947,9 @@ class CoreNetworkBase(NodeBase):
will run on, default is None for localhost will run on, default is None for localhost
""" """
super().__init__(session, _id, name, server) super().__init__(session, _id, name, server)
self.brname = None self.brname: Optional[str] = None
self._linked = {} self._linked: Dict[CoreInterface, Dict[CoreInterface, bool]] = {}
self._linked_lock = threading.Lock() self._linked_lock: threading.Lock = threading.Lock()
@abc.abstractmethod @abc.abstractmethod
def startup(self) -> None: def startup(self) -> None:

View file

@ -21,7 +21,7 @@ from core.emulator.enumerations import (
RegisterTlvs, RegisterTlvs,
) )
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.executables import EBTABLES, TC from core.executables import NFTABLES, TC
from core.nodes.base import CoreNetworkBase from core.nodes.base import CoreNetworkBase
from core.nodes.interface import CoreInterface, GreTap, Veth from core.nodes.interface import CoreInterface, GreTap, Veth
from core.nodes.netclient import get_net_client from core.nodes.netclient import get_net_client
@ -36,31 +36,31 @@ if TYPE_CHECKING:
WirelessModelType = Type[WirelessModel] WirelessModelType = Type[WirelessModel]
LEARNING_DISABLED: int = 0 LEARNING_DISABLED: int = 0
ebtables_lock: threading.Lock = threading.Lock() NFTABLES_LOCK: threading.Lock = threading.Lock()
class EbtablesQueue: class NftablesQueue:
""" """
Helper class for queuing up ebtables commands into rate-limited Helper class for queuing up nftables commands into rate-limited
atomic commits. This improves performance and reliability when there are atomic commits. This improves performance and reliability when there are
many WLAN link updates. many WLAN link updates.
""" """
# update rate is every 300ms # update rate is every 300ms
rate: float = 0.3 rate: float = 0.3
# ebtables atomic_file: str = "/tmp/pycore.nftables.atomic"
atomic_file: str = "/tmp/pycore.ebtables.atomic" chain: str = "forward"
def __init__(self) -> None: def __init__(self) -> None:
""" """
Initialize the helper class, but don't start the update thread Initialize the helper class, but don't start the update thread
until a WLAN is instantiated. until a WLAN is instantiated.
""" """
self.doupdateloop: bool = False self.running: bool = False
self.updatethread: Optional[threading.Thread] = None self.run_thread: Optional[threading.Thread] = None
# this lock protects cmds and updates lists # this lock protects cmds and updates lists
self.updatelock: threading.Lock = threading.Lock() self.lock: threading.Lock = threading.Lock()
# list of pending ebtables commands # list of pending nftables commands
self.cmds: List[str] = [] self.cmds: List[str] = []
# list of WLANs requiring update # list of WLANs requiring update
self.updates: List["CoreNetwork"] = [] self.updates: List["CoreNetwork"] = []
@ -68,192 +68,164 @@ class EbtablesQueue:
# using this queue # using this queue
self.last_update_time: Dict["CoreNetwork", float] = {} self.last_update_time: Dict["CoreNetwork", float] = {}
def startupdateloop(self, wlan: "CoreNetwork") -> None: def start(self, net: "CoreNetwork") -> None:
""" """
Kick off the update loop; only needs to be invoked once. Start thread to listen for updates for the provided network.
:param net: network to start checking updates
:return: nothing :return: nothing
""" """
with self.updatelock: with self.lock:
self.last_update_time[wlan] = time.monotonic() self.last_update_time[net] = time.monotonic()
if self.doupdateloop: if self.running:
return return
self.doupdateloop = True self.running = True
self.updatethread = threading.Thread(target=self.updateloop, daemon=True) self.run_thread = threading.Thread(target=self.run, daemon=True)
self.updatethread.start() self.run_thread.start()
def stopupdateloop(self, wlan: "CoreNetwork") -> None: def stop(self, net: "CoreNetwork") -> None:
""" """
Kill the update loop thread if there are no more WLANs using it. Stop updates for network, when no networks remain, stop update thread.
:param net: network to stop watching updates
:return: nothing :return: nothing
""" """
with self.updatelock: with self.lock:
try: self.last_update_time.pop(net, None)
del self.last_update_time[wlan] if self.last_update_time:
except KeyError: return
logger.exception( self.running = False
"error deleting last update time for wlan, ignored before: %s", wlan if self.run_thread:
) self.run_thread.join()
if len(self.last_update_time) > 0: self.run_thread = None
return
self.doupdateloop = False
if self.updatethread:
self.updatethread.join()
self.updatethread = None
def ebatomiccmd(self, cmd: str) -> str: def last_update(self, net: "CoreNetwork") -> float:
""" """
Helper for building ebtables atomic file command list. Return the time elapsed since this network was last updated.
:param net: network node
:param cmd: ebtable command
:return: ebtable atomic command
"""
return f"{EBTABLES} --atomic-file {self.atomic_file} {cmd}"
def lastupdate(self, wlan: "CoreNetwork") -> float:
"""
Return the time elapsed since this WLAN was last updated.
:param wlan: wlan entity
:return: elpased time :return: elpased time
""" """
try: if net in self.last_update_time:
elapsed = time.monotonic() - self.last_update_time[wlan] elapsed = time.monotonic() - self.last_update_time[net]
except KeyError: else:
self.last_update_time[wlan] = time.monotonic() self.last_update_time[net] = time.monotonic()
elapsed = 0.0 elapsed = 0.0
return elapsed return elapsed
def updated(self, wlan: "CoreNetwork") -> None: def updated(self, net: "CoreNetwork") -> None:
""" """
Keep track of when this WLAN was last updated. Keep track of when this network was last updated.
:param wlan: wlan entity :param net: network node
:return: nothing :return: nothing
""" """
self.last_update_time[wlan] = time.monotonic() self.last_update_time[net] = time.monotonic()
self.updates.remove(wlan) self.updates.remove(net)
def updateloop(self) -> None: def run(self) -> None:
""" """
Thread target that looks for WLANs needing update, and Thread target that looks for networks needing update, and
rate limits the amount of ebtables activity. Only one userspace program rate limits the amount of nftables activity. Only one userspace program
should use ebtables at any given time, or results can be unpredictable. should use nftables at any given time, or results can be unpredictable.
:return: nothing :return: nothing
""" """
while self.doupdateloop: while self.running:
with self.updatelock: with self.lock:
for wlan in self.updates: for net in self.updates:
# Check if wlan is from a previously closed session. Because of the if not net.up:
# rate limiting scheme employed here, this may happen if a new session self.updated(net)
# is started soon after closing a previous session.
# TODO: if these are WlanNodes, this will never throw an exception
try:
wlan.session
except Exception:
# Just mark as updated to remove from self.updates.
self.updated(wlan)
continue continue
if self.last_update(net) > self.rate:
if self.lastupdate(wlan) > self.rate: self.build_cmds(net)
self.buildcmds(wlan) self.commit(net)
self.ebcommit(wlan) self.updated(net)
self.updated(wlan)
time.sleep(self.rate) time.sleep(self.rate)
def ebcommit(self, wlan: "CoreNetwork") -> None: def commit(self, net: "CoreNetwork") -> None:
""" """
Perform ebtables atomic commit using commands built in the self.cmds list. Commit changes to nftables for the provided network.
:param net: network to commit nftables changes
:return: nothing :return: nothing
""" """
# save kernel ebtables snapshot to a file if not self.cmds:
args = self.ebatomiccmd("--atomic-save") return
wlan.host_cmd(args) # write out nft commands to file
for cmd in self.cmds:
net.host_cmd(f"echo {cmd} >> {self.atomic_file}", shell=True)
# read file as atomic change
net.host_cmd(f"{NFTABLES} -f {self.atomic_file}")
# remove file
net.host_cmd(f"rm -f {self.atomic_file}")
self.cmds.clear()
# modify the table file using queued ebtables commands def update(self, net: "CoreNetwork") -> None:
for c in self.cmds:
args = self.ebatomiccmd(c)
wlan.host_cmd(args)
self.cmds = []
# commit the table file to the kernel
args = self.ebatomiccmd("--atomic-commit")
wlan.host_cmd(args)
try:
wlan.host_cmd(f"rm -f {self.atomic_file}")
except CoreCommandError:
logger.exception("error removing atomic file: %s", self.atomic_file)
def ebchange(self, wlan: "CoreNetwork") -> None:
""" """
Flag a change to the given WLAN's _linked dict, so the ebtables Flag this network has an update, so the nftables chain will be rebuilt.
chain will be rebuilt at the next interval. :param net: wlan network
:return: nothing :return: nothing
""" """
with self.updatelock: with self.lock:
if wlan not in self.updates: if net not in self.updates:
self.updates.append(wlan) self.updates.append(net)
def buildcmds(self, wlan: "CoreNetwork") -> None: def build_cmds(self, net: "CoreNetwork") -> None:
""" """
Inspect a _linked dict from a wlan, and rebuild the ebtables chain for that WLAN. Inspect linked nodes for a network, and rebuild the nftables chain commands.
:param net: network to build commands for
:return: nothing :return: nothing
""" """
with wlan._linked_lock: with net._linked_lock:
if wlan.has_ebtables_chain: if net.has_nftables_chain:
# flush the chain self.cmds.append(f"flush table bridge {net.brname}")
self.cmds.append(f"-F {wlan.brname}")
else: else:
wlan.has_ebtables_chain = True net.has_nftables_chain = True
self.cmds.extend( policy = net.policy.value.lower()
[ self.cmds.append(f"add table bridge {net.brname}")
f"-N {wlan.brname} -P {wlan.policy.value}", self.cmds.append(
f"-A FORWARD --logical-in {wlan.brname} -j {wlan.brname}", f"add chain bridge {net.brname} {self.chain} {{type filter hook "
] f"forward priority 0\\; policy {policy}\\;}}"
) )
# add default rule to accept all traffic not for this bridge
self.cmds.append(
f"add rule bridge {net.brname} {self.chain} "
f"ibriport != {net.brname} accept"
)
# rebuild the chain # rebuild the chain
for iface1, v in wlan._linked.items(): for iface1, v in net._linked.items():
for oface2, linked in v.items(): for iface2, linked in v.items():
if wlan.policy == NetworkPolicy.DROP and linked: policy = None
self.cmds.extend( if net.policy == NetworkPolicy.DROP and linked:
[ policy = "accept"
f"-A {wlan.brname} -i {iface1.localname} -o {oface2.localname} -j ACCEPT", elif net.policy == NetworkPolicy.ACCEPT and not linked:
f"-A {wlan.brname} -o {iface1.localname} -i {oface2.localname} -j ACCEPT", policy = "drop"
] if policy:
self.cmds.append(
f"add rule bridge {net.brname} {self.chain} "
f"iif {iface1.localname} oif {iface2.localname} "
f"{policy}"
) )
elif wlan.policy == NetworkPolicy.ACCEPT and not linked: self.cmds.append(
self.cmds.extend( f"add rule bridge {net.brname} {self.chain} "
[ f"oif {iface1.localname} iif {iface2.localname} "
f"-A {wlan.brname} -i {iface1.localname} -o {oface2.localname} -j DROP", f"{policy}"
f"-A {wlan.brname} -o {iface1.localname} -i {oface2.localname} -j DROP",
]
) )
# a global object because all WLANs share the same queue # a global object because all networks share the same queue
# cannot have multiple threads invoking the ebtables commnd # cannot have multiple threads invoking the nftables commnd
ebq: EbtablesQueue = EbtablesQueue() nft_queue: NftablesQueue = NftablesQueue()
def ebtablescmds(call: Callable[..., str], cmds: List[str]) -> None: def nftables_cmds(call: Callable[..., str], cmds: List[str]) -> None:
""" """
Run ebtable commands. Run nftable commands.
:param call: function to call commands :param call: function to call commands
:param cmds: commands to call :param cmds: commands to call
:return: nothing :return: nothing
""" """
with ebtables_lock: with NFTABLES_LOCK:
for args in cmds: for cmd in cmds:
call(args) call(cmd)
class CoreNetwork(CoreNetworkBase): class CoreNetwork(CoreNetworkBase):
@ -285,11 +257,11 @@ class CoreNetwork(CoreNetworkBase):
if name is None: if name is None:
name = str(self.id) name = str(self.id)
if policy is not None: if policy is not None:
self.policy = policy self.policy: NetworkPolicy = policy
self.name: Optional[str] = name self.name: Optional[str] = name
sessionid = self.session.short_session_id() sessionid = self.session.short_session_id()
self.brname: str = f"b.{self.id}.{sessionid}" self.brname: str = f"b.{self.id}.{sessionid}"
self.has_ebtables_chain: bool = False self.has_nftables_chain: bool = False
def host_cmd( def host_cmd(
self, self,
@ -324,9 +296,9 @@ class CoreNetwork(CoreNetworkBase):
:raises CoreCommandError: when there is a command exception :raises CoreCommandError: when there is a command exception
""" """
self.net_client.create_bridge(self.brname) self.net_client.create_bridge(self.brname)
self.has_ebtables_chain = False self.has_nftables_chain = False
self.up = True self.up = True
ebq.startupdateloop(self) nft_queue.start(self)
def shutdown(self) -> None: def shutdown(self) -> None:
""" """
@ -336,23 +308,19 @@ class CoreNetwork(CoreNetworkBase):
""" """
if not self.up: if not self.up:
return return
ebq.stopupdateloop(self) nft_queue.stop(self)
try: try:
self.net_client.delete_bridge(self.brname) self.net_client.delete_bridge(self.brname)
if self.has_ebtables_chain: if self.has_nftables_chain:
cmds = [ cmds = [f"{NFTABLES} delete table bridge {self.brname}"]
f"{EBTABLES} -D FORWARD --logical-in {self.brname} -j {self.brname}", nftables_cmds(self.host_cmd, cmds)
f"{EBTABLES} -X {self.brname}",
]
ebtablescmds(self.host_cmd, cmds)
except CoreCommandError: except CoreCommandError:
logger.exception("error during shutdown") logging.exception("error during shutdown")
# removes veth pairs used for bridge-to-bridge connections # removes veth pairs used for bridge-to-bridge connections
for iface in self.get_ifaces(): for iface in self.get_ifaces():
iface.shutdown() iface.shutdown()
self.ifaces.clear() self.ifaces.clear()
self._linked.clear() self._linked.clear()
del self.session
self.up = False self.up = False
def attach(self, iface: CoreInterface) -> None: def attach(self, iface: CoreInterface) -> None:
@ -404,8 +372,7 @@ class CoreNetwork(CoreNetworkBase):
def unlink(self, iface1: CoreInterface, iface2: CoreInterface) -> None: def unlink(self, iface1: CoreInterface, iface2: CoreInterface) -> None:
""" """
Unlink two interfaces, resulting in adding or removing ebtables Unlink two interfaces, resulting in adding or removing filtering rules.
filtering rules.
:param iface1: interface one :param iface1: interface one
:param iface2: interface two :param iface2: interface two
@ -415,13 +382,12 @@ class CoreNetwork(CoreNetworkBase):
if not self.linked(iface1, iface2): if not self.linked(iface1, iface2):
return return
self._linked[iface1][iface2] = False self._linked[iface1][iface2] = False
nft_queue.update(self)
ebq.ebchange(self)
def link(self, iface1: CoreInterface, iface2: CoreInterface) -> None: def link(self, iface1: CoreInterface, iface2: CoreInterface) -> None:
""" """
Link two interfaces together, resulting in adding or removing Link two interfaces together, resulting in adding or removing
ebtables filtering rules. filtering rules.
:param iface1: interface one :param iface1: interface one
:param iface2: interface two :param iface2: interface two
@ -431,8 +397,7 @@ class CoreNetwork(CoreNetworkBase):
if self.linked(iface1, iface2): if self.linked(iface1, iface2):
return return
self._linked[iface1][iface2] = True self._linked[iface1][iface2] = True
nft_queue.update(self)
ebq.ebchange(self)
def linkconfig( def linkconfig(
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
@ -986,7 +951,7 @@ class WlanNode(CoreNetwork):
:return: nothing :return: nothing
""" """
super().startup() super().startup()
ebq.ebchange(self) nft_queue.update(self)
def attach(self, iface: CoreInterface) -> None: def attach(self, iface: CoreInterface) -> None:
""" """

View file

@ -63,8 +63,8 @@ eval "$ifcommand" | awk '
/b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);} /b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
' '
ebtables -L FORWARD | awk ' nft list ruleset | awk '
/^-.*b\./ {print "removing ebtables " $0; system("ebtables -D FORWARD " $0); print "removing ebtables chain " $4; system("ebtables -X " $4);} $3 ~ /^b\./ {print "removing nftables " $3; system("nft delete table bridge " $3);}
' '
rm -rf /tmp/pycore* rm -rf /tmp/pycore*

View file

@ -51,8 +51,8 @@ filesystem in CORE.
CORE combines these namespaces with Linux Ethernet bridging to form networks. CORE combines these namespaces with Linux Ethernet bridging to form networks.
Link characteristics are applied using Linux Netem queuing disciplines. Link characteristics are applied using Linux Netem queuing disciplines.
Ebtables is Ethernet frame filtering on Linux bridges. Wireless networks are Nftables provides Ethernet frame filtering on Linux bridges. Wireless networks are
emulated by controlling which interfaces can send and receive with ebtables emulated by controlling which interfaces can send and receive with nftables
rules. rules.
## Prior Work ## Prior Work

View file

@ -104,7 +104,7 @@ vcmd -c /tmp/pycore.50160/n1 -- /sbin/ip -4 ro
A script named *core-cleanup* is provided to clean up any running CORE emulations. It will attempt to kill any A script named *core-cleanup* is provided to clean up any running CORE emulations. It will attempt to kill any
remaining vnoded processes, kill any EMANE processes, remove the :file:`/tmp/pycore.*` session directories, and remove remaining vnoded processes, kill any EMANE processes, remove the :file:`/tmp/pycore.*` session directories, and remove
any bridges or *ebtables* rules. With a *-d* option, it will also kill any running CORE daemon. any bridges or *nftables* rules. With a *-d* option, it will also kill any running CORE daemon.
### netns command ### netns command
@ -121,5 +121,5 @@ ip link show type bridge
# view the netem rules used for applying link effects # view the netem rules used for applying link effects
tc qdisc show tc qdisc show
# view the rules that make the wireless LAN work # view the rules that make the wireless LAN work
ebtables -L nft list ruleset
``` ```

View file

@ -172,7 +172,7 @@ will draw the link with a dashed line.
Wireless nodes, i.e. those connected to a WLAN node, can be assigned to Wireless nodes, i.e. those connected to a WLAN node, can be assigned to
different emulation servers and participate in the same wireless network different emulation servers and participate in the same wireless network
only if an EMANE model is used for the WLAN. The basic range model does only if an EMANE model is used for the WLAN. The basic range model does
not work across multiple servers due to the Linux bridging and ebtables not work across multiple servers due to the Linux bridging and nftables
rules that are used. rules that are used.
**NOTE: The basic range wireless model does not support distributed emulation, **NOTE: The basic range wireless model does not support distributed emulation,

View file

@ -544,7 +544,7 @@ on platform. See the table below for a brief overview of wireless model types.
|Model|Type|Supported Platform(s)|Fidelity|Description| |Model|Type|Supported Platform(s)|Fidelity|Description|
|-----|----|---------------------|--------|-----------| |-----|----|---------------------|--------|-----------|
|Basic|on/off|Linux|Low|Ethernet bridging with ebtables| |Basic|on/off|Linux|Low|Ethernet bridging with nftables|
|EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types| |EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types|
To quickly build a wireless network, you can first place several router nodes To quickly build a wireless network, you can first place several router nodes

View file

@ -15,20 +15,14 @@ containers, as a general rule you should select a machine having as much RAM and
* Linux Kernel v3.3+ * Linux Kernel v3.3+
* iproute2 4.5+ is a requirement for bridge related commands * iproute2 4.5+ is a requirement for bridge related commands
* ebtables not backed by nftables * nftables compatible kernel and nft command line tool
### Supported Linux Distributions ### Supported Linux Distributions
Plan is to support recent Ubuntu and CentOS LTS releases. Plan is to support recent Ubuntu and CentOS LTS releases.
Verified: Verified:
* Ubuntu - 18.04, 20.04 * Ubuntu - 18.04, 20.04
* CentOS - 7.8, 8.0* * CentOS - 7.8, 8.0
> **NOTE:** Ubuntu 20.04 requires installing legacy ebtables for WLAN
> functionality
> **NOTE:** CentOS 8 does not provide legacy ebtables support, WLAN will not
> function properly
> **NOTE:** CentOS 8 does not have the netem kernel mod available by default > **NOTE:** CentOS 8 does not have the netem kernel mod available by default

View file

@ -521,7 +521,7 @@ on platform. See the table below for a brief overview of wireless model types.
|Model|Type|Supported Platform(s)|Fidelity|Description| |Model|Type|Supported Platform(s)|Fidelity|Description|
|-----|----|---------------------|--------|-----------| |-----|----|---------------------|--------|-----------|
|Basic|on/off|Linux|Low|Ethernet bridging with ebtables| |Basic|on/off|Linux|Low|Ethernet bridging with nftables|
|EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types| |EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types|
To quickly build a wireless network, you can first place several router nodes To quickly build a wireless network, you can first place several router nodes

View file

@ -159,14 +159,14 @@ def check_existing_core(c: Context, hide: bool) -> None:
def install_system(c: Context, os_info: OsInfo, hide: bool) -> None: def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
if os_info.like == OsLike.DEBIAN: if os_info.like == OsLike.DEBIAN:
c.run( c.run(
"sudo apt install -y automake pkg-config gcc libev-dev ebtables " "sudo apt install -y automake pkg-config gcc libev-dev nftables "
"iproute2 ethtool tk python3-tk bash", "iproute2 ethtool tk python3-tk bash",
hide=hide hide=hide
) )
elif os_info.like == OsLike.REDHAT: elif os_info.like == OsLike.REDHAT:
c.run( c.run(
"sudo yum install -y automake pkgconf-pkg-config gcc gcc-c++ " "sudo yum install -y automake pkgconf-pkg-config gcc gcc-c++ "
"libev-devel iptables-ebtables iproute python3-devel python3-tkinter " "libev-devel nftables iproute python3-devel python3-tkinter "
"tk ethtool make bash", "tk ethtool make bash",
hide=hide hide=hide
) )
@ -179,18 +179,6 @@ def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
print("sudo yum update") print("sudo yum update")
sys.exit(1) sys.exit(1)
# attempt to setup legacy ebtables when an nftables based version is found
r = c.run("ebtables -V", hide=hide)
if "nf_tables" in r.stdout:
if not c.run(
"sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy",
warn=True,
hide=hide
):
print(
"\nWARNING: unable to setup ebtables-legacy, WLAN will not work"
)
def install_grpcio(c: Context, hide: bool) -> None: def install_grpcio(c: Context, hide: bool) -> None:
c.run( c.run(