heiher / natmap

TCP/UDP port mapping for full cone NAT
MIT License
1.3k stars 98 forks source link

是否可以增加个先行脚本参数呢 例如-e 带的脚本 #65

Closed dmserver closed 4 months ago

dmserver commented 4 months ago

是否可以增加个先行脚本参数呢 例如-e 带的脚本 只是这个先行脚本 需要在程序功能执行前 先执行这个脚本 并且程序将-s -b等参数附加给脚本调用 想到这个是因为tplink路由下程序运行起来后 tp无法进行端口映射和upnp了 所以想穿出去 必须先在路由上做端口映射 或者 upnp 路由upnp upnpc -i -e "natmap" -a 192.168.1.100 8080 8080 TCP 0

ysc3839 commented 4 months ago

可以通过环境变量传递

dmserver commented 4 months ago

可以接受环境变量传递

需要的是先执行脚本,后运行natmap,这样tplink路由才能正常使用 实在不行就只能先计划任务执行脚本 再运行natmap了

dmserver commented 4 months ago

`#!/bin/bash ###################################################################脚本测试模式数据

捆绑物理网卡

    eth="eth0"
    #路由层数   有几层路由就填几,当前主机上面只有一台路由就填1,可以使用ssh命令traceroute -n -m 10 223.5.5.5检查经过几个家用路由器(运营商的不算,具体看情况)
    wan_upnp=4
    #打开外网端口    这个是natmap -b的端口
    wan_upnp_port=9090
    #打开内网端口    这个是natmap -b的端口
    lan_upnp_port=2022
    #路由upnp xml 文件名 各家路由可能不一样 尽量搜集就是了
    upnp_xml="igd.xml rootDesc.xml UPnP/IGD.xml"
    #upnp 备注名
    upnp_port_name="STUN-TEXT"
    #旁路由跃迁  旁路由跃迁模式(yes/no),开启后,若发现路由网关信息内出现旁路由,将直接由主路映射至下级设备,流量不经转旁路,正常情况不会出现旁路信息
    side_route="no"
    #测试协议
    protocol="tcp"

########################################################################################################下方固定函数及内容 times=$(date +"%F %T") upnp_text_info=$times" upnp测试" if ! type upnpc > /dev/null 2>&1; then echo 'upnpc 未安装,开始安装'; apt-get install -y miniupnpc else cd / fi

if ! type traceroute > /dev/null 2>&1; then echo 'traceroute 未安装,开始安装'; apt-get install -y traceroute else cd / fi

if ! type fping > /dev/null 2>&1; then echo 'fping 未安装,开始安装'; apt-get install -y fping else cd / fi

lan_ip=$(ip address show $eth | head -n4 | grep inet | awk '{print$2}' | cut -d/ -f1-1) break_id=0 for ((;;)) do if [ -n "$eth" ]; then wan_gw_info=$(traceroute -n -m $wan_upnp -i $eth 223.5.5.5 | awk '{print$2}' | grep ".") else wan_gw_info=$(traceroute -n -m $wan_upnp 223.5.5.5 | awk '{print$2}' | grep ".") fi if [ -n "$wan_gw_info" ] || [ $break_id -eq 5 ]; then break fi ((break_id++)); done if [ ! -n "$wan_gw_info" ];then exit 0 fi #######################################################################操作函数 get_gw_upnp (){ gw_ip=$1 gx_xml=$2 break_id=1 upnp_xmls=(${gx_xml// / }) for ((upp=1;upp<3;upp++)) do upnp_port=$(nmap -F -max_rtt_timeout 1 -sT $gw_ip | grep open) if [ -n "$upnp_port" ] ; then break fi done upnp_port_info=$(echo $upnp_port | awk '{split($0, a, "open upnp"); print a[1]}' | rev | cut -d' ' -f2 | rev | cut -d/ -f1-1) if [[ -z $upnp_port_info ]] || [[ "$upnp_port_info" = "p" ]] || [[ "$upnp_port_info" = "p" ]]; then upnp_port_info="80 1900 5000" fi for upnp_xmlname in ${upnp_xmls[@]}; do for upnp_porti in ${upnp_port_info[@]}; do upnpxml_cs="http://"$gw_ip":"$upnp_porti"/"$upnp_xmlname upnp_xml_info=$(curl -L -k --connect-timeout 1 -m 1 -s "$upnpxml_cs") if [[ "$upnp_xml_info" = "xml"* ]];then upnpxml_url=$upnpxml_cs break fi done if [ ! -z $upnpxml_url ];then echo $upnpxml_url return 1 fi done break_id=1 for ((;;)) do upnpxml_cs=$(upnpc -i -P | grep "$gw_ip" | grep "desc" | cut -d: -f2-4) if [ -n "$upnpxml_cs" ]; then echo $upnpxml_cs return 1 fi upnpxml_cs=$(upnpc -P | grep "$gw_ip" | grep "desc" | cut -d: -f2-4) if [ -n "$upnpxml_cs" ]; then echo $upnpxml_cs return 1 fi if [ $break_id -eq 5 ];then echo $upnpxml_cs return 1 fi ((break_id++)); done } get_upnp_exip (){ upnpxml=$1 break_id=0 upnp_eth=$2 if [ -n "$upnp_eth" ]; then upnpeth=" -m "$upnp_eth else upnpeth=" " fi for ((;;)) do upnpexip=$(upnpc $upnpeth -u $upnpxml -S | grep ExternalIPAddress | awk '{print $3}') if [ -n "$upnpexip" ];then echo $upnpexip return 1 fi upnpexip=$(upnpc $upnpeth -i -u $upnpxml -S | grep ExternalIPAddress | awk '{print $3}') if [ -n "$upnpexip" ];then echo $upnpexip return 1 fi upnpexip=$(upnpc $upnpeth -u $upnpxml -s | grep ExternalIPAddress | awk '{print $3}') if [ -n "$upnpexip" ];then echo $upnpexip return 1 fi upnpexip=$(upnpc $upnpeth -i -u $upnpxml -s | grep ExternalIPAddress | awk '{print $3}') if [ -n "$upnpexip" ];then echo $upnpexip return 1 fi if [ $break_id -eq 5 ];then break fi ((break_id++)); done }

get_ip (){ gw_ip=$1 gw_port=$2 protocolas=$3 protocolac=$(echo $protocolas | tr a-z A-Z) upnp_text_infos=$4 ip_scan=$(echo $gw_ip | cut -d. -f1-3) ip_a_info=$(fping -a -g -q $ip_scan.0/24) ip_a_array=(${ip_a_info// / }) for ip_add in ${ip_a_array[@]}; do if [[ ${protocolac} = "TCP" ]] then ip_upnp_port=$(nc -z $ip_add $gw_port -w 2 && echo "open" || echo "close") ip_upnp_ports_a=$(curl -L -k --connect-timeout 1 -m 2 -s "http://$ip_add:$gw_port") ip_upnp_ports_b=$(curl -L -k --connect-timeout 1 -m 2 -s "https://$ip_add:$gw_port") else ip_upnp_port=$(nc -z $ip_add $gw_port -w 2 -u && echo "open" || echo "close") fi if [[ "$ip_upnp_port" = "open" ]] && [[ "$gw_ip" != "$ip_add" ]];then echo $ip_add return 1 fi done } upnp_nat_v2 (){ lan_ip_add=$1 route_upnp_xml=$2 lan_ip_port=$3 wan_ip_port=$4 upnp_protocol=$5 upnp_base=$6 upnp_eth=$7 upnp_pa=$upnpbase""$wan_ipport""$lan_ipport""$(echo $upnp_protocol | tr a-z A-Z) upnp_pb=$upnpbase""$wan_ipport""$lan_ipport""$upnp_protocol if [ -n "$upnp_eth" ]; then upnpeth=" -m "$upnp_eth else upnpeth=" " fi upnp_array=$(upnpc -u $route_upnp_xml $upnpeth -L | grep "$upnpbase" | awk '{print $3""$2}'| sed 's/-../ /g' | awk '{print $1""$1"_"$2}') for upnp_tp in ${upnp_array[@]}; do upnpc -u $route_upnp_xml $upnpeth -N $(echo $upnptp | sed 's/\/ /g') > /dev/null done upnpc -u $route_upnp_xml $upnpeth -N $wan_ip_port $wan_ip_port $upnp_protocol >/dev/null add_upnp_log=$(upnpc -u $route_upnp_xml $upnpeth -e "$upnp_base" -n $lan_ip_add $lan_ip_port $wan_ip_port $upnp_protocol 0) add_upnp_log_a=$(echo $add_upnp_log | grep "duration" | grep "InternalIP:Port") if [ -n "$add_upnp_log_a" ];then return 1 fi upnp_arrayp=$(upnpc -u $route_upnp_xml $upnpeth -L | grep "$upnpbase" | awk '{print $4""$3"_"$2}'| sed "s/->..:/_/g" | sed "s/'//g") for upnp_tpp in ${upnp_arrayp[@]}; do if [[ ${upnp_tpp} = ${upnp_pa} ]] || [[ ${upnp_tpp} = ${upnp_pb} ]] then return 1 else cd / fi done return 0 } upnp_nat_v2i (){ lan_ip_add=$1 route_upnp_xml=$2 lan_ip_port=$3 wan_ip_port=$4 upnp_protocol=$5 upnp_base=$6 upnp_eth=$7 upnp_pa=$upnpbase""$wan_ipport""$lan_ipport""$(echo $upnp_protocol | tr a-z A-Z) upnp_pb=$upnpbase""$wan_ipport""$lan_ipport""$upnp_protocol if [ -n "$upnp_eth" ]; then upnpeth=" -m "$upnp_eth else upnpeth=" " fi upnp_array=$(upnpc -i -u $route_upnp_xml $upnpeth -L | grep "$upnpbase" | awk '{print $3""$2}'| sed 's/-../ /g' | awk '{print $1""$1"_"$2}') for upnp_tp in ${upnp_array[@]}; do upnpc -i -u $route_upnp_xml $upnpeth -N $(echo $upnptp | sed 's/\/ /g') > /dev/null done upnpc -i -u $route_upnp_xml $upnpeth -N $wan_ip_port $wan_ip_port $upnp_protocol >/dev/null add_upnp_log=$(upnpc -i -u $route_upnp_xml $upnpeth -e "$upnp_base" -n $lan_ip_add $lan_ip_port $wan_ip_port $upnp_protocol 0) add_upnp_log_a=$(echo $add_upnp_log | grep "duration" | grep "InternalIP:Port") if [ -n "$add_upnp_log_a" ];then return 1 fi upnp_arrayp=$(upnpc -i -u $route_upnp_xml $upnpeth -L | grep "$upnpbase" | awk '{print $4""$3"_"$2}'| sed "s/->..:/_/g" | sed "s/'//g") for upnp_tpp in ${upnp_arrayp[@]}; do if [[ ${upnp_tpp} = ${upnp_pa} ]] || [[ ${upnp_tpp} = ${upnp_pb} ]] then return 1 else cd / fi done return 0 } upnp_nat_v1 (){ lan_ip_add=$1 route_upnp_xml=$2 lan_ip_port=$3 wan_ip_port=$4 upnp_protocol=$5 upnp_base=$6 upnp_eth=$7 upnp_pa=$upnpbase""$wan_ipport""$lan_ipport""$(echo $upnp_protocol | tr a-z A-Z) upnp_pb=$upnpbase""$wan_ipport""$lan_ipport""$upnp_protocol if [ -n "$upnp_eth" ]; then upnpeth=" -m "$upnp_eth else upnpeth=" " fi upnp_array=$(upnpc -u $route_upnp_xml $upnpeth -l | grep "$upnpbase" | awk '{print $3""$2}'| sed 's/-..//g') for upnp_tp in ${upnp_array[@]}; do upnpc -u $route_upnp_xml $upnpeth -d $(echo $upnptp | sed 's/\/ /g') > /dev/null done upnpc -u $route_upnp_xml $upnpeth -d $wan_ip_port $upnp_protocol >/dev/null add_upnp_log=$(upnpc -u $route_upnp_xml $upnpeth -e "$upnp_base" -a $lan_ip_add $lan_ip_port $wan_ip_port $upnp_protocol 0) add_upnp_log_a=$(echo $add_upnp_log | grep "duration" | grep "InternalIP:Port") if [ -n "$add_upnp_log_a" ];then return 1 fi upnp_arrayp=$(upnpc -u $route_upnp_xml $upnpeth -l | grep "$upnpbase" | awk '{print $4""$3"_"$2}'| sed "s/->..:/_/g" | sed "s/'//g") for upnp_tpp in ${upnp_arrayp[@]}; do if [[ ${upnp_tpp} = ${upnp_pa} ]] || [[ ${upnp_tpp} = ${upnp_pb} ]] then return 1 else cd / fi done return 0 } upnp_nat_v1i (){ lan_ip_add=$1 route_upnp_xml=$2 lan_ip_port=$3 wan_ip_port=$4 upnp_protocol=$5 upnp_base=$6 upnp_eth=$7 upnp_pa=$upnpbase""$wan_ipport""$lan_ipport""$(echo $upnp_protocol | tr a-z A-Z) upnp_pb=$upnpbase""$wan_ipport""$lan_ipport""$upnp_protocol if [ -n "$upnp_eth" ]; then upnpeth=" -m "$upnp_eth else upnpeth=" " fi upnp_array=$(upnpc -i -u $route_upnp_xml $upnpeth -l | grep "$upnpbase" | awk '{print $3""$2}'| sed 's/-..//g') for upnp_tp in ${upnp_array[@]}; do upnpc -i -u $route_upnp_xml $upnpeth -d $(echo $upnptp | sed 's/\/ /g') > /dev/null done upnpc -i -u $route_upnp_xml $upnpeth -d $wan_ip_port $upnp_protocol >/dev/null add_upnp_log=$(upnpc -i -u $route_upnp_xml $upnpeth -e "$upnp_base" -a $lan_ip_add $lan_ip_port $wan_ip_port $upnp_protocol 0) add_upnp_log_a=$(echo $add_upnp_log | grep "duration" | grep "InternalIP:Port") if [ -n "$add_upnp_log_a" ];then return 1 fi upnp_arrayp=$(upnpc -i -u $route_upnp_xml $upnpeth -l | grep "$upnpbase" | awk '{print $4""$3"_"$2}'| sed "s/->..:/_/g" | sed "s/'//g") for upnp_tpp in ${upnp_arrayp[@]}; do if [[ ${upnp_tpp} = ${upnp_pa} ]] || [[ ${upnp_tpp} = ${upnp_pb} ]] then return 1 else cd / fi done return 0 } #######################################################################开始处理数据 fb=$(expr $wan_upnp - 1) echo upnp_exip_up="" upnp_exip_down="" wan_gw_array=(${wan_gw_info// / }) for ((i=0;i<$wan_upnp;i++)) do wan_gw_ip=${wan_gw_array[$i]} gw_upnp_xml=$(get_gw_upnp $wan_gw_ip "$upnp_xml") echo $(date +"%F %T")"获取网关xml:"$gw_upnp_xml upnp_exip_down=$(get_upnp_exip "$gw_upnp_xml" $eth) echo $(date +"%F %T")"获取网关出口IP:"$upnp_exip_down

    if [ ! -n "$gw_upnp_xml" ];then
        echo "网关"$wan_gw_ip"未找到UPNP服务"
        exit 0
    fi
    if [[ $i -eq 0 ]]
        then
            #本机IP OR 内网下lan IP
            upnp_exip_up=$lan_ip
        else
            #映射的内网端口
            lan_upnp_port=$wan_upnp_port
            #本机IP OR 内网下lan IP(上一轮的出口IP)
            upnp_exip_up=$upnp_exip_down_d
            if [ ! -n "$upnp_exip_up" ]; then
                #上一轮未获取到出口IP作为LAN IP,将通过服务信息检索出服务IP
                upnp_exip_up=$(get_ip $wan_gw_ip $wan_upnp_port $protocol "$upnp_text_info")
            fi
    fi
    #差值 将本轮出口IP 作为下一网关 内网下lan
    upnp_exip_down_d=$upnp_exip_down

    #旁路由判定-基于IP段是否相同的方式判定 因为正常路由wan和lan不能处于相同网段
    gw_wan_domain_a=$(echo ${wan_gw_array[$i]} | cut -d. -f1-3)
    gw_wan_domain_b=$(echo ${wan_gw_array[$i + 1]} | cut -d. -f1-3)
    if [[ "$gw_wan_domain_a" = "$gw_wan_domain_b" ]] && [[ "$side_route" = "yes" ]];then
        echo "疑似遇到旁路由模式,停止本网关映射,进行映射跃迁,不经过旁路映射"
        if [[ $i -eq 0 ]]
            then
                #差值 将本轮 LAN IP 作为下一网关 内网下lan
                upnp_exip_down_d=$lan_ip
            else
                #差值 将本轮 LAN IP 作为下一网关 内网下lan
                upnp_exip_down_d=$upnp_exip_up
        fi
        continue
    fi
    break_id=1
    for ((;;))
        do
            upnp_nat_v2 $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            if [ $? -eq 1 ];then
                upnp_state="V2映射成功"
                break
            fi
            upnp_nat_v2i $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            if [ $? -eq 1 ];then
                upnp_state="V2i映射成功"
                break
            fi
            upnp_nat_v1 $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            if [ $? -eq 1 ];then
                upnp_state="V1映射成功"
                break
            fi
            upnp_nat_v1i $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            if [ $? -eq 1 ];then
                upnp_state="V1i映射成功"
                break
            fi
            if [ $break_id -eq 1 ];then
                upnp_state="映射失败"
                break
            fi
            ((break_id++));
        done
    if [[ "$cspding" != "route" ]]
        then
            echo ""
            echo $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            echo "路由层 "$i" "$upnp_state"--------"$upnp_exip_up "-->>" $wan_gw_ip "upnpXML" $gw_upnp_xml "ExitIP "$upnp_exip_down
        else
            echo $upnp_exip_up "$gw_upnp_xml" $lan_upnp_port $wan_upnp_port $protocol "$upnp_port_name" $eth
            echo "路由层 "$i" "$upnp_state"--------"$upnp_exip_up "-->>" $wan_gw_ip "upnpXML" $gw_upnp_xml "ExitIP "$upnp_exip_down
    fi
    echo ""
    echo ""
done`

