From 1e11c4dce8f1a873d12add838580c124729ec477 Mon Sep 17 00:00:00 2001 From: baichal <154722332+baichal@users.noreply.github.com> Date: Thu, 15 Aug 2024 19:34:16 +0800 Subject: [PATCH] Update socat.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加ipv6转发 增加防火墙检测 --- socat.sh | 377 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 326 insertions(+), 51 deletions(-) diff --git a/socat.sh b/socat.sh index 4a0f401..3decfe4 100644 --- a/socat.sh +++ b/socat.sh @@ -5,7 +5,7 @@ export PATH # ==================================================== # 系统要求: CentOS 6+、Debian 7+、Ubuntu 14+ # 描述: Socat 一键安装管理脚本 -# 版本: 3.1 +# 版本: 3.3 # ==================================================== Green="\033[32m" @@ -55,10 +55,14 @@ check_sys(){ # 获取本机IP(优化版本) get_ip(){ - ip=$(ip addr | grep 'inet ' | grep -v 127.0.0.1 | head -n1 | awk '{print $2}' | cut -d'/' -f1) - if [[ -z "$ip" ]]; then - ip="未知" - fi + local ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n1) + echo ${ip:-"未知IPv4"} +} + +# 获取IPv6地址 +get_ipv6(){ + local ipv6=$(ip -6 addr show | grep -oP '(?<=inet6\s)[\da-f:]+' | grep -v '^::1' | grep -v '^fe80' | head -n1) + echo ${ipv6:-"未知IPv6"} } # 安装Socat(只在需要时执行) @@ -84,18 +88,25 @@ install_socat(){ init_config() { if [ ! -f "$CONFIG_FILE" ]; then touch "$CONFIG_FILE" + echo "Debug: Created new config file: $CONFIG_FILE" + else + echo "Debug: Config file already exists: $CONFIG_FILE" fi } # 添加转发到配置文件 add_to_config() { - echo "$port1 $socatip $port2" >> "$CONFIG_FILE" + if [ "$ip_version" == "1" ]; then + echo "ipv4 $port1 $socatip $port2" >> "$CONFIG_FILE" + else + echo "ipv6 $port1 $socatip $port2" >> "$CONFIG_FILE" + fi } # 从配置文件中移除转发 remove_from_config() { local listen_port=$1 - sed -i "/^$listen_port /d" "$CONFIG_FILE" + sed -i "/ $listen_port /d" "$CONFIG_FILE" } # 检测端口是否占用 @@ -107,8 +118,109 @@ check_port() { return 0 } +# 规范化 IPv6 地址 +normalize_ipv6() { + local ip=$1 + + # 转换为小写 + ip=$(echo $ip | tr '[:upper:]' '[:lower:]') + + # 移除每组中的前导零 + ip=$(echo $ip | sed 's/\b0*\([0-9a-f]\)/\1/g') + + # 找到最长的连续零段 + local longest_zero="" + local current_zero="" + local IFS=":" + for group in $ip; do + if [ "$group" = "0" ]; then + current_zero="$current_zero:" + else + if [ ${#current_zero} -gt ${#longest_zero} ]; then + longest_zero=$current_zero + fi + current_zero="" + fi + done + if [ ${#current_zero} -gt ${#longest_zero} ]; then + longest_zero=$current_zero + fi + + # 如果存在连续的零,用双冒号替换 + if [ -n "$longest_zero" ]; then + ip=$(echo $ip | sed "s/$longest_zero/::/") + # 确保不会出现三个冒号 + ip=$(echo $ip | sed 's/:::/::/') + fi + + # 确保开头和结尾没有多余的冒号 + ip=$(echo $ip | sed 's/^://' | sed 's/:$//') + + echo $ip +} + +# 检查是否支持IPv6 +check_ipv6_support() { + # 检查系统是否启用了 IPv6 + if [ ! -f /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then + echo -e "${Red}错误: 您的系统似乎不支持 IPv6${Font}" + return 1 + fi + + if [ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6)" -eq 1 ]; then + echo -e "${Yellow}警告: IPv6 当前被禁用${Font}" + read -p "是否要启用 IPv6? (y/n): " enable_ipv6 + if [[ $enable_ipv6 =~ ^[Yy]$ ]]; then + sysctl -w net.ipv6.conf.all.disable_ipv6=0 + echo -e "${Green}IPv6 已启用${Font}" + else + echo -e "${Red}IPv6 保持禁用状态,无法进行 IPv6 转发${Font}" + return 1 + fi + fi + + # 检查系统是否有可用的 IPv6 地址 + local ipv6_addr=$(ip -6 addr show | grep -oP '(?<=inet6 )([0-9a-fA-F:]+)' | grep -v '^::1' | grep -v '^fe80' | head -n 1) + if [ -z "$ipv6_addr" ]; then + echo -e "${Red}错误: 未检测到可用的 IPv6 地址${Font}" + echo -e "${Yellow}请确保您的网络接口已配置 IPv6 地址${Font}" + return 1 + else + echo -e "${Green}检测到 IPv6 地址: $ipv6_addr${Font}" + fi + + # 检查 IPv6 转发是否启用 + if [ "$(cat /proc/sys/net/ipv6/conf/all/forwarding)" -eq 0 ]; then + echo -e "${Yellow}警告: IPv6 转发当前被禁用${Font}" + read -p "是否要启用 IPv6 转发? (y/n): " enable_forwarding + if [[ $enable_forwarding =~ ^[Yy]$ ]]; then + sysctl -w net.ipv6.conf.all.forwarding=1 + echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf + echo -e "${Green}IPv6 转发已启用${Font}" + else + echo -e "${Red}IPv6 转发保持禁用状态,可能影响转发功能${Font}" + return 1 + fi + fi + + return 0 +} + + # 配置Socat config_socat(){ + echo -e "${Green}请选择转发类型:${Font}" + echo "1. IPv4 端口转发" + echo "2. IPv6 端口转发" + read -p "请输入选项 [1-2]: " ip_version + + if [ "$ip_version" == "2" ]; then + if ! check_ipv6_support; then + echo -e "${Red}无法进行 IPv6 转发,请检查系统配置${Font}" + return 1 + fi + fi + echo -e "${Green}请输入Socat配置信息!${Font}" while true; do read -p "请输入本地端口: " port1 @@ -118,12 +230,146 @@ config_socat(){ done read -p "请输入远程端口: " port2 read -p "请输入远程IP: " socatip + + # 验证IP地址格式 + if [ "$ip_version" == "1" ]; then + if ! [[ $socatip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo -e "${Red}错误: 无效的IPv4地址格式${Font}" + return 1 + fi + elif [ "$ip_version" == "2" ]; then + if ! [[ $socatip =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]]; then + echo -e "${Red}错误: 无效的IPv6地址格式${Font}" + return 1 + fi + # 规范化 IPv6 地址 + socatip=$(normalize_ipv6 "$socatip") + else + echo -e "${Red}错误: 无效的选项${Font}" + return 1 + fi +} + +# 显示和删除转发 +view_delete_forward() { + if [ ! -s "$CONFIG_FILE" ]; then + echo -e "${Red}当前没有活动的转发。${Font}" + return + fi + + echo -e "${Green}当前转发列表:${Font}" + local i=1 + while IFS=' ' read -r ip_type listen_port remote_ip remote_port; do + if [ "$ip_type" == "ipv4" ]; then + echo "$i. IPv4: $ip:$listen_port --> $remote_ip:$remote_port" + else + echo "$i. IPv6: [$ipv6]:$listen_port --> [$remote_ip]:$remote_port" + fi + ((i++)) + done < "$CONFIG_FILE" + + read -p "请输入要删除的转发编号(多个编号用空格分隔,直接回车取消): " numbers + if [ -n "$numbers" ]; then + for num in $numbers; do + if [ $num -ge 1 ] && [ $num -lt $i ]; then + local line=$(sed -n "${num}p" "$CONFIG_FILE") + read -r ip_type listen_port remote_ip remote_port <<< "$line" + pkill -f "socat.*LISTEN:${listen_port}" + sed -i "${num}d" "$CONFIG_FILE" + remove_from_startup "$listen_port" "$ip_type" + if [ "$ip_type" == "ipv4" ]; then + echo -e "${Green}已删除IPv4转发: $ip:$listen_port${Font}" + else + echo -e "${Green}已删除IPv6转发: [$ipv6]:$listen_port${Font}" + fi + else + echo -e "${Red}无效的编号: $num${Font}" + fi + done + fi +} + +# 防火墙检测 +configure_firewall() { + local port=$1 + local ip_version=$2 + + # 检测防火墙工具 + local firewall_tool="" + if command -v firewall-cmd >/dev/null 2>&1; then + firewall_tool="firewalld" + elif command -v ufw >/dev/null 2>&1; then + firewall_tool="ufw" + elif command -v iptables >/dev/null 2>&1; then + firewall_tool="iptables" + fi + + if [ -z "$firewall_tool" ]; then + echo -e "${Yellow}未检测到防火墙工具,端口 ${port} 配置完成。${Font}" + return + fi + + # 检查防火墙修改权限 + local has_permission=false + case $firewall_tool in + "firewalld") + if firewall-cmd --state >/dev/null 2>&1; then + has_permission=true + fi + ;; + "ufw") + if ufw status >/dev/null 2>&1; then + has_permission=true + fi + ;; + "iptables") + if iptables -L >/dev/null 2>&1; then + has_permission=true + fi + ;; + esac + + if [ "$has_permission" = true ]; then + # 配置防火墙规则 + case $firewall_tool in + "firewalld") + if [ "$ip_version" == "ipv4" ]; then + firewall-cmd --zone=public --add-port=${port}/tcp --permanent >/dev/null 2>&1 + else + firewall-cmd --zone=public --add-port=${port}/tcp --permanent --ipv6 >/dev/null 2>&1 + fi + firewall-cmd --reload >/dev/null 2>&1 + ;; + "ufw") + ufw allow ${port}/tcp >/dev/null 2>&1 + ;; + "iptables") + if [ "$ip_version" == "ipv4" ]; then + iptables -I INPUT -p tcp --dport ${port} -j ACCEPT >/dev/null 2>&1 + else + ip6tables -I INPUT -p tcp --dport ${port} -j ACCEPT >/dev/null 2>&1 + fi + ;; + esac + echo -e "${Green}已成功为 ${ip_version} 端口 ${port} 配置防火墙规则。${Font}" + else + echo -e "${Yellow}检测到 ${firewall_tool},但无权限修改。请手动配置 ${ip_version} 端口 ${port} 的防火墙规则。${Font}" + fi } # 启动Socat start_socat(){ echo -e "${Green}正在配置Socat...${Font}" - nohup socat TCP4-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP4:${socatip}:${port2},keepalive,nodelay >> ./socat.log 2>&1 & + + if [ "$ip_version" == "1" ]; then + nohup socat TCP4-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP4:${socatip}:${port2},keepalive,nodelay >> ./socat.log 2>&1 & + elif [ "$ip_version" == "2" ]; then + nohup socat TCP6-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP6:${socatip}:${port2},keepalive,nodelay >> ./socat.log 2>&1 & + else + echo -e "${Red}无效的选项,退出配置。${Font}" + return + fi + local pid=$! sleep 2 @@ -132,10 +378,21 @@ start_socat(){ echo -e "${Blue}本地端口: ${port1}${Font}" echo -e "${Blue}远程端口: ${port2}${Font}" echo -e "${Blue}远程IP: ${socatip}${Font}" - echo -e "${Blue}本地服务器IP: ${ip}${Font}" + if [ "$ip_version" == "1" ]; then + echo -e "${Blue}本地服务器IP: ${ip}${Font}" + echo -e "${Blue}IP版本: IPv4${Font}" + else + echo -e "${Blue}本地服务器IPv6: ${ipv6}${Font}" + echo -e "${Blue}IP版本: IPv6${Font}" + fi add_to_config add_to_startup + if [ "$ip_version" == "1" ]; then + configure_firewall ${port1} "ipv4" + else + configure_firewall ${port1} "ipv6" + fi else echo -e "${Red}Socat启动失败,请检查配置和系统设置。${Font}" echo "检查 socat.log 文件以获取更多信息。" @@ -143,19 +400,6 @@ start_socat(){ fi } -configure_firewall() { - if [ "${OS}" == "CentOS" ]; then - firewall-cmd --zone=public --add-port=${port1}/tcp --permanent - firewall-cmd --reload - else - ufw allow ${port1}/tcp - fi - echo -e "${Green}已在防火墙中开放端口 ${port1}${Font}" -} - -# 在 start_socat 函数成功启动后调用 -configure_firewall - # 添加到开机自启 add_to_startup() { rc_local="/etc/rc.local" @@ -163,7 +407,12 @@ add_to_startup() { echo '#!/bin/bash' > "$rc_local" fi - startup_cmd="nohup socat TCP4-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP4:${socatip}:${port2},keepalive,nodelay >> $(pwd)/socat.log 2>&1 &" + if [ "$ip_version" == "1" ]; then + startup_cmd="nohup socat TCP4-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP4:${socatip}:${port2},keepalive,nodelay >> $(pwd)/socat.log 2>&1 &" + else + startup_cmd="nohup socat TCP6-LISTEN:${port1},reuseaddr,fork,keepalive,nodelay TCP6:${socatip}:${port2},keepalive,nodelay >> $(pwd)/socat.log 2>&1 &" + fi + if ! grep -q "$startup_cmd" "$rc_local"; then echo "$startup_cmd" >> "$rc_local" chmod +x "$rc_local" @@ -182,24 +431,32 @@ view_delete_forward() { echo -e "${Green}当前转发列表:${Font}" local i=1 - while read -r line; do - local listen_port=$(echo $line | awk '{print $1}') - local remote_ip=$(echo $line | awk '{print $2}') - local remote_port=$(echo $line | awk '{print $3}') - echo "$i. $ip:$listen_port --> $remote_ip:$remote_port" + local entries=() + while IFS=' ' read -r ip_type listen_port remote_ip remote_port; do + entries+=("$ip_type $listen_port $remote_ip $remote_port") + if [ "$ip_type" == "ipv4" ]; then + echo "$i. IPv4: $ip:$listen_port --> $remote_ip:$remote_port" + else + echo "$i. IPv6: [$ipv6]:$listen_port --> [$remote_ip]:$remote_port" + fi ((i++)) done < "$CONFIG_FILE" read -p "请输入要删除的转发编号(多个编号用空格分隔,直接回车取消): " numbers if [ -n "$numbers" ]; then - for num in $numbers; do + local nums_to_delete=($(echo "$numbers" | tr ' ' '\n' | sort -rn)) + for num in "${nums_to_delete[@]}"; do if [ $num -ge 1 ] && [ $num -lt $i ]; then - local line=$(sed -n "${num}p" "$CONFIG_FILE") - local listen_port=$(echo $line | awk '{print $1}') + local index=$((num-1)) + IFS=' ' read -r ip_type listen_port remote_ip remote_port <<< "${entries[$index]}" pkill -f "socat.*LISTEN:${listen_port}" - remove_from_config $listen_port - remove_from_startup $listen_port - echo -e "${Green}已删除转发: $ip:$listen_port${Font}" + sed -i "${num}d" "$CONFIG_FILE" + remove_from_startup "$listen_port" "$ip_type" + if [ "$ip_type" == "ipv4" ]; then + echo -e "${Green}已删除IPv4转发: $ip:$listen_port${Font}" + else + echo -e "${Green}已删除IPv6转发: [$ipv6]:$listen_port${Font}" + fi else echo -e "${Red}无效的编号: $num${Font}" fi @@ -210,10 +467,17 @@ view_delete_forward() { # 从开机自启动中移除 remove_from_startup() { local listen_port=$1 + local ip_type=$2 rc_local="/etc/rc.local" - if [ -f "$rc_local" ]; then - sed -i "/socat.*LISTEN:${listen_port}/d" "$rc_local" - echo -e "${Green}已从开机自启动中移除端口 ${listen_port} 的转发${Font}" + if [ -f "$rc_local" ] && [ -n "$listen_port" ] && [ -n "$ip_type" ]; then + if [ "$ip_type" == "ipv4" ]; then + sed -i "/socat TCP4-LISTEN:${listen_port}/d" "$rc_local" + elif [ "$ip_type" == "ipv6" ]; then + sed -i "/socat TCP6-LISTEN:${listen_port}/d" "$rc_local" + fi + echo -e "${Green}已从开机自启动中移除${ip_type}端口 ${listen_port} 的转发${Font}" + else + echo -e "${Yellow}警告: 无法移除开机自启动项,可能是因为文件不存在或参数无效${Font}" fi } @@ -231,22 +495,23 @@ kill_all_socat() { > "$CONFIG_FILE" # 清理开机自启动脚本 sed -i '/socat TCP4-LISTEN/d' /etc/rc.local + sed -i '/socat TCP6-LISTEN/d' /etc/rc.local echo -e "${Green}已从配置和开机自启动中移除所有 Socat 转发${Font}" } # 开启端口转发加速 enable_acceleration() { echo -e "${Green}正在开启端口转发加速...${Font}" - + # 启用 TCP Fast Open echo 3 > /proc/sys/net/ipv4/tcp_fastopen - + # 优化内核参数 sysctl -w net.ipv4.tcp_congestion_control=bbr sysctl -w net.core.default_qdisc=fq sysctl -w net.ipv4.tcp_slow_start_after_idle=0 sysctl -w net.ipv4.tcp_mtu_probing=1 - + # 新增优化参数 sysctl -w net.core.rmem_max=26214400 sysctl -w net.core.wmem_max=26214400 @@ -270,7 +535,7 @@ enable_acceleration() { sysctl -w net.ipv4.tcp_moderate_rcvbuf=1 sysctl -w net.core.optmem_max=65535 sysctl -w net.ipv4.tcp_notsent_lowat=16384 - + # 持久化设置 echo "net.ipv4.tcp_fastopen = 3" >> /etc/sysctl.conf echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf @@ -300,23 +565,23 @@ enable_acceleration() { echo "net.ipv4.tcp_moderate_rcvbuf = 1" >> /etc/sysctl.conf echo "net.core.optmem_max = 65535" >> /etc/sysctl.conf echo "net.ipv4.tcp_notsent_lowat = 16384" >> /etc/sysctl.conf - + sysctl -p - + echo -e "${Green}端口转发加速已开启${Font}" } # 关闭端口转发加速 disable_acceleration() { echo -e "${Yellow}正在关闭端口转发加速...${Font}" - + # 恢复默认内核参数 sysctl -w net.ipv4.tcp_fastopen=0 sysctl -w net.ipv4.tcp_congestion_control=cubic sysctl -w net.core.default_qdisc=pfifo_fast sysctl -w net.ipv4.tcp_slow_start_after_idle=1 sysctl -w net.ipv4.tcp_mtu_probing=0 - + # 恢复其他参数到默认值 sysctl -w net.core.wmem_max=212992 sysctl -w net.ipv4.tcp_rmem='4096 87380 6291456' @@ -337,7 +602,7 @@ disable_acceleration() { sysctl -w net.ipv4.tcp_moderate_rcvbuf=1 sysctl -w net.core.optmem_max=20480 sysctl -w net.ipv4.tcp_notsent_lowat=4294967295 - + # 从配置文件中移除所有自定义设置 sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.conf sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf @@ -365,9 +630,9 @@ disable_acceleration() { sed -i '/net.ipv4.tcp_moderate_rcvbuf/d' /etc/sysctl.conf sed -i '/net.core.optmem_max/d' /etc/sysctl.conf sed -i '/net.ipv4.tcp_notsent_lowat/d' /etc/sysctl.conf - + sysctl -p - + echo -e "${Yellow}端口转发加速已关闭${Font}" } @@ -388,7 +653,14 @@ main() { check_root check_sys install_socat - get_ip + + ip=$(get_ip) + ipv6=$(get_ipv6) + + echo "Debug: IP = $ip" + echo "Debug: IPv6 = $ipv6" + echo "Debug: CONFIG_FILE = $CONFIG_FILE" + init_config clear_screen @@ -398,8 +670,11 @@ main() { clear_screen case $choice in 1) - config_socat - start_socat + if config_socat; then + start_socat + else + echo -e "${Red}配置失败,未能启动 Socat${Font}" + fi press_any_key ;; 2)