Last active
January 1, 2026 23:05
-
-
Save GodSpoon/51de6d98181a4060ebf664e113e37700 to your computer and use it in GitHub Desktop.
diet-bridge setup script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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