``

ysc3839 commented 4 months ago

我以为你是想传递数据给成功后执行的notify script。 如果是要在启动时执行,那可以考虑脚本执行后再由脚本运行 natmap?

dmserver commented 4 months ago

我以为你是想传递数据给成功后执行的notify script。 如果是要在启动时执行,那可以考虑脚本执行后再由脚本运行 natmap?

这个方法可以,但还是得先给脚本指定参数再运行natmap,如果能直接给natmap指定参数去调用脚本就省事了

dmserver commented 4 months ago

我以为你是想传递数据给成功后执行的notify script。 如果是要在启动时执行,那可以考虑脚本执行后再由脚本运行 natmap?

我的考虑是,脚本模板被natmap调用, 无论natmap参数如何变动,把natmap参数给脚本预处理完成再运行natmap 如果是脚本调用natmap,那么脚本模板就得写natmap运行命令, 如果我把脚本重复的功能拆分成一个个的脚本模板,让模板可以相互组合调用,那么模板里的natmap启动命令就是个大问题,因为主脚本模板调用其他模板时不知道natmap是否在运行中,是否正常启动了

dmserver commented 4 months ago

我以为你是想传递数据给成功后执行的notify script。 如果是要在启动时执行,那可以考虑脚本执行后再由脚本运行 natmap?

