Skip to content

Commit

Permalink
Merge pull request #465 from tiiuae/upgrade-cbma-to-v0.1.8
Browse files Browse the repository at this point in the history
  • Loading branch information
TIISR authored Jul 7, 2024
2 parents 74c76dc + 0627b7f commit 3d1667a
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 66 deletions.
8 changes: 4 additions & 4 deletions modules/sc-mesh-secure-deployment/src/nats/cbma/VERSION
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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]
Expand All @@ -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


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash -x
#!/bin/bash


NUM_NODES=2
Expand All @@ -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
Expand All @@ -26,6 +26,7 @@ LOG_LEVEL="${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}"
NODES=$(awk -vA=$(printf "%d" \'A) -vc=$NUM_NODES 'BEGIN{while(n<c)printf "%c\n",A + n++}')
TOTAL_NUM_INTERFACES=0


check_dependencies() {
echo -n "[+] Checking dependencies... "

Expand All @@ -34,7 +35,7 @@ check_dependencies() {
exit 1
fi

for t in brctl ebtables batctl faketime; do
for t in ebtables batctl faketime ip iw sed grep awk modprobe killall; do
if ! type $t >/dev/null 2>&1; then
printf "\n[!] FATAL: '$t' is missing!\n"
exit 1
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand 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
Expand Down Expand Up @@ -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}"
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
24 changes: 14 additions & 10 deletions modules/sc-mesh-secure-deployment/src/nats/cbma/standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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:
Expand Down
55 changes: 26 additions & 29 deletions modules/sc-mesh-secure-deployment/src/nats/cbma/utils/macsec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand All @@ -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}")
Expand Down Expand Up @@ -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

0 comments on commit 3d1667a

Please sign in to comment.