Skip to content

Commit

Permalink
Merge pull request #484 from tiiuae/bridge_control
Browse files Browse the repository at this point in the history
Change br-lan control
  • Loading branch information
saauvine authored Oct 23, 2024
2 parents 61d2773 + f4af286 commit e9ad4c4
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ CBMA:
- sap0
- sta0
- wfd0
white_interfaces:
- end0
- end1
white_interfaces:
red_interfaces:
- wlan1
- usb0
- eth0
- end0
- end1
- lan1

BATMAN:
routing_algo: BATMAN_V
Expand All @@ -35,17 +41,17 @@ BATMAN:
hardif:
halow1: 20

VLAN:
#VLAN:
# Remember that IP address definitions for such interface that is added to
# CBMA's red_interfaces list (i.e. br-lan bridge) are not effective.
vlan_black:
parent_interface: eth0
vlan_id: 100
ipv4_address: 192.168.1.1
ipv4_subnet_mask: 255.255.255.0
ipv6_local_address: fe80::192.168.1.1
ipv6_prefix_length: 64
vlan_red:
parent_interface: eth0
vlan_id: 200
# vlan_black:
# parent_interface: eth0
# vlan_id: 100
# ipv4_address: 192.168.1.1
# ipv4_subnet_mask: 255.255.255.0
# ipv6_local_address: fe80::192.168.1.1
# ipv6_prefix_length: 64
# vlan_red:
# parent_interface: eth0
# vlan_id: 200