这个参数推荐有,因为有这个参数可以省去写模板时调用natmap和监测natmap运行的麻烦, 主脚本引用其他脚本也不需要担心natmap重复运行的情况, 因为计划任务发现natmap程序在运行这个端口就不会再启动,如果预处理时间长导致多次启动脚本,这种就比较麻烦了,因为多启动的脚本会重复启动natmap

heiher commented 4 months ago

我也比较倾向于针对特定需求定制natmap的启动器(脚本),关于多个模板脚本之间的关联以及natmap的是否启动,在启动器中都可以维护状态标识来判断的。

dmserver commented 4 months ago

我也比较倾向于针对特定需求定制natmap的启动器(脚本),关于多个模板脚本之间的关联以及natmap的是否启动,在启动器中都可以维护状态标识来判断的。

我目前判断启动是根natmap的-b参数来判断的 如果无法获取到natmap进程里存在这个端口的任务就无法确定natmap是否正常启动了 所以我在想,是否可以增加先行脚本,来确保natmap的启动以及避免重复启动 因为upnp虽然是个很即时的功能,但如果同时存在2个相同进程 upnp可能会被清理掉, 因为upnp必须保持实时清理端口的状态,保持这个状态是因为很多路由厂商限制了upnp映射的数量,运行脚本首先做的就是清理upnp缓存,缓存是根据upnp任务备注来识别的, 华为路由限制upnp数量是32 如果出现多个相同upnp任务,就会导致upnp最终被清理,如果存在环境文件修改可能会导致文件配置崩溃

