New package for joining SecuBox mesh networks from OpenWRT devices. RPCD handler (/usr/libexec/rpcd/luci.masterlink): - status: Current mesh membership state - join: Join mesh with master_ip and token - leave: Leave current mesh network - info: Local node info (fingerprint, hostname, IP) - verify: Verify master before joining CLI tool (/usr/bin/sbx-mesh-join): - URL parsing: sbx-mesh-join 'http://ip:7331/master-link/?token=xxx' - Direct args: sbx-mesh-join 192.168.1.1 token123 - Auto-generates node fingerprint from MAC address - Saves to UCI on success LuCI interface (Services > Master-Link): - Status display (connected/pending/disconnected) - Invite URL/token input with Verify and Join buttons - Leave mesh button when connected - CLI usage help section Also adds screenshot-capture.js for automated LuCI screenshots. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
221 lines
7.0 KiB
Bash
221 lines
7.0 KiB
Bash
#!/bin/sh
|
|
# SecuBox Master-Link RPCD handler
|
|
# Provides ubus interface for mesh enrollment
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
NODE_ID_FILE="/etc/secubox/node.id"
|
|
CONFIG_FILE="/etc/config/masterlink"
|
|
|
|
# Generate or retrieve node fingerprint
|
|
get_fingerprint() {
|
|
if [ -f "$NODE_ID_FILE" ]; then
|
|
cat "$NODE_ID_FILE"
|
|
else
|
|
mkdir -p /etc/secubox
|
|
local mac=""
|
|
# Try br-lan first (OpenWRT), then eth0
|
|
if [ -f /sys/class/net/br-lan/address ]; then
|
|
mac=$(cat /sys/class/net/br-lan/address | tr -d ':')
|
|
elif [ -f /sys/class/net/eth0/address ]; then
|
|
mac=$(cat /sys/class/net/eth0/address | tr -d ':')
|
|
else
|
|
mac=$(cat /sys/class/net/*/address 2>/dev/null | head -1 | tr -d ':')
|
|
fi
|
|
local fp="owrt-${mac}"
|
|
echo "$fp" > "$NODE_ID_FILE"
|
|
echo "$fp"
|
|
fi
|
|
}
|
|
|
|
# Get primary LAN IP address
|
|
get_local_ip() {
|
|
local ip=""
|
|
# Try br-lan first (standard OpenWRT)
|
|
ip=$(ip -4 addr show br-lan 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1)
|
|
if [ -z "$ip" ]; then
|
|
# Fallback to eth0
|
|
ip=$(ip -4 addr show eth0 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}' | head -1)
|
|
fi
|
|
echo "$ip"
|
|
}
|
|
|
|
# Get device model
|
|
get_model() {
|
|
if [ -f /tmp/sysinfo/model ]; then
|
|
cat /tmp/sysinfo/model
|
|
elif [ -f /etc/board.json ]; then
|
|
jsonfilter -i /etc/board.json -e '@.model.name' 2>/dev/null
|
|
else
|
|
echo "Unknown"
|
|
fi
|
|
}
|
|
|
|
# Join a mesh network
|
|
do_join() {
|
|
local master_ip="$1"
|
|
local token="$2"
|
|
local fingerprint=$(get_fingerprint)
|
|
local hostname=$(uci -q get system.@system[0].hostname || echo "openwrt")
|
|
local address=$(get_local_ip)
|
|
local model=$(get_model)
|
|
|
|
# Prepare JSON payload
|
|
local payload="{\"token\":\"${token}\",\"fingerprint\":\"${fingerprint}\",\"hostname\":\"${hostname}\",\"address\":\"${address}\",\"model\":\"${model}\"}"
|
|
|
|
# Call master API
|
|
local response=$(wget -qO- --post-data="$payload" \
|
|
--header="Content-Type: application/json" \
|
|
--timeout=30 \
|
|
"http://${master_ip}:7331/api/v1/p2p/master-link/join" 2>/dev/null)
|
|
|
|
if [ -z "$response" ]; then
|
|
json_init
|
|
json_add_string "status" "error"
|
|
json_add_string "message" "Failed to connect to master"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Parse response status
|
|
local status=$(echo "$response" | jsonfilter -e '@.status' 2>/dev/null)
|
|
local master_fp=$(echo "$response" | jsonfilter -e '@.master_fingerprint' 2>/dev/null)
|
|
local depth=$(echo "$response" | jsonfilter -e '@.depth' 2>/dev/null)
|
|
|
|
case "$status" in
|
|
approved|pending)
|
|
# Save configuration
|
|
uci set masterlink.settings.enabled='1'
|
|
uci set masterlink.settings.role='peer'
|
|
uci set masterlink.settings.master_ip="$master_ip"
|
|
uci set masterlink.settings.status="$status"
|
|
[ -n "$master_fp" ] && uci set masterlink.settings.master_fingerprint="$master_fp"
|
|
[ -n "$depth" ] && uci set masterlink.settings.depth="$depth"
|
|
uci set masterlink.settings.joined_at="$(date -Iseconds)"
|
|
uci commit masterlink
|
|
;;
|
|
esac
|
|
|
|
# Return original response
|
|
echo "$response"
|
|
}
|
|
|
|
# Leave current mesh
|
|
do_leave() {
|
|
local master_ip=$(uci -q get masterlink.settings.master_ip)
|
|
local fingerprint=$(get_fingerprint)
|
|
|
|
# Notify master if connected
|
|
if [ -n "$master_ip" ]; then
|
|
wget -qO- --post-data="{\"fingerprint\":\"${fingerprint}\"}" \
|
|
--header="Content-Type: application/json" \
|
|
--timeout=10 \
|
|
"http://${master_ip}:7331/api/v1/p2p/master-link/leave" 2>/dev/null || true
|
|
fi
|
|
|
|
# Clear local configuration
|
|
uci set masterlink.settings.enabled='0'
|
|
uci set masterlink.settings.role='standalone'
|
|
uci set masterlink.settings.status='disconnected'
|
|
uci delete masterlink.settings.master_ip 2>/dev/null
|
|
uci delete masterlink.settings.master_fingerprint 2>/dev/null
|
|
uci delete masterlink.settings.depth 2>/dev/null
|
|
uci delete masterlink.settings.joined_at 2>/dev/null
|
|
uci commit masterlink
|
|
|
|
json_init
|
|
json_add_string "status" "ok"
|
|
json_add_string "message" "Left mesh network"
|
|
json_dump
|
|
}
|
|
|
|
# Get current status
|
|
do_status() {
|
|
local enabled=$(uci -q get masterlink.settings.enabled || echo '0')
|
|
local role=$(uci -q get masterlink.settings.role || echo 'standalone')
|
|
local status=$(uci -q get masterlink.settings.status || echo 'disconnected')
|
|
local master_ip=$(uci -q get masterlink.settings.master_ip)
|
|
local master_fp=$(uci -q get masterlink.settings.master_fingerprint)
|
|
local depth=$(uci -q get masterlink.settings.depth || echo '0')
|
|
local joined_at=$(uci -q get masterlink.settings.joined_at)
|
|
|
|
json_init
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_string "role" "$role"
|
|
json_add_string "status" "$status"
|
|
json_add_string "master_ip" "$master_ip"
|
|
json_add_string "master_fingerprint" "$master_fp"
|
|
json_add_int "depth" "$depth"
|
|
json_add_string "joined_at" "$joined_at"
|
|
json_add_string "fingerprint" "$(get_fingerprint)"
|
|
json_dump
|
|
}
|
|
|
|
# Get local node info
|
|
do_info() {
|
|
json_init
|
|
json_add_string "fingerprint" "$(get_fingerprint)"
|
|
json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'openwrt')"
|
|
json_add_string "address" "$(get_local_ip)"
|
|
json_add_string "model" "$(get_model)"
|
|
json_add_string "firmware" "$(cat /etc/openwrt_release 2>/dev/null | grep DISTRIB_RELEASE | cut -d= -f2 | tr -d \"\')"
|
|
json_dump
|
|
}
|
|
|
|
# Verify master before joining (get master info without committing)
|
|
do_verify() {
|
|
local master_ip="$1"
|
|
local token="$2"
|
|
|
|
# Request master info
|
|
local response=$(wget -qO- \
|
|
--timeout=10 \
|
|
"http://${master_ip}:7331/api/v1/p2p/master-link/info?token=${token}" 2>/dev/null)
|
|
|
|
if [ -z "$response" ]; then
|
|
json_init
|
|
json_add_string "status" "error"
|
|
json_add_string "message" "Failed to connect to master"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
echo "$response"
|
|
}
|
|
|
|
case "$1" in
|
|
list)
|
|
echo '{"status":{},"join":{"master_ip":"str","token":"str"},"leave":{},"info":{},"verify":{"master_ip":"str","token":"str"}}'
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
do_status
|
|
;;
|
|
join)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var master_ip master_ip
|
|
json_get_var token token
|
|
do_join "$master_ip" "$token"
|
|
;;
|
|
leave)
|
|
do_leave
|
|
;;
|
|
info)
|
|
do_info
|
|
;;
|
|
verify)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var master_ip master_ip
|
|
json_get_var token token
|
|
do_verify "$master_ip" "$token"
|
|
;;
|
|
*)
|
|
echo '{"error":"Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|