#!/usr/bin/env bash
# Create transparent bridge between two interfaces
# Usage: setup_bridge.sh <if1> <if2>
# No IP assigned — invisible on network
#
# This script creates a Layer 2 bridge for inline packet capture and
# 802.1X bypass.  The bridge has NO IP address, making it undetectable
# via network scanning.  STP/BPDU/CDP/LLDP frames are suppressed via
# ebtables to avoid triggering network monitoring systems.

set -euo pipefail

BRIDGE="br0"

# --------------------------------------------------------------------------
# Validate arguments
# --------------------------------------------------------------------------
if [[ $# -ne 2 ]]; then
    echo "Usage: $0 <interface1> <interface2>" >&2
    exit 1
fi

IF1="$1"
IF2="$2"

# Verify interfaces exist
for iface in "$IF1" "$IF2"; do
    if ! ip link show "$iface" &>/dev/null; then
        echo "Error: interface $iface does not exist" >&2
        exit 1
    fi
done

# --------------------------------------------------------------------------
# Cleanup any existing bridge
# --------------------------------------------------------------------------
if ip link show "$BRIDGE" &>/dev/null; then
    echo "Removing existing bridge $BRIDGE"
    ip link set down dev "$BRIDGE" 2>/dev/null || true
    ip link del "$BRIDGE" 2>/dev/null || true
fi

# --------------------------------------------------------------------------
# Create bridge
# --------------------------------------------------------------------------
echo "Creating bridge $BRIDGE: $IF1 <-> $IF2"

# Create the bridge device
ip link add name "$BRIDGE" type bridge

# Disable Spanning Tree Protocol
ip link set "$BRIDGE" type bridge stp_state 0

# Zero forwarding delay — start forwarding immediately
ip link set "$BRIDGE" type bridge forward_delay 0

# Disable ageing to keep all MACs in forwarding table
ip link set "$BRIDGE" type bridge ageing_time 0

# --------------------------------------------------------------------------
# Configure interfaces
# --------------------------------------------------------------------------

# Flush any existing IP addresses — bridge must have no L3 presence
ip addr flush dev "$IF1" 2>/dev/null || true
ip addr flush dev "$IF2" 2>/dev/null || true

# Set promiscuous mode — capture all frames
ip link set "$IF1" promisc on
ip link set "$IF2" promisc on

# Disable multicast snooping (avoid IGMP interference)
echo 0 > /sys/devices/virtual/net/"$BRIDGE"/bridge/multicast_snooping 2>/dev/null || true

# Add interfaces to bridge
ip link set "$IF1" master "$BRIDGE"
ip link set "$IF2" master "$BRIDGE"

# Bring everything up
ip link set up dev "$IF1"
ip link set up dev "$IF2"
ip link set up dev "$BRIDGE"

# Ensure bridge has NO IP address
ip addr flush dev "$BRIDGE" 2>/dev/null || true

# --------------------------------------------------------------------------
# ebtables rules — suppress discovery protocols
# --------------------------------------------------------------------------

# Check if ebtables is available
if command -v ebtables &>/dev/null; then
    echo "Applying ebtables suppression rules"

    # Flush existing rules
    ebtables -F 2>/dev/null || true

    # STP/BPDU — 01:80:C2:00:00:00
    ebtables -A FORWARD -d 01:80:C2:00:00:00 -j DROP
    ebtables -A OUTPUT  -d 01:80:C2:00:00:00 -j DROP

    # CDP/VTP — 01:00:0C:CC:CC:CC
    ebtables -A FORWARD -d 01:00:0C:CC:CC:CC -j DROP
    ebtables -A OUTPUT  -d 01:00:0C:CC:CC:CC -j DROP

    # LLDP — 01:80:C2:00:00:0E
    ebtables -A FORWARD -d 01:80:C2:00:00:0E -j DROP
    ebtables -A OUTPUT  -d 01:80:C2:00:00:0E -j DROP

    # Cisco PVST+ — 01:00:0C:CC:CC:CD
    ebtables -A FORWARD -d 01:00:0C:CC:CC:CD -j DROP
    ebtables -A OUTPUT  -d 01:00:0C:CC:CC:CD -j DROP

    # LLDP alternate — 01:80:C2:00:00:03
    ebtables -A FORWARD -d 01:80:C2:00:00:03 -j DROP
    ebtables -A OUTPUT  -d 01:80:C2:00:00:03 -j DROP

    # 802.1X EAP passthrough — allow EAP frames across the bridge
    # (default FORWARD policy is ACCEPT, so EAP frames pass unless
    # we explicitly blocked them above — we only block management protos)
else
    echo "Warning: ebtables not installed — protocol suppression skipped" >&2
fi

# --------------------------------------------------------------------------
# Disable IPv6 on bridge (prevent RA/NS/NA leakage)
# --------------------------------------------------------------------------
sysctl -qw "net.ipv6.conf.${BRIDGE}.disable_ipv6=1" 2>/dev/null || true
sysctl -qw "net.ipv6.conf.${IF1}.disable_ipv6=1" 2>/dev/null || true
sysctl -qw "net.ipv6.conf.${IF2}.disable_ipv6=1" 2>/dev/null || true

# --------------------------------------------------------------------------
# Verify bridge is active
# --------------------------------------------------------------------------
if ip link show "$BRIDGE" | grep -q "state UP"; then
    echo "Bridge $BRIDGE is UP: $IF1 <-> $IF2 (no IP, STP disabled)"
    bridge link show
    exit 0
else
    echo "Error: bridge $BRIDGE failed to come up" >&2
    exit 1
fi
