You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

89 lines
4.8 KiB

#!/bin/sh
# dpworld-ap-apply.sh — WiFi AP bring-up/down (spec §8). STA(wlan0) 무접촉, ap0 전용.
# 호출: --boot(seed) | (무인자, on-demand). ap-enabled 마커로 enable/disable 판정.
set -u
AP_DIR="/home/root/network/ap"
HOSTAPD_CONF="${AP_DIR}/hostapd-ap0.conf"
UDHCPD_CONF="${AP_DIR}/udhcpd-ap0.conf"
ENABLED_MARK="${AP_DIR}/ap-enabled"
STATE_DIR="/opt/dpworld-network"
LOG="${STATE_DIR}/ap_apply.log"
WLAN_MAC_FILE="/sys/class/net/wlan0/address"
log() { echo "[$(date '+%F %T' 2>/dev/null) up=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)] $*" >&2
[ -d "$STATE_DIR" ] && echo "$*" >> "$LOG" 2>/dev/null || true; }
run() { "$@"; rc=$?; [ "$rc" -ne 0 ] && log " cmd rc=$rc: $*"; return "$rc"; }
ap_mac() { # wlan0 MAC의 첫 옥텟에 local-admin bit(0x02) 세팅 → 별도 MAC
base=$(cat "$WLAN_MAC_FILE" 2>/dev/null); [ -n "$base" ] || { echo ""; return; }
o1=$(echo "$base" | cut -d: -f1); rest=$(echo "$base" | cut -d: -f2-)
[ -n "$o1" ] || { echo ""; return; } # 드라이버 리로드 중 빈 옥텟 → 산술오류 방지
new=$(printf '%02x' $(( 0x$o1 | 0x02 )))
echo "${new}:${rest}"
}
# §8.1 ap0 방화벽 — 전용 chain(cleanup=flush+delete로 잔재/중복 원천 차단).
# INPUT(장치 자체): v1.11.7 — ap0 클라이언트에 장치 서비스 전부 개방(SSH 22 포함). WPA2 PSK가 접근 게이트.
# (이전 default-deny[9090/67/icmp만 허용, 8989·8990·SSH 차단]에서 전환 — 현장 관리 접근성 우선, 비번 강화 전제.)
# FORWARD(경유): ip_forward=1일 때 AP 클라이언트가 PLC(eth1)·업링크로 라우팅하는 것을 전면 차단(provisioning 격리) — 유지.
# ★ INPUT은 열되 FORWARD 격리는 유지 → AP 클라이언트는 장치 자체엔 접근 가능하나 내부망/PLC로는 경유 불가.
AP_IN_CHAIN="DPWORLD_AP_IN"
AP_FWD_CHAIN="DPWORLD_AP_FWD"
fw_clear() { # unhook(점프 전부 drain) → flush → delete chain. 멱등 + 중복 점프(비정상/크래시 잔재)까지 제거.
while iptables -D INPUT -i ap0 -j "$AP_IN_CHAIN" 2>/dev/null; do :; done
while iptables -D FORWARD -i ap0 -j "$AP_FWD_CHAIN" 2>/dev/null; do :; done
while iptables -D FORWARD -o ap0 -j "$AP_FWD_CHAIN" 2>/dev/null; do :; done
iptables -F "$AP_IN_CHAIN" 2>/dev/null
iptables -F "$AP_FWD_CHAIN" 2>/dev/null
iptables -X "$AP_IN_CHAIN" 2>/dev/null
iptables -X "$AP_FWD_CHAIN" 2>/dev/null
}
fw_apply() { # 전용 chain 재생성 + INPUT/FORWARD에서 ap0 트래픽을 chain으로 점프. ap0 한정 → STA/eth 무영향.
fw_clear
# INPUT(장치 자체): ap0 클라이언트에 전체 개방(SSH 포함) — WPA2 PSK가 접근 게이트(v1.11.7).
run iptables -N "$AP_IN_CHAIN" || { fw_clear; return 1; }
run iptables -A "$AP_IN_CHAIN" -j ACCEPT || { fw_clear; return 1; }
run iptables -A INPUT -i ap0 -j "$AP_IN_CHAIN" || { fw_clear; return 1; }
# FORWARD(경유): PLC(eth1)/업링크 라우팅 전면 차단 — provisioning 격리 유지(INPUT 개방과 독립).
run iptables -N "$AP_FWD_CHAIN" || { fw_clear; return 1; }
run iptables -A "$AP_FWD_CHAIN" -j DROP || { fw_clear; return 1; }
run iptables -A FORWARD -i ap0 -j "$AP_FWD_CHAIN" || { fw_clear; return 1; }
run iptables -A FORWARD -o ap0 -j "$AP_FWD_CHAIN" || { fw_clear; return 1; }
}
ap_down() { # 멱등 정리 (stop은 미기동 시 routine 실패 → run 미사용, ap_up과 일치)
systemctl stop dpworld-udhcpd-ap0.service 2>/dev/null
systemctl stop dpworld-hostapd-ap0.service 2>/dev/null
fw_clear
ip addr flush dev ap0 2>/dev/null
iw dev ap0 del 2>/dev/null
log "AP down (cleaned)"
}
ap_up() {
[ -f "$HOSTAPD_CONF" ] || { log "no hostapd conf — skip"; return 0; }
# RECONCILE: 기존 데몬/방화벽 정리, vif는 재사용(없을 때만 add)
systemctl stop dpworld-udhcpd-ap0.service 2>/dev/null
systemctl stop dpworld-hostapd-ap0.service 2>/dev/null
fw_clear
iw dev ap0 info >/dev/null 2>&1 || run iw dev wlan0 interface add ap0 type __ap || return 1
mac=$(ap_mac); [ -n "$mac" ] && run ip link set ap0 address "$mac"
ap_ip=$(sed -n 's/^opt router \([0-9.]*\).*/\1/p' "$UDHCPD_CONF" 2>/dev/null)
if [ -n "$ap_ip" ]; then run ip addr replace "${ap_ip}/24" dev ap0 || return 1 # replace=멱등
else log "no udhcpd conf/router — fail closed"; return 1; fi
run ip link set ap0 up || return 1
if ! fw_apply; then
log "AP firewall setup failed"
ap_down
return 1
fi
touch /run/udhcpd-ap0.leases 2>/dev/null
run systemctl restart dpworld-hostapd-ap0.service || return 1
run systemctl restart dpworld-udhcpd-ap0.service || return 1
log "AP up (ssid rendered, ap0 up)"
}
mkdir -p "$STATE_DIR" 2>/dev/null || true
if [ -f "$ENABLED_MARK" ]; then ap_up; rc=$?; else ap_down; rc=$?; fi
exit "$rc"