#!/usr/bin/env bash
set -euo pipefail
### ================= 基本配置 =================
REDSOCKS_CONF="/etc/redsocks.conf"
REDSOCKS_BIN="/usr/sbin/redsocks" # 源码版可能是 /usr/local/bin/redsocks
REDSOCKS_PORT=12345
CHAIN="NETPROXY"
MARK="0x1"
STATE_DIR="/run/netproxy"
IPTABLES_BACKUP="$STATE_DIR/iptables.bak"
### ================= 工具函数 =================
die() {
echo "[ERROR] $*" >&2
exit 1
}
need_root() {
[[ $EUID -eq 0 ]] || die "Must run as root"
}
ensure_dirs() {
mkdir -p "$STATE_DIR"
}
parse_proxy() {
[[ "$1" =~ ^([^:]+):([0-9]+)$ ]] || die "Invalid proxy format: ip:port"
PROXY_IP="${BASH_REMATCH[1]}"
PROXY_PORT="${BASH_REMATCH[2]}"
}
### ================= redsocks =================
write_redsocks_conf() {
cat > "$REDSOCKS_CONF" <<EOF
base {
log_info = on;
log = "syslog:daemon";
daemon = on;
redirector = iptables;
}
redsocks {
local_ip = 127.0.0.1;
local_port = $REDSOCKS_PORT;
ip = $PROXY_IP;
port = $PROXY_PORT;
type = socks5;
}
EOF
}
start_redsocks() {
pkill redsocks 2>/dev/null || true
"$REDSOCKS_BIN" -c "$REDSOCKS_CONF"
}
stop_redsocks() {
pkill redsocks 2>/dev/null || true
}
### ================= iptables =================
iptables_save() {
iptables-save > "$IPTABLES_BACKUP"
}
iptables_restore() {
[[ -f "$IPTABLES_BACKUP" ]] && iptables-restore < "$IPTABLES_BACKUP"
}
iptables_apply() {
# nat 链
iptables -t nat -N $CHAIN 2>/dev/null || true
iptables -t nat -F $CHAIN
# 已标记流量直接放行(防回环)
iptables -t nat -A $CHAIN -m mark --mark $MARK -j RETURN
# 本地 & 私网放行
iptables -t nat -A $CHAIN -d 127.0.0.0/8 -j RETURN
# 排除代理服务器自身(关键)
iptables -t nat -A $CHAIN -d "$PROXY_IP" -j RETURN
# TCP 全部重定向
iptables -t nat -A $CHAIN -p tcp -j REDIRECT --to-ports $REDSOCKS_PORT
# 挂载到 OUTPUT(只影响本机)
iptables -t nat -C OUTPUT -p tcp -j $CHAIN 2>/dev/null || \
iptables -t nat -A OUTPUT -p tcp -j $CHAIN
# mangle:给 redsocks 出口流量打标记
iptables -t mangle -C OUTPUT -p tcp --sport $REDSOCKS_PORT -j MARK --set-mark $MARK 2>/dev/null || \
iptables -t mangle -A OUTPUT -p tcp --sport $REDSOCKS_PORT -j MARK --set-mark $MARK
}
iptables_clear() {
iptables -t nat -D OUTPUT -p tcp -j $CHAIN 2>/dev/null || true
iptables -t nat -F $CHAIN 2>/dev/null || true
iptables -t nat -X $CHAIN 2>/dev/null || true
iptables -t mangle -D OUTPUT -p tcp --sport $REDSOCKS_PORT -j MARK --set-mark $MARK 2>/dev/null || true
}
### ================= 命令实现 =================
cmd_start() {
parse_proxy "$1"
ensure_dirs
iptables_save
write_redsocks_conf
start_redsocks
iptables_apply
echo "[OK] netproxy started via $PROXY_IP:$PROXY_PORT"
}
cmd_stop() {
stop_redsocks
iptables_restore || iptables_clear
rm -rf "$STATE_DIR"
echo "[OK] netproxy stopped and restored"
}
cmd_status() {
echo "=== redsocks ==="
pgrep redsocks >/dev/null && echo "running" || echo "stopped"
echo
echo "=== iptables nat ==="
iptables -t nat -S | grep "$CHAIN" || echo "no netproxy nat rules"
echo
echo "=== iptables mangle ==="
iptables -t mangle -S | grep "$REDSOCKS_PORT" || echo "no netproxy mangle rules"
}
### ================= 主入口 =================
need_root
case "${1:-}" in
start)
[[ $# -eq 2 ]] || die "Usage: $0 start ip:port"
cmd_start "$2"
;;
stop)
cmd_stop
;;
status)
cmd_status
;;
*)
echo "Usage:"
echo " $0 start ip:port"
echo " $0 stop"
echo " $0 status"
exit 1
;;
esac