diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/VERSION b/modules/sc-mesh-secure-deployment/src/nats/cbma/VERSION index d0a45d62..fbce69be 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/VERSION +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/VERSION @@ -1,4 +1,4 @@ -GIT_VERSION=v0.1.7-old_requirements_fixes -GIT_SHA=2b333ce8b4308663fd7901d87add93b731314f56 -EPOCH_TIMESTAMP=1717686203 -PRECISE_DATE_TIMESTAMP="2024-06-06 - 15:03:23.866091572" +GIT_VERSION=v0.1.8-old_requirements_fixes +GIT_SHA=a5479177202ef1b312262e988a50ea481944d973 +EPOCH_TIMESTAMP=1720347074 +PRECISE_DATE_TIMESTAMP="2024-07-07 - 10:11:14.674692664" diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/macsec/macsec.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/macsec/macsec.py index afaf379e..97a14b22 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/macsec/macsec.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/macsec/macsec.py @@ -34,8 +34,6 @@ def __init__(self, self.tx_key: str = '' self.rx_key: str = '' - self.tx_port: int = 0 - self.rx_port: int = 0 def __termination_handler(self) -> None: @@ -57,14 +55,12 @@ def start(self, conn: SecureConnection) -> bool: def update_config(self, conn: SecureConnection) -> bool: try: - keys, ports = get_macsec_config(conn) + tx_key, rx_key = get_macsec_config(conn) conn.close() except Exception as e: logger.error(f"Exception when obtaining MACsec configuration: {e}") conn.close() return False - tx_key, rx_key = keys - tx_port, rx_port = ports if self.is_upper: self.tx_key = tx_key.hex()[:self.UPPER_KEY_LENGTH] @@ -73,9 +69,6 @@ def update_config(self, conn: SecureConnection) -> bool: self.tx_key = tx_key.hex()[:self.LOWER_KEY_LENGTH] self.rx_key = rx_key.hex()[:self.LOWER_KEY_LENGTH] - self.tx_port = tx_port - self.rx_port = rx_port - return True diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/scripts/run_simulation.sh b/modules/sc-mesh-secure-deployment/src/nats/cbma/scripts/run_simulation.sh index 66cf0c22..20f90ff1 100755 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/scripts/run_simulation.sh +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/scripts/run_simulation.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash NUM_NODES=2 @@ -7,7 +7,7 @@ KEYPAIR_TYPE="rsa" # Can be ecdsa, eddsa, or rsa DEFAULT_LOG_LEVEL="INFO" -BASE_MTU="1500" +CONSTANTS_RC="mess/constants.rc" CBMA_DEBUG=0 BAT_DEBUG=0 @@ -26,6 +26,7 @@ LOG_LEVEL="${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" NODES=$(awk -vA=$(printf "%d" \'A) -vc=$NUM_NODES 'BEGIN{while(n/dev/null 2>&1; then printf "\n[!] FATAL: '$t' is missing!\n" exit 1 @@ -84,13 +85,29 @@ wait_until_interface_ready() { } +get_base_mtu_from_constants_rc() ( + DIR="${1:-.}" + + . "${DIR}/${CONSTANTS_RC}" + echo $HOPEFULLY1500 +) + + +get_mtu_overhead_from_constants_rc() ( + DIR="${1:-.}" + + . "${DIR}/${CONSTANTS_RC}" + echo $((MACSEC_OVERHEAD + BATMAN_OVERHEAD)) +) + + setup_wlan() { I="$1" WLAN="$2" TOTAL_NUM_INTERFACES=$((TOTAL_NUM_INTERFACES + 1)) - PHY="$(echo /sys/class/ieee80211/*/device/net/$WLAN | cut -d / -f 5)" + read PHY < "/sys/class/net/$WLAN/phy80211/name" iw dev "$WLAN" del 2>/dev/null iw phy "$PHY" interface add "$WLAN" type mesh @@ -104,7 +121,8 @@ setup_wlan() { # ip netns exec "$I" ip link set dev "$WLAN" name "wlp1s${I}" # ip netns exec "$I" iw dev "wlp1s${I}" set type mesh - ip netns exec "$I" ip link set dev "wlp1s${I}" mtu $((BASE_MTU + 80)) + ip netns exec "$I" ip link set dev "wlp1s${I}" mtu $((BASE_MTU + MTU_OVERHEAD + \ + MTU_OVERHEAD * START_UPPER_BATMAN)) ip netns exec "$I" ip link set dev "wlp1s${I}" address "00:20:91:0${I}:0${I}:0${I}" ip netns exec "$I" ip link set dev "wlp1s${I}" up @@ -124,8 +142,8 @@ setup_eth() { ip link add "$ETH" type veth peer name "eth${I}" netns "$I" - # NOTE - No need to set MTU for now - ip netns exec "$I" ip link set dev "eth${I}" mtu $((BASE_MTU + 108)) + ip netns exec "$I" ip link set dev "eth${I}" mtu $((BASE_MTU + MTU_OVERHEAD + \ + MTU_OVERHEAD * START_UPPER_BATMAN)) ip netns exec "$I" ip link set dev "eth${I}" address "00:20:91:${I}0:${I}0:${I}0" ip link set "$ETH" up @@ -145,12 +163,14 @@ setup_bat() { ip netns exec "$I" ip link add "$BAT" type batadv if [ -n "$IFACE" ]; then - MAC="$(ip netns exec "$I" cat "/sys/class/net/$IFACE/address")" + MAC="$(a="$(ip netns exec "$I" cat "/sys/class/net/$IFACE/address")" && \ + printf "%02x${a#??}\n" $(( 0x${a%%:*} ^ 0x2 )))" ip netns exec "$I" ip link set dev "$BAT" address "$MAC" fi - # NOTE - No need to set MTU for now - # ip netns exec "$I" ip link set dev "eth${I}" mtu $((BASE_MTU + 108)) + if [ $START_UPPER_BATMAN -eq 1 -a "$BAT" = "bat0" ]; then + ip netns exec "$I" ip link set dev "$BAT" mtu $(( BASE_MTU + MTU_OVERHEAD )) 2>/dev/null + fi ip netns exec "$I" ip link set "$BAT" up @@ -178,8 +198,12 @@ setup_nodes() { setup_wlan "$I" "wlan${N}" setup_eth "$I" "veth${N}" + setup_bat "$I" "bat0" "wlp1s${I}" - setup_bat "$I" "bat1" + [ $START_UPPER_BATMAN -eq 0 ] || ( + MTU_OVERHEAD=0 + setup_bat "$I" "bat1" + ) N=$((N + 1)) done @@ -282,7 +306,12 @@ launch_upper_cbma() { wait_for_batman_neighbors() { BAT_IFACE="$1" - bat_neighbors=$(( TOTAL_NUM_INTERFACES / NUM_NODES )) + + if [ "$BAT_IFACE" = "bat0" ]; then + bat_neighbors=$(( TOTAL_NUM_INTERFACES - TOTAL_NUM_INTERFACES / NUM_NODES )) + else + bat_neighbors=$(( NUM_NODES - 1 )) + fi for I in $NODES; do printf "[+] Waiting for ${BAT_IFACE} neighbors in CBMA node ${I}... ${CBMA_DEBUG:+\n}${BAT_DEBUG:+\n}" @@ -333,11 +362,16 @@ check_dependencies set +e DIR="$(dirname $0)" + BASE_MTU="$(get_base_mtu_from_constants_rc "$DIR")" + MTU_OVERHEAD="$(get_mtu_overhead_from_constants_rc "$DIR")" trap interrupt_handler INT EXIT QUIT KILL check_max_nest_dev + . "${0%/*}/${CONSTANTS_RC}" + MTU_OVERHEAD=$((MACSEC_OVERHEAD + BATMAN_OVERHEAD)) + setup_nodes generate_certificates "$DIR" diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/client.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/client.py index 1e226226..61777fad 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/client.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/client.py @@ -41,7 +41,7 @@ def __init__(self, self.server_port = server_port self.macsec_callback = macsec_callback - super().__init__(certificates, SSL.TLSv1_2_METHOD) + super().__init__(certificates, SSL.SSLv23_METHOD) def __create_socket_connection_object(self) -> FileBasedSecureConnection: diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/secure_context.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/secure_context.py index 8d9ddd6e..86ff3983 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/secure_context.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/secure_context.py @@ -13,7 +13,7 @@ class FileBasedSecureContext(SecureContextInterface): CTX_OPTIONS: int = SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3 | SSL.OP_NO_TLSv1 | \ - SSL.OP_NO_TLSv1_1 | SSL.OP_NO_TLSv1_2 + SSL.OP_NO_TLSv1_1 SSL_SESSION_TIMEOUT: int = 60 # seconds def __init__(self, diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/server.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/server.py index 376dd0c8..0e34510c 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/server.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/secure_socket/server.py @@ -32,7 +32,7 @@ def __init__(self, self.client_ipv6 = client_ipv6 self.macsec_callback = macsec_callback - super().__init__(certificates, SSL.TLSv1_2_METHOD) + super().__init__(certificates, SSL.SSLv23_METHOD) def __create_socket_connection_object(self) -> FileBasedSecureConnection: diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/standalone.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/standalone.py index a5ad6250..68e69d09 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/standalone.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/standalone.py @@ -130,16 +130,6 @@ def set_interface_mtu(interface: str, mtu: int) -> bool: is_upper = args.upper or any('bat' in i and glob(f"/sys/class/net/*/upper_{i}") for i in interfaces) - mtu_base = get_mtu_from_constants_rc(exclude=['OVERHEAD']) - mtu_overhead = get_mtu_from_constants_rc(exclude=['HOPEFULLY']) - if not is_upper: - mtu_overhead *= 2 - mtu = mtu_base + mtu_overhead - - for i in interfaces: - if not set_interface_mtu(i, mtu): - sys.exit(255) - enable_macsec_encryption = is_upper try: controller = CBMAController(args.port, @@ -151,9 +141,23 @@ def set_interface_mtu(interface: str, mtu: int) -> bool: logger.error(f"Exception when creating the CBMAController: {e}") sys.exit(255) + mtu_base = get_mtu_from_constants_rc(exclude=['OVERHEAD']) + mtu_overhead = get_mtu_from_constants_rc(exclude=['HOPEFULLY']) + mtu_batman = mtu_base + + if not is_upper: + mtu_batman += mtu_overhead + mtu_overhead *= 2 + + mtu = mtu_base + mtu_overhead + for i in interfaces: + if not set_interface_mtu(i, mtu): + sys.exit(255) + if not (existing_batman := f"/sys/class/net/{args.batman}" in glob("/sys/class/net/*")): mac = get_interface_locally_administered_mac(interfaces[0]) create_batman(args.batman, mac) + set_interface_mtu(args.batman, mtu_batman) try: logger.info(f"Adding {interfaces} to the CBMAController") for iface in interfaces: diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/utils/macsec.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/utils/macsec.py index 621a2de2..3b905bf2 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/utils/macsec.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/utils/macsec.py @@ -10,16 +10,18 @@ from . import logging +MACSEC_CONFIG_VERSION = 1 + BYTES_LENGTH = 128 -MAX_MACSEC_PORT = 2 ** 16 MAX_RETRIES = 5 MIN_WAIT_TIME = 0.1 # seconds MAX_WAIT_TIME = 1 # seconds -_macsec_ports: list[int] = [] +macsec_struct_format = '!{}sH'.format(BYTES_LENGTH * 2) +macsec_struct_v1 = Struct(macsec_struct_format) +# TODO - Add future structs here: macsec_struct_v2 = Struct(macsec_struct_format + 's16') -macsec_struct = Struct('!{}sH'.format(BYTES_LENGTH * 2)) logger = logging.get_logger() @@ -38,23 +40,6 @@ def xor_bytes(local_key: bytes, peer_key: bytes) -> bytes: return result -def get_macsec_port() -> int: - # TODO - Check ports in used with ip macsec - port = random.randint(1, MAX_MACSEC_PORT) - - _macsec_ports.append(port) - return port - - -def free_macsec_port(port: int) -> bool: - try: - _macsec_ports.remove(port) - return True - except ValueError: - logger.error(f"MACsec port {port} wasn't reserved") - return False - - def send_macsec_config(conn: SecureConnection, my_config: bytes) -> int: peer_ipv6 = conn.get_peer_name()[0] logger.debug(f"Sending MACsec configuration to {peer_ipv6}") @@ -100,29 +85,41 @@ def exchange_macsec_config(conn: SecureConnection, my_config: bytes) -> bytes: return peer_config -def get_macsec_config(conn: SecureConnection) -> Tuple[Tuple[bytes, bytes], Tuple[int, int]]: +def get_macsec_config(conn: SecureConnection) -> Tuple[bytes, bytes]: peer_ipv6 = conn.get_peer_name()[0] logger.debug(f"Generating MACsec configuration for {peer_ipv6}") my_tx_key_bytes = generate_random_bytes() my_rx_key_bytes = generate_random_bytes() - rx_port = get_macsec_port() + my_version = MACSEC_CONFIG_VERSION - my_packed_config = macsec_struct.pack(my_tx_key_bytes + my_rx_key_bytes, rx_port) + my_packed_config = macsec_struct_v1.pack(my_tx_key_bytes + my_rx_key_bytes, my_version) peer_packed_config = exchange_macsec_config(conn, my_packed_config) - peer_config = macsec_struct.unpack(peer_packed_config) + peer_packed_config_size = len(peer_packed_config) + if peer_packed_config_size < macsec_struct_v1.size: + raise ValueError(f"Size of received data from {peer_ipv6} is lower than minimum expected: {peer_packed_config_size} < {macsec_struct_v1.size}") + + peer_config = macsec_struct_v1.unpack(peer_packed_config[:macsec_struct_v1.size]) peer_rx_key_bytes = peer_config[0][:BYTES_LENGTH] peer_tx_key_bytes = peer_config[0][BYTES_LENGTH:] - tx_port = peer_config[1] + peer_version = peer_config[1] + + config_version = min(my_version, max(peer_version, 1)) + if my_version != peer_version: + # NOTE - Prior to version v1 the version field was a never-used MACsec random port + # TODO - This is temporarily here to be used in future newer config versions + logger.debug(f"Ignore -> Version mismatch in MACsec configurations => our: {my_version} - theirs: {peer_version} - using: {config_version}") + + # TODO - Add here extraction of future configs fields, for example: + # if config_version == 2: + # peer_config = macsec_struct_v2.unpack(peer_packed_config[:macsec_struct_v2.size]) + # peer_cipher = peer_config[2] tx_key = xor_bytes(my_tx_key_bytes, peer_tx_key_bytes) rx_key = xor_bytes(my_rx_key_bytes, peer_rx_key_bytes) - keys = (tx_key, rx_key) - ports = (rx_port, tx_port) - logger.debug(f"MACsec configuration for {peer_ipv6} generated successfully") - return (keys, ports) + return tx_key, rx_key