fix(firmware): Three critical bugs found during MT3000 testing

1. Add yggdrasil init script (procd) — OpenWrt 25.x package doesn't
   include one, causing uci-defaults to fail on `/etc/init.d/yggdrasil
   enable` with set -e, leaving script in /etc/uci-defaults/ to re-run
   every boot and overwrite all config changes.

2. Delete default br-lan/lan before creating br-private — both bridges
   competing for eth0/eth1 port, preventing LAN cable access.

3. Per-device port mapping via /etc/parahub/port_map — filogic devices
   (MT3000, MT6000, WR3000) have eth0=WAN, eth1=LAN (opposite of
   qualcommax/ath79). Build.sh writes PORT_MAP, uci-defaults reads it.

Also: remove `set -e` from uci-defaults (too fragile for first-boot),
add SSH/HTTP firewall rules on yggdrasil zone for remote management.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 15:06:47 +00:00
parent 44e6e616cf
commit e84d578ce4
3 changed files with 90 additions and 11 deletions

17
files/etc/init.d/yggdrasil Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh /etc/rc.common
START=95
STOP=10
USE_PROCD=1
CONF_FILE="/etc/yggdrasil.conf"
start_service() {
[ -f "$CONF_FILE" ] || return 1
procd_open_instance
procd_set_param command /usr/sbin/yggdrasil -useconffile "$CONF_FILE"
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}

View File

@@ -7,8 +7,6 @@
# bumblebee (L3 Gateway) — full stack: yggdrasil, GRE6, VPN, guest isolation, SQM, DoH
# bee (L2 Transport) — minimal: batman-adv mesh relay, heartbeat
set -e
# Read firmware role (written by build.sh)
ROLE=$(cat /etc/parahub/role 2>/dev/null || echo "bumblebee")
@@ -77,6 +75,40 @@ chmod 600 /etc/parahub/keys
# 4. NETWORK CONFIGURATION
# ============================================================================
# --- Port mapping (written by build.sh per device) ---
PORT_MAP=$(cat /etc/parahub/port_map 2>/dev/null || echo "eth0:lan eth1:wan")
if [ "$PORT_MAP" = "dsa" ]; then
# DSA switch (ax53u) — individual ports
LAN_PORTS="lan1 lan2 lan3 lan4"
WAN_DEV="wan"
else
# Parse port map: "eth0:wan eth1:lan" → extract LAN and WAN
LAN_PORTS=""
WAN_DEV=""
for mapping in $PORT_MAP; do
port="${mapping%%:*}"
role="${mapping##*:}"
if [ "$role" = "lan" ]; then
LAN_PORTS="$port"
elif [ "$role" = "wan" ]; then
WAN_DEV="$port"
fi
done
[ -z "$LAN_PORTS" ] && LAN_PORTS="eth0"
[ -z "$WAN_DEV" ] && WAN_DEV="eth1"
fi
# Delete default OpenWrt br-lan/lan (conflicts with our br-private)
uci -q delete network.lan 2>/dev/null || true
# Find and delete br-lan device
for i in 0 1 2 3 4; do
name=$(uci -q get "network.@device[$i].name" 2>/dev/null)
if [ "$name" = "br-lan" ]; then
uci delete "network.@device[$i]"
break
fi
done
if [ "$ROLE" = "bee" ]; then
# --- Bee: minimal network (bat0 gw_mode=client, no GRE6, no guest, no policy routing) ---
uci batch <<-NET_EOF
@@ -114,7 +146,6 @@ set network.private_dev.type='bridge'
set network.private_dev.name='br-private'
delete network.private_dev.ports
add_list network.private_dev.ports='bat0'
add_list network.private_dev.ports='eth0'
set network.private=interface
set network.private.device='br-private'
@@ -124,10 +155,15 @@ set network.private.netmask='255.255.255.0'
# --- wan (DHCP, create if missing) ---
set network.wan=interface
set network.wan.device='eth1'
set network.wan.device='${WAN_DEV}'
set network.wan.proto='dhcp'
NET_EOF
# Add LAN ports to private bridge (DSA: lan1-4, non-DSA: eth0)
for port in $LAN_PORTS; do
uci add_list network.private_dev.ports="$port"
done
else
# --- Bumblebee: full network (gw_mode=server, GRE6, guest, policy routing) ---
uci batch <<-NET_EOF
@@ -165,7 +201,6 @@ set network.private_dev.type='bridge'
set network.private_dev.name='br-private'
delete network.private_dev.ports
add_list network.private_dev.ports='bat0'
add_list network.private_dev.ports='eth0'
set network.private=interface
set network.private.device='br-private'
@@ -181,7 +216,7 @@ set network.guest.netmask='255.255.255.0'
# --- wan (DHCP, create if missing) ---
set network.wan=interface
set network.wan.device='eth1'
set network.wan.device='${WAN_DEV}'
set network.wan.proto='dhcp'
# --- GRE6 tunnel (guest traffic → VPS gateway via Yggdrasil) ---
@@ -200,6 +235,11 @@ set network.@rule[-1].src='${GUEST_SUBNET}/24'
set network.@rule[-1].lookup='100'
set network.@rule[-1].priority='100'
NET_EOF
# Add LAN ports to private bridge (DSA: lan1-4, non-DSA: eth0)
for port in $LAN_PORTS; do
uci add_list network.private_dev.ports="$port"
done
fi
uci commit network
@@ -271,8 +311,7 @@ set wireless.public_2g=wifi-iface
set wireless.public_2g.device='${RADIO_2G}'
set wireless.public_2g.mode='ap'
set wireless.public_2g.ssid='${PUBLIC_SSID}'
set wireless.public_2g.encryption='owe'
set wireless.public_2g.owe_transition='1'
set wireless.public_2g.encryption='none'
set wireless.public_2g.network='private'
WIFI_2G_PUB
else
@@ -282,8 +321,7 @@ set wireless.public_2g=wifi-iface
set wireless.public_2g.device='${RADIO_2G}'
set wireless.public_2g.mode='ap'
set wireless.public_2g.ssid='${PUBLIC_SSID}'
set wireless.public_2g.encryption='owe'
set wireless.public_2g.owe_transition='1'
set wireless.public_2g.encryption='none'
set wireless.public_2g.isolate='1'
set wireless.public_2g.network='guest'
WIFI_2G_PUB
@@ -625,6 +663,22 @@ set firewall.@zone[-1].output='ACCEPT'
set firewall.@zone[-1].forward='REJECT'
add_list firewall.@zone[-1].network='yggdrasil'
# Allow SSH on Yggdrasil (remote management)
add firewall rule
set firewall.@rule[-1].name='Allow SSH Ygg'
set firewall.@rule[-1].src='yggdrasil'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].dest_port='22'
set firewall.@rule[-1].target='ACCEPT'
# Allow HTTP on Yggdrasil (LuCI)
add firewall rule
set firewall.@rule[-1].name='Allow HTTP Ygg'
set firewall.@rule[-1].src='yggdrasil'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].dest_port='80'
set firewall.@rule[-1].target='ACCEPT'
# Allow GRE6 protocol input (tunnel endpoint)
add firewall rule
set firewall.@rule[-1].name='Allow GRE6 input'
@@ -657,7 +711,7 @@ YGG_FW_EOF
uci commit firewall
# Enable yggdrasil service
/etc/init.d/yggdrasil enable
/etc/init.d/yggdrasil enable 2>/dev/null || true
# Save yggdrasil address to node keys file
YGG_ADDR=$(yggdrasil -address -useconffile /etc/yggdrasil.conf 2>/dev/null || echo "unknown")

