fix(eye-remote): Resolve rp_filter and dual-interface routing issues

Root causes identified and fixed:
- "martian source" packet drops due to rp_filter=1 on USB interfaces
- Dual interface conflict: usb0 (RNDIS) and usb1 (CDC-ECM) both getting
  same IP 10.55.0.2, causing routing loops on Pi Zero

MOCHAbin fixes:
- Add sysctl.d/99-secubox-usb.conf to disable rp_filter globally
- Update udev rules to disable rp_filter per-interface on add
- Add bind/change events for USB re-plug detection
- Update secubox-eye-network.sh with find_interface() and rp_filter disable

Pi Zero fixes:
- Add usb1-disable config to install_zerow.sh to prevent routing conflict

Tested: Ping/TCP connectivity restored, metrics flowing via API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-05-05 11:22:31 +02:00
parent a444d9046c
commit 01f2bf119d
5 changed files with 121 additions and 25 deletions

View File

@ -1,5 +1,6 @@
udev/90-secubox-eye-remote.rules etc/udev/rules.d/
scripts/secubox-eye-network.sh usr/local/sbin/
sysctl.d/99-secubox-usb.conf etc/sysctl.d/
systemd/secubox-eye-remote.service etc/systemd/system/
api/main.py usr/share/secubox/eye-remote/api/
www/* var/www/secubox/eye-remote/

View File

@ -1,53 +1,108 @@
#!/bin/bash
# ==============================================================================
# SecuBox Eye Remote - Host Network Configuration Script
# Called by udev when Pi Zero USB gadget is connected/disconnected
# Called by udev/systemd when Pi Zero USB gadget is connected/disconnected
#
# Usage: secubox-eye-network.sh up|down
# Usage: secubox-eye-network.sh up|down|status
#
# CyberMind - https://cybermind.fr
# ==============================================================================
set -euo pipefail
# Find the usb0/usb1 interface created by gadget
INTERFACE="${INTERFACE:-usb0}"
# Find usb interface - prefer usb0, fallback to finding by MAC
find_interface() {
# Check if INTERFACE is set (from udev)
if [[ -n "${INTERFACE:-}" ]] && ip link show "$INTERFACE" &>/dev/null; then
echo "$INTERFACE"
return 0
fi
# Look for interface with Pi Zero MAC prefix (02:fb:*)
for iface in /sys/class/net/usb*; do
if [[ -f "$iface/address" ]]; then
mac=$(cat "$iface/address")
if [[ "$mac" == 02:fb:* ]]; then
basename "$iface"
return 0
fi
fi
done
# Fallback to usb0
if ip link show usb0 &>/dev/null; then
echo "usb0"
return 0
fi
return 1
}
HOST_IP="10.55.0.1"
NETMASK="24"
PEER_IP="10.55.0.2"
LOG_TAG="secubox-eye-network"
log() {
logger -t "$LOG_TAG" "$*"
echo "$*"
}
case "${1:-}" in
up)
log "Configuring $INTERFACE (Host: $HOST_IP/$NETMASK)"
IFACE=$(find_interface)
if [[ -z "$IFACE" ]]; then
log "ERROR: No Eye Remote interface found"
exit 1
fi
log "Configuring $IFACE (Host: $HOST_IP/$NETMASK)"
# CRITICAL: Disable reverse path filter to prevent martian source drops
sysctl -w net.ipv4.conf.$IFACE.rp_filter=0 2>/dev/null || true
sysctl -w net.ipv4.conf.all.rp_filter=0 2>/dev/null || true
# Wait for interface to be ready
for i in {1..10}; do
if ip link show "$INTERFACE" &>/dev/null; then
for i in {1..20}; do
if ip link show "$IFACE" &>/dev/null; then
break
fi
sleep 0.5
done
# Configure IP
ip link set "$INTERFACE" up
ip addr add "$HOST_IP/$NETMASK" dev "$INTERFACE" 2>/dev/null || true
# Bring interface up
ip link set "$IFACE" up 2>/dev/null || true
log "Interface $INTERFACE configured, peer at $PEER_IP"
# Check if already configured
if ip addr show "$IFACE" | grep -q "$HOST_IP"; then
log "Interface $IFACE already configured"
else
# Remove any existing IPs and add ours
ip addr flush dev "$IFACE" 2>/dev/null || true
ip addr add "$HOST_IP/$NETMASK" dev "$IFACE" 2>/dev/null || true
fi
# Notify SecuBox API
curl -s --unix-socket /run/secubox/eye-remote.sock \
-X POST "http://localhost/api/v1/eye-remote/connected?peer_ip=$PEER_IP" \
2>/dev/null || true
# Wait for link to be up
sleep 1
log "Interface $IFACE UP, peer at $PEER_IP"
# Notify SecuBox API (run in background)
(
sleep 2
curl -s --unix-socket /run/secubox/eye-remote.sock \
-X POST "http://localhost/api/v1/eye-remote/connected?peer_ip=$PEER_IP" \
2>/dev/null || true
) &
;;
down)
log "Removing $INTERFACE configuration"
ip addr del "$HOST_IP/$NETMASK" dev "$INTERFACE" 2>/dev/null || true
IFACE=$(find_interface)
if [[ -z "$IFACE" ]]; then
log "No interface to remove"
exit 0
fi
log "Removing $IFACE configuration"
ip addr del "$HOST_IP/$NETMASK" dev "$IFACE" 2>/dev/null || true
# Notify SecuBox API
curl -s --unix-socket /run/secubox/eye-remote.sock \
@ -55,8 +110,19 @@ case "${1:-}" in
2>/dev/null || true
;;
status)
IFACE=$(find_interface)
if [[ -z "$IFACE" ]]; then
echo "No Eye Remote interface found"
exit 1
fi
ip addr show "$IFACE"
echo "rp_filter: $(cat /proc/sys/net/ipv4/conf/$IFACE/rp_filter 2>/dev/null || echo 'N/A')"
ping -c 1 -W 2 "$PEER_IP" &>/dev/null && echo "Peer $PEER_IP: REACHABLE" || echo "Peer $PEER_IP: NOT REACHABLE"
;;
*)
log "Usage: $0 up|down"
echo "Usage: $0 up|down|status"
exit 1
;;
esac

View File

@ -0,0 +1,6 @@
# SecuBox Eye Remote - sysctl configuration
# Disable reverse path filter for USB gadget interfaces
# Prevents "martian source" packet drops on usb0/usb1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0

View File

@ -11,13 +11,24 @@
# Pi Zero USB Gadget - CDC Ethernet (ECM)
# Vendor: 1d6b (Linux Foundation)
# Product: 0104 (Multifunction Composite Gadget)
# Note: Interface auto-named usb0/usb1 by kernel
ACTION=="add", SUBSYSTEM=="net", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0104", \
RUN+="/usr/local/sbin/secubox-eye-network.sh up"
ACTION=="remove", SUBSYSTEM=="net", ENV{ID_VENDOR_ID}=="1d6b", ENV{ID_MODEL_ID}=="0104", \
RUN+="/usr/local/sbin/secubox-eye-network.sh down"
# When USB gadget interface appears, configure it and disable rp_filter
# to prevent "martian source" packet drops
ACTION=="add", SUBSYSTEM=="net", KERNEL=="usb*", DRIVERS=="rndis_host|cdc_ether", \
RUN+="/bin/sh -c 'sysctl -w net.ipv4.conf.%k.rp_filter=0; /usr/local/sbin/secubox-eye-network.sh up'"
# When USB composite gadget binds (handles re-plug scenarios)
ACTION=="bind", SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", ATTR{idProduct}=="0104", \
RUN+="/bin/sh -c 'sleep 2 && /usr/local/sbin/secubox-eye-network.sh up &'"
# Alternative: match by MAC prefix (02:fb:00:00 - Pi Zero gadget)
ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="02:fb:*", \
RUN+="/bin/sh -c 'sysctl -w net.ipv4.conf.%k.rp_filter=0 2>/dev/null; /usr/local/sbin/secubox-eye-network.sh up'"
# Handle interface state changes (for re-plug detection)
ACTION=="change", SUBSYSTEM=="net", ATTR{address}=="02:fb:*", \
RUN+="/usr/local/sbin/secubox-eye-network.sh up"
# Cleanup on removal
ACTION=="remove", SUBSYSTEM=="net", ENV{ID_VENDOR_ID}=="1d6b", ENV{ID_MODEL_ID}=="0104", \
RUN+="/usr/local/sbin/secubox-eye-network.sh down"

View File

@ -788,10 +788,22 @@ USB0SVC
ln -sf /etc/systemd/system/usb0-up.service \
"$ROOT_MNT/etc/systemd/system/multi-user.target.wants/usb0-up.service"
# CRITICAL: Disable usb1 to prevent routing conflict
# Pi Zero exposes both RNDIS (usb0) and CDC-ECM (usb1) with same IP,
# causing routing loops. We only need usb0.
cat > "$ROOT_MNT/etc/network/interfaces.d/usb1-disable" << 'USB1DIS'
# Disable usb1 to prevent routing conflict with usb0
# Both interfaces get same IP causing routing loops
auto usb1
iface usb1 inet manual
pre-up ip link set usb1 down || true
USB1DIS
log "OTG Composite configuré:"
log " - Network: ECM (usb0 @ 10.55.0.2/30)"
log " - Serial: ACM (ttyGS0 @ 115200)"
log " - Modes: normal, flash, debug, tty, auth"
log " - usb1 disabled (routing conflict prevention)"
fi
# ══════════════════════════════════════════════════════════════════════════════