Skip to content

Instantly share code, notes, and snippets.

@GodSpoon
Last active January 1, 2026 23:05
Show Gist options
  • Select an option

  • Save GodSpoon/51de6d98181a4060ebf664e113e37700 to your computer and use it in GitHub Desktop.

Select an option

Save GodSpoon/51de6d98181a4060ebf664e113e37700 to your computer and use it in GitHub Desktop.
diet-bridge setup script
#!/bin/bash
#
# Proxy ARP Bridge Setup - Production Version
# Handles USB hotplug, WiFi reconnects, IP changes, etc.
#
set -e
LOGFILE="/var/log/bridge-setup.log"
WIFI_INTERFACE="wlan0"
ETH_INTERFACE="eth0"
WPA_CONF="/etc/wpa_supplicant/wpa_supplicant.conf"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}
log "=== Robust Proxy ARP Bridge Setup Started ==="
# Detect interfaces with improved validation
if ! ip link show "$WIFI_INTERFACE" &>/dev/null; then
WIFI_INTERFACE=$(ip link show | grep -E "wlan[0-9]|wlp" | cut -d: -f2 | tr -d ' ' | head -n1)
log "Auto-detected WiFi interface: $WIFI_INTERFACE"
fi
if ! ip link show "$ETH_INTERFACE" &>/dev/null; then
ETH_INTERFACE=$(ip link show | grep -E "usb[0-9]|eth[0-9]|enp" | grep -v "$WIFI_INTERFACE" | cut -d: -f2 | tr -d ' ' | head -n1)
log "Auto-detected Ethernet interface: $ETH_INTERFACE"
fi
# Validate interfaces
if [[ -z "$WIFI_INTERFACE" ]] || [[ -z "$ETH_INTERFACE" ]]; then
log "ERROR: Could not detect required network interfaces"
log "Available interfaces:"
ip link show | grep -E "^[0-9]+" | tee -a "$LOGFILE"
exit 1
fi
if [[ "$WIFI_INTERFACE" == "$ETH_INTERFACE" ]]; then
log "ERROR: WiFi and Ethernet interfaces are the same ($WIFI_INTERFACE)"
exit 1
fi
log "Using WiFi: $WIFI_INTERFACE, Ethernet: $ETH_INTERFACE"
# Install required packages
log "Installing required packages..."
apt-get update -qq
apt-get install -y parprouted dhcp-helper avahi-daemon >> "$LOGFILE" 2>&1
# Create bridge manager script
log "Creating bridge manager script..."
cat > /usr/local/sbin/bridge-manager << 'BRIDGE_MANAGER_EOF'
#!/bin/bash
# Proxy ARP Bridge Manager
# Manages parprouted and interface configuration
WIFI_IFACE="${WIFI_IFACE:-wlan0}"
ETH_IFACE="${ETH_IFACE:-eth0}"
LOGFILE="/var/log/bridge-manager.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}
# Auto-detect interfaces if not set
if ! ip link show "$WIFI_IFACE" &>/dev/null; then
WIFI_IFACE=$(ip link show | grep -E "wlan[0-9]" | cut -d: -f2 | tr -d ' ' | head -n1)
fi
if ! ip link show "$ETH_IFACE" &>/dev/null; then
ETH_IFACE=$(ip link show | grep -E "usb[0-9]|eth[0-9]" | grep -v "$WIFI_IFACE" | cut -d: -f2 | tr -d ' ' | head -n1)
fi
start_bridge() {
log "Starting bridge: $ETH_IFACE <-> $WIFI_IFACE"
# Wait for WiFi to have an IP (up to 30 seconds)
for i in {1..30}; do
WLAN_IP=$(ip -o -4 addr show "$WIFI_IFACE" 2>/dev/null | awk '{print $4}' | cut -d'/' -f1 | head -n1)
if [[ -n "$WLAN_IP" ]] && [[ "$WLAN_IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
break
fi
sleep 1
done
if [[ -z "$WLAN_IP" ]]; then
log "WARNING: WiFi has no IP address after 30 seconds"
log "Bridge will start when WiFi gets an IP (via ifupdown hook)"
return 0
fi
log "WiFi IP: $WLAN_IP"
# Wait for eth interface to appear (up to 20 seconds)
ETH_FOUND=0
for i in {1..20}; do
if ip link show "$ETH_IFACE" &>/dev/null; then
ETH_FOUND=1
log "$ETH_IFACE found"
break
fi
if [[ $i -eq 1 ]]; then
log "Waiting for $ETH_IFACE to appear..."
fi
sleep 1
done
if [[ $ETH_FOUND -eq 0 ]]; then
log "WARNING: $ETH_IFACE not found after 20 seconds"
log "Bridge will start when $ETH_IFACE appears (via udev hotplug)"
return 0
fi
# Bring up eth interface if not up
if ! ip link show "$ETH_IFACE" 2>/dev/null | grep -q "state UP"; then
log "Bringing up $ETH_IFACE..."
ip link set "$ETH_IFACE" up 2>/dev/null || true
sleep 2
fi
# Remove old IP if exists
ip addr flush dev "$ETH_IFACE" 2>/dev/null || true
# Clone WiFi IP to eth with /32
log "Cloning IP to $ETH_IFACE..."
ip addr add "${WLAN_IP}/32" dev "$ETH_IFACE" 2>/dev/null || true
# Kill any existing parprouted
killall parprouted 2>/dev/null || true
sleep 1
# Start parprouted
log "Starting parprouted..."
/usr/sbin/parprouted "$ETH_IFACE" "$WIFI_IFACE" &
PARPROUTED_PID=$!
log "Bridge started (parprouted PID: $PARPROUTED_PID)"
}
stop_bridge() {
log "Stopping bridge..."
killall parprouted 2>/dev/null || true
ip addr flush dev "$ETH_IFACE" 2>/dev/null || true
log "Bridge stopped"
}
restart_bridge() {
stop_bridge
sleep 2
start_bridge
}
status_bridge() {
echo "=== Proxy ARP Bridge Status ==="
echo ""
echo "Interfaces:"
ip -br addr show | grep -E "$WIFI_IFACE|$ETH_IFACE"
echo ""
echo "Parprouted: $(pgrep -f parprouted >/dev/null && echo 'Running (PID: '$(pgrep parprouted)')' || echo 'Not running')"
echo ""
echo "Active Routes (/32):"
ip route show | grep "/32" | head -n 10
echo ""
}
case "$1" in
start)
start_bridge
;;
stop)
stop_bridge
;;
restart)
restart_bridge
;;
status)
status_bridge
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
BRIDGE_MANAGER_EOF
chmod +x /usr/local/sbin/bridge-manager
# Replace interface names in the script
sed -i "s/WIFI_IFACE:-wlan0/WIFI_IFACE:-$WIFI_INTERFACE/" /usr/local/sbin/bridge-manager
sed -i "s/ETH_IFACE:-eth0/ETH_IFACE:-$ETH_INTERFACE/" /usr/local/sbin/bridge-manager
# Create systemd service
log "Creating systemd service..."
cat > /etc/systemd/system/proxy-arp-bridge.service << EOF
[Unit]
Description=Proxy ARP Bridge Service
After=network-online.target wpa_supplicant.service
Wants=network-online.target
# Use Wants instead of BindsTo to avoid hard dependency on eth device
Wants=sys-subsystem-net-devices-${ETH_INTERFACE}.device
After=sys-subsystem-net-devices-${ETH_INTERFACE}.device
[Service]
Type=simple
RemainAfterExit=yes
ExecStart=/usr/local/sbin/bridge-manager start
ExecStop=/usr/local/sbin/bridge-manager stop
# Don't restart on failure if eth0 doesn't exist - it will be started by udev when plugged
Restart=on-failure
RestartSec=10
# Allow service to start even if eth device doesn't exist yet
# The bridge-manager script will handle waiting for it
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Create udev rule for USB hotplug
log "Creating udev rule for USB hotplug..."
cat > /etc/udev/rules.d/90-bridge-hotplug.rules << EOF
# Restart bridge when USB Ethernet adapter is plugged in
ACTION=="add", SUBSYSTEM=="net", KERNEL=="$ETH_INTERFACE", RUN+="/bin/systemctl restart proxy-arp-bridge.service"
EOF
# Reload udev rules
udevadm control --reload-rules
# Create ifupdown hook for WiFi IP changes
log "Creating ifupdown hook for WiFi reconnects..."
mkdir -p /etc/network/if-up.d
cat > /etc/network/if-up.d/bridge-sync << 'EOF'
#!/bin/bash
# Restart bridge when WiFi comes up or gets new IP
WIFI_IFACE="$WIFI_INTERFACE"
if [[ "\$IFACE" == "\$WIFI_IFACE" ]]; then
# Wait for IP to be fully configured (poll up to 10 seconds)
for i in {1..10}; do
IP=\$(ip -o -4 addr show "\$WIFI_IFACE" 2>/dev/null | awk '{print \$4}' | cut -d'/' -f1)
if [[ -n "\$IP" ]] && [[ "\$IP" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\$ ]]; then
break
fi
sleep 1
done
# Only restart if we actually have an IP
if [[ -n "\$IP" ]]; then
/bin/systemctl restart proxy-arp-bridge.service
fi
fi
EOF
# Replace the placeholder with actual interface name
sed -i "s/WIFI_INTERFACE/$WIFI_INTERFACE/" /etc/network/if-up.d/bridge-sync
chmod +x /etc/network/if-up.d/bridge-sync
# Create simple network interfaces config
log "Configuring network interfaces..."
cat > /etc/network/interfaces << EOF
# Loopback interface
auto lo
iface lo inet loopback
# Ethernet interface - managed by bridge service
# Only use allow-hotplug to prevent boot hangs if device is missing
allow-hotplug $ETH_INTERFACE
iface $ETH_INTERFACE inet manual
# WiFi interface
auto $WIFI_INTERFACE
allow-hotplug $WIFI_INTERFACE
iface $WIFI_INTERFACE inet dhcp
wpa-conf $WPA_CONF
EOF
# Enable IP forwarding
log "Enabling IP forwarding..."
cat > /etc/sysctl.d/local.conf << EOF
net.ipv4.ip_forward=1
EOF
sysctl -w net.ipv4.ip_forward=1 >/dev/null
# Configure DHCP helper
log "Configuring DHCP relay..."
cat > /etc/default/dhcp-helper << EOF
DHCPHELPER_OPTS="-b $WIFI_INTERFACE"
EOF
# Enable mDNS reflection
log "Configuring mDNS reflection..."
if grep -q "^\[reflector\]" /etc/avahi/avahi-daemon.conf; then
sed -i '/^\[reflector\]/,/^\[/ s/^#*enable-reflector=.*/enable-reflector=yes/' /etc/avahi/avahi-daemon.conf
else
cat >> /etc/avahi/avahi-daemon.conf << EOF
[reflector]
enable-reflector=yes
EOF
fi
# Enable services
log "Enabling services..."
systemctl daemon-reload
systemctl enable proxy-arp-bridge.service
systemctl enable dhcp-helper 2>/dev/null || true
systemctl enable avahi-daemon 2>/dev/null || true
# Start the bridge now if WiFi is up
if ip addr show "$WIFI_INTERFACE" 2>/dev/null | grep -q "inet "; then
log "WiFi is up, starting bridge now..."
/usr/local/sbin/bridge-manager start
systemctl restart dhcp-helper
systemctl restart avahi-daemon 2>/dev/null || true
# Validate services started successfully
sleep 3
log "Validating service status..."
if systemctl is-active --quiet dhcp-helper; then
log "✓ dhcp-helper is running"
else
log "⚠ WARNING: dhcp-helper failed to start"
log " Check: journalctl -u dhcp-helper -n 20"
fi
if systemctl is-active --quiet avahi-daemon; then
log "✓ avahi-daemon is running"
else
log "⚠ WARNING: avahi-daemon failed to start"
log " Check: journalctl -u avahi-daemon -n 20"
fi
if pgrep -x parprouted >/dev/null; then
log "✓ parprouted is running (PID: $(pgrep -x parprouted))"
else
log "⚠ WARNING: parprouted is not running"
log " Check: /var/log/bridge-manager.log"
fi
else
log "WiFi is not up yet. Bridge will start automatically when WiFi connects."
fi
# Create management scripts
log "Creating management scripts..."
cat > /usr/local/bin/bridge-status << 'EOF'
#!/bin/bash
/usr/local/sbin/bridge-manager status
echo "Services:"
echo " proxy-arp-bridge: $(systemctl is-active proxy-arp-bridge)"
echo " dhcp-helper: $(systemctl is-active dhcp-helper)"
echo " avahi-daemon: $(systemctl is-active avahi-daemon)"
EOF
chmod +x /usr/local/bin/bridge-status
cat > /usr/local/bin/bridge-restart << 'EOF'
#!/bin/bash
systemctl restart proxy-arp-bridge
systemctl restart dhcp-helper
echo "Bridge restarted"
/usr/local/sbin/bridge-manager status
EOF
chmod +x /usr/local/bin/bridge-restart
# Install and configure NetBird
NETBIRD_SETUP_KEY_FILE="/var/tmp/netbird_setup_key"
NETBIRD_HOSTNAME="diet-bridge"
if [[ -f "$NETBIRD_SETUP_KEY_FILE" ]]; then
log "=== Installing NetBird ==="
log "Setup key file found, proceeding with NetBird installation..."
# Read the setup key
NETBIRD_SETUP_KEY=$(cat "$NETBIRD_SETUP_KEY_FILE" | tr -d '[:space:]')
if [[ -z "$NETBIRD_SETUP_KEY" ]]; then
log "⚠ WARNING: NetBird setup key file is empty, skipping installation"
else
# Download and install NetBird
log "Downloading NetBird installer..."
if curl -fsSL https://pkgs.netbird.io/install.sh -o /tmp/netbird-install.sh; then
log "Running NetBird installer..."
bash /tmp/netbird-install.sh >> "$LOGFILE" 2>&1
if command -v netbird >/dev/null 2>&1; then
log "✓ NetBird installed successfully"
# Connect to NetBird network
log "Connecting to NetBird network..."
netbird up --setup-key "$NETBIRD_SETUP_KEY" \
--hostname "$NETBIRD_HOSTNAME" \
--allow-server-ssh \
--enable-ssh-root >> "$LOGFILE" 2>&1
if netbird status >/dev/null 2>&1; then
log "✓ NetBird connected successfully"
netbird status | tee -a "$LOGFILE"
else
log "⚠ WARNING: NetBird connection failed"
log " Check: netbird status"
log " Check: journalctl -u netbird -n 20"
fi
# Clean up installer
rm -f /tmp/netbird-install.sh
else
log "⚠ WARNING: NetBird installation failed"
log " Check: $LOGFILE"
fi
else
log "⚠ WARNING: Failed to download NetBird installer"
fi
fi
else
log "NetBird setup key file not found at $NETBIRD_SETUP_KEY_FILE, skipping NetBird installation"
fi
log "=== Setup Complete ==="
log "Configuration:"
log " WiFi Interface: $WIFI_INTERFACE"
log " Ethernet Interface: $ETH_INTERFACE"
log " Hostname: $NETBIRD_HOSTNAME"
log " Mode: Proxy ARP (Layer 3) with systemd management"
log ""
log "The bridge is managed by systemd and will:"
log " - Start automatically on boot"
log " - Restart when USB Ethernet is reconnected"
log " - Restart when WiFi reconnects or IP changes"
log " - Auto-recover from failures"
log ""
log "Management commands:"
log " bridge-status - Check bridge operation"
log " bridge-restart - Manually restart bridge"
log " netbird status - Check NetBird VPN connection (if installed)"
log ""
log "Logs:"
log " Setup: $LOGFILE"
log " Bridge: /var/log/bridge-manager.log"
log " System: journalctl -u proxy-arp-bridge"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment