Compare commits
5 Commits
c5b9229ad0
...
5c0984b97d
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0984b97d | |||
| 29070e95ef | |||
| 3771521d59 | |||
| e84d578ce4 | |||
| 44e6e616cf |
79
files/etc/init.d/parahub-vpn-tunnel
Executable file
79
files/etc/init.d/parahub-vpn-tunnel
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Parahub VPN Tunnel — Creates GRE6 tunnel over Yggdrasil for guest traffic
|
||||
# Runs after yggdrasil (START=95). OpenWrt 25.x lacks the netifd grev6 protocol
|
||||
# handler, so we create the tunnel manually with ip6gre.
|
||||
#
|
||||
# IMPORTANT: encaplimit must be "none" — Yggdrasil drops IPv6 packets with
|
||||
# Destination Options extension headers (added by default encaplimit 4).
|
||||
|
||||
START=96
|
||||
STOP=10
|
||||
|
||||
VPS_YGG="200:39f1:6a26:328a:d901:fbd2:d30d:faef"
|
||||
GRE_LOCAL_IP="172.16.0.2"
|
||||
GRE_GATEWAY="172.16.0.1"
|
||||
|
||||
start() {
|
||||
# Only for Bumblebee role
|
||||
[ "$(cat /etc/parahub/role 2>/dev/null)" = "bumblebee" ] || return 0
|
||||
|
||||
# Wait for Yggdrasil address (up to 60s)
|
||||
local ygg_addr="" attempts=0
|
||||
while [ $attempts -lt 30 ]; do
|
||||
ygg_addr=$(ip -6 addr show dev ygg0 scope global 2>/dev/null | awk '/inet6/{print $2}' | cut -d/ -f1 | head -1)
|
||||
[ -n "$ygg_addr" ] && break
|
||||
attempts=$((attempts + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ -z "$ygg_addr" ]; then
|
||||
logger -t parahub-vpn "No Yggdrasil address after 60s, skipping GRE6 tunnel"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create GRE6 tunnel (encaplimit none — critical for Yggdrasil compatibility)
|
||||
ip -6 tunnel add gre6-vpn mode ip6gre \
|
||||
remote "$VPS_YGG" \
|
||||
local "$ygg_addr" \
|
||||
encaplimit none
|
||||
ip addr add ${GRE_LOCAL_IP}/24 dev gre6-vpn
|
||||
ip link set gre6-vpn mtu 1400 up
|
||||
|
||||
# Default route through GRE (table 100 — used by guest policy routing)
|
||||
ip route add default via "$GRE_GATEWAY" table 100
|
||||
|
||||
# Guest subnet direct route in table 100 (background — WiFi may not be up yet)
|
||||
# Without this, router's own replies (source IP in guest subnet) go through
|
||||
# GRE instead of back to the guest client (ip rule matches source-based)
|
||||
local guest_subnet
|
||||
guest_subnet=$(awk -F= '/GUEST_SUBNET/{print $2}' /etc/parahub/keys 2>/dev/null)
|
||||
if [ -n "$guest_subnet" ]; then
|
||||
# WiFi interfaces take time to come up at boot — retry in background
|
||||
(
|
||||
local guest_dev="" ga=0
|
||||
while [ $ga -lt 30 ]; do
|
||||
guest_dev=$(ip route | grep "^${guest_subnet} " | awk '{print $3}')
|
||||
[ -n "$guest_dev" ] && break
|
||||
ga=$((ga + 1))
|
||||
sleep 2
|
||||
done
|
||||
if [ -n "$guest_dev" ]; then
|
||||
ip route add "$guest_subnet" dev "$guest_dev" table 100 2>/dev/null
|
||||
logger -t parahub-vpn "Guest route: $guest_subnet via $guest_dev in table 100"
|
||||
else
|
||||
logger -t parahub-vpn "Warning: guest device not found for $guest_subnet after 60s"
|
||||
fi
|
||||
) &
|
||||
fi
|
||||
|
||||
# Reload firewall so vpn_tunnel zone picks up gre6-vpn device
|
||||
/etc/init.d/firewall reload 2>/dev/null &
|
||||
|
||||
logger -t parahub-vpn "GRE6 tunnel up: ${GRE_LOCAL_IP} → ${VPS_YGG} via $ygg_addr (encaplimit none)"
|
||||
}
|
||||
|
||||
stop() {
|
||||
ip route flush table 100 2>/dev/null
|
||||
ip -6 tunnel del gre6-vpn 2>/dev/null
|
||||
logger -t parahub-vpn "GRE6 tunnel down"
|
||||
}
|
||||
17
files/etc/init.d/yggdrasil
Executable file
17
files/etc/init.d/yggdrasil
Executable 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
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -25,7 +23,7 @@ NODE_SUFFIX=$(echo "$BASE_MAC" | awk -F: '{print toupper($5$6)}')
|
||||
HOSTNAME="Parahub-${NODE_SUFFIX}"
|
||||
PRIVATE_SSID="Parahub_${NODE_SUFFIX}"
|
||||
MESH_ID="parahub-mesh"
|
||||
PUBLIC_SSID="Parahub_Free"
|
||||
PUBLIC_SSID="parahub.io/free"
|
||||
|
||||
# ============================================================================
|
||||
# 2. SUBNET GENERATION (collision avoidance from MAC octets)
|
||||
@@ -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,18 +216,13 @@ 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) ---
|
||||
# --- VPN tunnel interface (device created by parahub-vpn-tunnel init script) ---
|
||||
set network.vpn_tunnel=interface
|
||||
set network.vpn_tunnel.proto='grev6'
|
||||
set network.vpn_tunnel.peeraddr='200:39f1:6a26:328a:d901:fbd2:d30d:faef'
|
||||
set network.vpn_tunnel.ipaddr='172.16.0.2'
|
||||
set network.vpn_tunnel.netmask='255.255.255.0'
|
||||
set network.vpn_tunnel.gateway='172.16.0.1'
|
||||
set network.vpn_tunnel.mtu='1400'
|
||||
set network.vpn_tunnel.ip4table='100'
|
||||
set network.vpn_tunnel.device='gre6-vpn'
|
||||
set network.vpn_tunnel.proto='none'
|
||||
|
||||
# --- Policy routing: guest traffic → VPN table 100 ---
|
||||
add network rule
|
||||
@@ -200,6 +230,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
|
||||
@@ -250,7 +285,7 @@ for radio in $RADIOS; do
|
||||
uci -q delete "wireless.${radio}.disabled" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# --- 2.4GHz radio: mesh backhaul + Parahub_Free AP ---
|
||||
# --- 2.4GHz radio: mesh backhaul + parahub.io/free AP ---
|
||||
if [ -n "$RADIO_2G" ]; then
|
||||
# Mesh interface on 2.4GHz
|
||||
uci batch <<-WIFI_2G_MESH
|
||||
@@ -265,25 +300,23 @@ set wireless.mesh_2g.network='bat0_hardif_mesh0'
|
||||
WIFI_2G_MESH
|
||||
|
||||
if [ "$ROLE" = "bee" ]; then
|
||||
# Bee: Parahub_Free on private bridge (no guest isolation)
|
||||
# Bee: parahub.io/free on private bridge (no guest isolation)
|
||||
uci batch <<-WIFI_2G_PUB
|
||||
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
|
||||
# Bumblebee: Parahub_Free on guest network (isolated)
|
||||
# Bumblebee: parahub.io/free on guest network (isolated)
|
||||
uci batch <<-WIFI_2G_PUB
|
||||
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 +658,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'
|
||||
@@ -656,8 +705,9 @@ set firewall.@forwarding[-1].dest='yggdrasil'
|
||||
YGG_FW_EOF
|
||||
uci commit firewall
|
||||
|
||||
# Enable yggdrasil service
|
||||
/etc/init.d/yggdrasil enable
|
||||
# Enable yggdrasil service + VPN tunnel (creates GRE6 after yggdrasil starts)
|
||||
/etc/init.d/yggdrasil enable 2>/dev/null || true
|
||||
/etc/init.d/parahub-vpn-tunnel 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")
|
||||
@@ -720,7 +770,7 @@ logger -t parahub-mesh "Role: ${ROLE}"
|
||||
logger -t parahub-mesh "Private: ${PRIVATE_SSID} @ ${PRIV_IP}/24"
|
||||
|
||||
if [ "$ROLE" = "bee" ]; then
|
||||
logger -t parahub-mesh "Parahub_Free: bridged to private (no guest isolation)"
|
||||
logger -t parahub-mesh "parahub.io/free: bridged to private (no guest isolation)"
|
||||
logger -t parahub-mesh "bat0 gw_mode: client (uses Bumblebee as gateway)"
|
||||
else
|
||||
logger -t parahub-mesh "Guest: ${PUBLIC_SSID} @ ${GUEST_IP}/24"
|
||||
|
||||
@@ -196,7 +196,7 @@ ACCT_EOF
|
||||
echo ""
|
||||
echo "Done! Guest traffic now routes directly through Mullvad."
|
||||
echo "Server: $SERVER_HOST ($COUNTRY)"
|
||||
echo "Test: connect to Parahub_Free, visit https://am.i.mullvad.net"
|
||||
echo "Test: connect to parahub.io/free, visit https://am.i.mullvad.net"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -23,41 +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"
|
||||
;;
|
||||
ax6s)
|
||||
OPENWRT_TARGET="mediatek/mt7622"
|
||||
PROFILE="xiaomi_redmi-router-ax6s"
|
||||
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
|
||||
@@ -112,7 +114,6 @@ usage() {
|
||||
echo " axt1800 GL.iNet GL-AXT1800 (Slate AX) qualcommax/ipq60xx Bumblebee"
|
||||
echo " mt3000 GL.iNet GL-MT3000 (Beryl AX) mediatek/filogic Bumblebee"
|
||||
echo " mt6000 GL.iNet GL-MT6000 (Flint 2) mediatek/filogic Bumblebee"
|
||||
echo " ax6s Xiaomi Redmi AX6S mediatek/mt7622 Bumblebee"
|
||||
echo " ax53u Asus RT-AX53U ramips/mt7621 Bumblebee"
|
||||
echo " ar300m16 GL.iNet GL-AR300M16-EXT (16MB) ath79/generic Bee"
|
||||
echo " wr3000 Cudy AX3000 (WR3000) mediatek/filogic Bee"
|
||||
@@ -175,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}"
|
||||
|
||||
Reference in New Issue
Block a user