View File

@@ -23,36 +23,43 @@ device_config() {
OPENWRT_TARGET="qualcommax/ipq60xx"
PROFILE="glinet_gl-axt1800"
FIRMWARE_ROLE="bumblebee"
PORT_MAP="eth0:lan eth1:wan"
;;
mt3000)
OPENWRT_TARGET="mediatek/filogic"
PROFILE="glinet_gl-mt3000"
FIRMWARE_ROLE="bumblebee"
PORT_MAP="eth0:wan eth1:lan"
;;
mt6000)
OPENWRT_TARGET="mediatek/filogic"
PROFILE="glinet_gl-mt6000"
FIRMWARE_ROLE="bumblebee"
PORT_MAP="eth0:wan eth1:lan"
;;
ax53u)
OPENWRT_TARGET="ramips/mt7621"
PROFILE="asus_rt-ax53u"
FIRMWARE_ROLE="bumblebee"
PORT_MAP="dsa"
;;
ar300m16)
OPENWRT_TARGET="ath79/generic"
PROFILE="glinet_gl-ar300m16"
FIRMWARE_ROLE="bee"
PORT_MAP="eth0:lan eth1:wan"
;;
wr3000)
OPENWRT_TARGET="mediatek/filogic"
PROFILE="cudy_wr3000-v1"
FIRMWARE_ROLE="bee"
PORT_MAP="eth0:wan eth1:lan"
;;
cpe710)
OPENWRT_TARGET="ath79/generic"
PROFILE="tplink_cpe710-v1"
FIRMWARE_ROLE="bee"
PORT_MAP="eth0:lan eth1:wan"
;;
*)
return 1
@@ -169,6 +176,7 @@ build_firmware() {
echo "$FIRMWARE_ROLE" > "$tmpfiles/etc/parahub/role"
echo "$OPENWRT_VERSION" > "$tmpfiles/etc/parahub/version"
echo "$PROFILE" > "$tmpfiles/etc/parahub/profile"
echo "$PORT_MAP" > "$tmpfiles/etc/parahub/port_map"
echo "Building firmware for profile: ${PROFILE}"
echo "Role: ${FIRMWARE_ROLE}"