113 changes: 98 additions & 15 deletions modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(
self.BR_NAME: str = Constants.BR_NAME.value
self.LOWER_BATMAN: str = Constants.LOWER_BATMAN.value
self.UPPER_BATMAN: str = Constants.UPPER_BATMAN.value
self.ALLOWED_KIND_LIST = {"vlan", "batadv"}
self.ALLOWED_KIND_LIST = {"vlan", "batadv", "dsa"}

# Set minimum required configuration
self.__white_interfaces = [self.LOWER_BATMAN]
Expand Down Expand Up @@ -116,9 +116,11 @@ def __validate_cbma_config(
black_interfaces: List[str] = []

# Validate input param types
if not isinstance(white_interfaces, list) or \
not isinstance(red_interfaces, list) or \
not isinstance(exclude_interfaces, list):
if (
not isinstance(white_interfaces, list)
or not isinstance(red_interfaces, list)
or not isinstance(exclude_interfaces, list)
):
self.logger.error("Input params are not lists!")
return False

Expand Down Expand Up @@ -270,6 +272,7 @@ def __delete_vlan_interfaces(self) -> bool:
interface.interface_name == vlan_name
for interface in self.__interfaces
):
self.__shutdown_interface(vlan_name)
# Delete existing VLAN interface
subprocess.run(["ip", "link", "delete", vlan_name], check=True)
except subprocess.CalledProcessError as e:
Expand Down Expand Up @@ -310,8 +313,8 @@ def __get_interfaces(self) -> None:
# thus should add only physical interfaces, vlan and batadv interfaces
if kind is None or kind in self.ALLOWED_KIND_LIST:
interfaces.append(interface_info)
# We want to monitor also br-lan status thus expcetion to basic rules
elif kind == "bridge" and ifname== Constants.BR_NAME.value:
# We want to monitor also br-lan status thus exception to basic rules
elif kind == "bridge" and ifname == Constants.BR_NAME.value:
interfaces.append(interface_info)
ip.close()

Expand Down Expand Up @@ -362,6 +365,7 @@ def __add_interface_to_bridge(
ip = IPRoute()

try:
# Get the index of the bridge
bridge_indices = ip.link_lookup(ifname=bridge_name)
if bridge_indices:
bridge_index = bridge_indices[0]
Expand All @@ -374,7 +378,6 @@ def __add_interface_to_bridge(

# Get the index of the interface to add
interface_indices = ip.link_lookup(ifname=interface_to_add)

if not interface_indices:
self.logger.debug(
"Cannot add interface %s to bridge %s!",
Expand All @@ -384,9 +387,27 @@ def __add_interface_to_bridge(
return

interface_index = interface_indices[0]
# Add the interface to the bridge
ip.link("set", index=interface_index, master=bridge_index)

# Get the current interface details
interface_info = ip.get_links(interface_index)[0]
current_master = interface_info.get("master")

if current_master == bridge_index:
self.logger.info(
"Interface %s is already part of the bridge %s.",
interface_to_add,
bridge_name,
)
else:
# Add the interface to the bridge
ip.link("set", index=interface_index, master=bridge_index)
self.logger.info(
"Successfully added interface %s to bridge %s.",
interface_to_add,
bridge_name,
)
# Bring the interface up
self.__set_interface_up(interface_to_add)
except Exception as e:
self.logger.error(
"Error adding interface %s to bridge %s! Error: %s",
Expand Down Expand Up @@ -415,6 +436,7 @@ def __shutdown_interface(self, interface_name: str) -> None:
try:
index = ip.link_lookup(ifname=interface_name)[0]
ip.link("set", index=index, state="down")
ip.link("set", index=index, master=None)
except IndexError:
self.logger.debug(
"Not able to shutdown interface %s! Interface not found!",
Expand Down Expand Up @@ -510,6 +532,35 @@ def __update_cbma_interface_lists(self) -> None:
if interface in self.__lower_cbma_interfaces:
self.__lower_cbma_interfaces.remove(interface)

def __update_red_interfaces_list(self):
"""
Updates the red_interfaces list:
1. Filters red_interfaces to keep only those interfaces that exist in self.__interfaces.
2. Handles mutually exclusive interfaces, removing eth/end interfaces if a lan interface is found.
3. Ensures "UPPER_BATMAN" is present in the red_interfaces list.
"""
# Step 1: Filter red_interfaces to keep only interfaces that exist in self.__interfaces.
red_interfaces = [
interface
for interface in self.__red_interfaces
if any(iface.interface_name == interface for iface in self.__interfaces)
]

# Step 2: Remove eth_end_interfaces if any lan interface is found in red_interfaces.
lan_interfaces = {"lan1", "lan2", "lan3"}
eth_end_interfaces = {"eth0", "end0", "end1"}

if any(lan in red_interfaces for lan in lan_interfaces):
red_interfaces = [
iface for iface in red_interfaces if iface not in eth_end_interfaces
]

# Step 3: Ensure "UPPER_BATMAN" is present in the red_interfaces list.
if self.UPPER_BATMAN not in red_interfaces:
red_interfaces.append(self.UPPER_BATMAN)
# Update the internal red interfaces list.
self.__red_interfaces = red_interfaces

def __wait_for_ap(self, timeout: int = 4) -> bool:
start_time = time.time()
while True:
Expand Down Expand Up @@ -587,13 +638,40 @@ def __create_mac(self, randomized: bool = False, interface_mac: str = "") -> str
return bytes(mac).hex(sep=":", bytes_per_sep=1)
else:
# Flip the locally administered bit
mac_bytes = bytearray.fromhex(interface_mac.replace(':', ''))
mac_bytes = bytearray.fromhex(interface_mac.replace(":", ""))
mac_bytes[0] ^= 0x2
return mac_bytes.hex(sep=':', bytes_per_sep=1)
return mac_bytes.hex(sep=":", bytes_per_sep=1)

def __create_mac_for_bridge(self) -> str:
"""
Creates a MAC address for the bridge. MAC is created from osf, eth0,
end0, end1 or wlan1 interface MAC by flipping the local admin bit.
In case all those interfaces are configured as black interfaces
(i.e. they are configured as red or white interfaces or excluded from CBMA)
then a random MAC is generated for the bridge.
:return: generated MAC for the bridge usage
"""
interfaces: List[str] = ["osf", "eth0", "end0", "end1", "wlan1"]
for interface in interfaces:
if (
interface in self.__white_interfaces
or interface in self.__red_interfaces
or interface in self.__na_cbma_interfaces
):
mac = self.__get_mac_addr(interface)
if mac:
# Flip the locally administered bit
mac_bytes = bytearray.fromhex(mac.replace(":", ""))
if not (mac_bytes[0] & 0x2):
mac_bytes[0] |= 0x2
return mac_bytes.hex(sep=":", bytes_per_sep=1)
# Generate random MAC in case didn't get any earlier
return self.__create_mac(True)

def __init_batman_and_bridge(self) -> None:
if_name = self.__comms_ctrl.settings.mesh_vif[0]
if if_name.startswith("halow"):
if if_name.startswith("halow") or if_name.startswith("morse"):
if_pattern = "wlp*s0"
for interface_name in self.__comms_ctrl.settings.mesh_vif:
if fnmatch.fnmatch(interface_name, if_pattern):
Expand All @@ -615,7 +693,7 @@ def __init_batman_and_bridge(self) -> None:
self.__create_bridge(self.BR_NAME)

# Set random MAC address for the bridge
self.__set_interface_mac(self.BR_NAME, self.__create_mac(True))
self.__set_interface_mac(self.BR_NAME, self.__create_mac_for_bridge())
self.__wait_for_interface(self.LOWER_BATMAN)
self.__wait_for_interface(self.UPPER_BATMAN)

Expand Down Expand Up @@ -661,7 +739,7 @@ def __setup_radios(self) -> bool:
for interface_name in self.__comms_ctrl.settings.mesh_vif:
self.logger.debug("mesh_vif: %s", interface_name)
timeout = 5
if interface_name.startswith("halow"):
if interface_name.startswith("halow") or interface_name.startswith("morse"):
timeout = 20
self.__wait_for_interface(interface_name, timeout)

Expand Down Expand Up @@ -734,6 +812,7 @@ def setup_cbma(self) -> bool:
self.__init_batman_and_bridge()

self.__update_cbma_interface_lists()
self.__update_red_interfaces_list()

# Set radios on
self.__setup_radios()
Expand Down Expand Up @@ -962,6 +1041,11 @@ def __setup_upper_cbma(self) -> bool:
return intf_added

def __cleanup_cbma(self) -> None:
"""
Shuts down /which removes them from bridge also) Batman interfaces
and then deletes them. Also deletes any created VLAN interfaces but
lets bridge remain for eample for ethernet usage.
"""
self.__get_interfaces()
self.__shutdown_interface(self.LOWER_BATMAN)
self.__shutdown_interface(self.UPPER_BATMAN)
Expand All @@ -970,7 +1054,6 @@ def __cleanup_cbma(self) -> None:
self.__batman.destroy_batman_interface(self.UPPER_BATMAN)

self.__delete_vlan_interfaces()
self.__shutdown_and_delete_bridge(self.BR_NAME)

def __stop_controller(self, controller, name: str) -> bool:
"""
Expand Down

0 comments on commit e9ad4c4

Please sign in to comment.