dmserver commented 4 months ago

我也比较倾向于针对特定需求定制natmap的启动器(脚本),关于多个模板脚本之间的关联以及natmap的是否启动,在启动器中都可以维护状态标识来判断的。

最主要的,虽然-b端口可以作为识别,但是在主机出现随机端口程序时,可能会导致natmap无法正常启动,因为端口冲突,这种时候需要终结natmap换端口再启动,而我也是这么做的,为了避免端口冲突,我都是用随机端口来启动natmap,正常启动后 -e的脚本做最后的操作,比如打开upnp 修改transmission端口

直到碰到tplink路由器,我才头疼,因为想用tplink就得先执行脚本开放upnp 再运行natmap 而脚本运行时长是不确定的,所以在脚本超时时,获取不到natmap就会再启动一遍,甚至换参数再启动,而为了避免端口冲突无法正常启动,在15秒后如果natmap没有获取到外网信息,就会强制换端口重启,而超时的前置脚本就可能成为残留任务,目前我是获取所有脚本进程以及根据-b来判断是否残留的,然后kill残留的脚本

dmserver commented 4 months ago

我也比较倾向于针对特定需求定制natmap的启动器(脚本),关于多个模板脚本之间的关联以及natmap的是否启动,在启动器中都可以维护状态标识来判断的。

因为脚本具有重复性,所以我把必要的参数全都写到配置文件中,脚本做成通用模板的形式,在启动前,启动脚本会根据备注名称修改配置文件的upnp端口 然后再启动natmap natmap启动后 -e脚本会获取到-b端口 然后脚本去配置文件读取这个端口的配置信息,加载完信息后开始打开路由upnp 修改对应的transmission端口。以及更新域名信息啥的

dmserver commented 4 months ago

我也比较倾向于针对特定需求定制natmap的启动器(脚本),关于多个模板脚本之间的关联以及natmap的是否启动,在启动器中都可以维护状态标识来判断的。

我已经决定好,修改我配置文件 和 脚本模板的全部结构 循环检测的结构也修改了, 这样就可以提前打开upnp了