23
2024
08
11:08:38

多 IP 环境下指定网络出口 IP 的方法 | 一键脚本

背景

内网防火墙接了多家宽带,故而主机也分配了多个 IPv6 地址,正常情况下防火墙默认线路是哪一条线路,主机默认使用的 IPv6 地址也是这条线路的。
但是,有时候我们需要使用移动 IPv6 来测试网络质量,有时候又需要电信,那么在不改防火墙配置的情况下有没有办法指定使用哪个 IPv6 地址呢?这是肯定的。

思路

Linux

Linux 环境下,我们可以利用 iptables 的 SNAT 功能,SNAT 即 Source Network Address Translation,源网络地址转换。内部地址要访问公网上的服务时,内部地址会主动发起连接,将内部地址转换为公网 IP,有关这个地址转换称为 SNAT。

Windows

Windows 环境下,也有 Linux 的 SNAT 相关功能,不过配置相对复杂,我们可以直接使用临时删除 IP 的方式来实现指定出站 IP。

方法

Linux

脚本

#!/bin/bash

echo -e '\n------------------------------\n'

#################### 指定 IPv6 运营商 ####################
#电信 ct、移动 cm、联通 cu
if [ ! -n "$1" ]; then
   echo -e '您未指定出口营运商前缀\n(电信:\033[36m ip6default.sh ct \033[0m;移动:\033[36m ip6default.sh cm \033[0m;联通:\033[36m ip6default.sh cu \033[0m)'
   t="default"
elif [ "${1}" == "ct" ]; then
   echo -e '指定营运商为:\033[36m 电信(240e) \033[0m'
   t="240e"
elif [ "${1}" == "cm" ]; then
   echo -e '指定营运商为:\033[36m 移动(2409) \033[0m'
   t="2409"
elif [ "${1}" == "cu" ]; then
   echo -e '指定营运商为:\033[36m 联通(2408) \033[0m'
   t="2408"
else
   echo -e '\033[31m指定前缀不支持,退出脚本 \033[0m\n'
   exit 0
fi

######################  获取网卡名称 ###################### 
check_net(){
   echo -e '\n------------------------------\n'
   echo -e "检测启用 IPv6 的网卡 ...\n"
   network=$(ifconfig |awk '{print $1}'|grep :|awk -F: '{print $1}')
   for net_name in ${network}
      do
         if [ ${net_name} = "lo" ]; then
            continue
         fi
         running_num=$(ifconfig ${net_name}|grep RUNNING|wc -l)
         if [ ${running_num} -ge 1 ]; then
            ips=$(ip addr show ${net_name} | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}')
            for aip in ${ips}
              do
                if [ "${aip}" != "${aip#*:[0-9a-fA-F]}" ]; then
                   preffix=$(echo ${aip} | tr ":" "\n" | head -n 1)
                   if [ "${preffix}" == "${t}" ]; then
                      echo -e "网卡\033[33m "${net_name}" \033[0m检测到匹配的 IPv6 地址:\033[33m "${aip}"\033[0m"
                      choose_net=${net_name}
                      choose_ip=${aip}
                      break
                   fi
                fi
              done
         else
            echo -e "网卡 "${net_name}" 已被禁用"
         fi
      done
   #ename=$(ifconfig |grep 4163|tr ":" "\n"|head -n 1)
   #ename="ens32"
   if [ ${choose_net} ]; then
      ename=${choose_net}
      echo -e '\n选定网卡接口名称:\033[33m '${ename}'\033[0m'
   else
      echo -e "\n所有网卡均未分配指定运营商 IPv6,退出脚本\n"
      exit 1
   fi
}

##################### 启/禁用 IPv6 ###################### 
enable_ipv6(){
   flag_ipv6=$(cat /proc/sys/net/ipv6/conf/${ename}/disable_ipv6)
   if [ "${1}" ]; then
         echo -e '------------------------------\n'
         echo -e '启用/禁用接口 IPv6 功能 ...'
      if [[ ${1} -eq "1" && ${flag_ipv6} -eq "0" ]]; then
         echo 0 > /proc/sys/net/ipv6/conf/${ename}/disable_ipv6
         echo -e '\n\033[32m已启用\033[0m'
      elif [[ ${1} -eq "0" && ${flag_ipv6} -eq "1" ]]; then
         echo -e '\n\033[32m已禁用\033[0m'
         echo 1 > /proc/sys/net/ipv6/conf/${ename}/disable_ipv6
      else
         echo -e '\n\033[32m无需操作\033[0m'
      fi
   else
      echo -e "\n未指定操作"
   fi
}
#################### 添加 NAT 规则  ####################
add_nat(){
   if [ ${1} == "1" ]; then
      echo -e '添加规则中 ...'
      add_nat=$(ip6tables -t nat -I POSTROUTING -o ${2} -d ::/0 -j SNAT --to-source ${3})
      add_cnt=$(ip6tables -t nat -vnL POSTROUTING --line-number | wc -l)
      if [ ${add_cnt} -eq 3 ]; then
         ipv6rules=$(ip6tables -t nat -vnL POSTROUTING --line-number | sed -n "3,3p" | awk '{str="接口:"$7",来源:"$8",目标:"$9",指定出口:"$10; printf str}' | awk 'BEGIN{FS="to:"}{printf $1}{printf $2}')
          echo -e "\n添加成功,NAT 规则为:\n\033[32m${ipv6rules}\033[0m\n"
          nat_tip="1"
      else
          echo -e '\n规则\033[32m添加失败\033[0m,可尝试手动执行命令:'
          do_nat ${1} ${2} ${3}
       fi
   fi
}
####################  NAT 操作命令  ####################
do_nat(){
    echo -e '查询 NAT 规则:(\033[36m ip6tables -t nat -vnL POSTROUTING --line-number\033[0m)'
    echo -e '删除 NAT 规则:(\033[36m ip6tables -t nat -F POSTROUTING\033[0m)'
    echo -e '添加 NAT 规则:(\033[36m ip6tables -t nat -I POSTROUTING -o '${2}' -d ::/0 -j SNAT --to-source '${3}'\033[0m)\n'
}
#################### 检测生效的 NAT  ####################
check_nat(){
   echo -e '\n------------------------------\n'
   echo -e '检测防火墙 NAT 规则 ...\n'
   rule_cnt=$(ip6tables -t nat -vnL POSTROUTING --line-number | wc -l)
   if [ ${rule_cnt} -le 2 ]; then
      echo -e '\033[32m未检测到生效的规则\033[0m\n'
      add_nat ${1} ${2} ${3}
   else
      if [ ${rule_cnt} -eq 3 ]; then
          ipv6rules=$(ip6tables -t nat -vnL POSTROUTING --line-number | sed -n "3,3p" | awk '{str="接口:"$7",来源:"$8",目标:"$9",指定出口:"$10; printf str}' | awk 'BEGIN{FS="to:"}{printf $1}{printf $2}')
          echo -e "NAT 规则为:\n\033[32m${ipv6rules}\033[0m"
          if [ -n "${3}" ]; then
            ipv6nat=$(ip6tables -t nat -vnL POSTROUTING --line-number | sed -n "3,3p" | awk '{printf $10}' | awk 'BEGIN{FS="to:"}{printf $2}')
            if [ "${ipv6nat}" != "${3}" ]; then
              echo -e "\n与已生效的 NAT 规则冲突,删除原规则 ..."
              del_rule=$(ip6tables -t nat -F POSTROUTING)
              rule_cnt=$(ip6tables -t nat -vnL POSTROUTING --line-number | wc -l)
              if [ ${rule_cnt} -le 2 ]; then
                echo -e '\033[32m\n删除成功\033[0m,尝试添加新规则 ...\n'
                add_nat ${1} ${2} ${3}
              fi
            else
               nat_tip="1"
            fi
          else
             nat_tip="1"
          fi
      else
          echo -e "\n检测到有多条生效规则,请手动执行命令:"
          do_nat ${1} ${2} ${3}
          echo -e '\n------------------------------\n'
          exit 1
      fi
      if [ "${nat_tip}" == "1" ]; then
        echo -e "\n可手动执行命令查看:\033[36m ip6tables -t nat -vnL POSTROUTING --line-number\033[0m\n"
      fi
    fi
}

#################### 检测生效的 IPv6 ####################
check(){
   echo -e '------------------------------\n'
   echo -e '检测已生效的 IP 地址 ...'
   ip6=$(curl -s -6 -X GET http://ipv6.lookup.test-ipv6.com | jq -r '(.ip)')
   if [[ ${ip6} == *"Couldn't connect to server"* ]]; then
      echo -e '\n未检测到生效的 IPv6 地址,退出脚本'
      exit 1
   elif [ ! "${ip6}" ]; then
      echo -e "\n\033[32m检测接口未响应数据,更换接口 ...\033[0m"
      ip6=$(curl -s -6 -X GET http://test6.ustc.edu.cn/backend/getIP.php | jq -r '(.processedString)')
      if [ ! "${ip6}" ]; then
         echo -e "\033[32m新检测接口未响应数据,再次更换接口 ...\033[0m"
         ip6=$(wget -q -O - http://speed.neu6.edu.cn/getIP.php)
         if [ ! "${ip6}" ]; then
            echo -e "\033[32m多次尝试仍未取得响应数据,退出脚本\033[0m"
            echo -e "\n可尝试手动执行命令:\033[36m curl -s -6 -X GET http://test6.ustc.edu.cn/backend/getIP.php | jq -r '(.processedString)'\033[0m\n"
             exit 1
         fi
      fi
   fi
      echo -e '\n检测到 IP 信息如下:'
      echo -e '\033[32mIP 地址:'${ip6}'\033[0m'
      info=$(curl -s -6 -X GET -A 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36' https://api.ip.sb/geoip/${ip6} | jq -r '"\n运营商:\(.isp)\n位置:\(.region),\(.country)"')
      echo -e '\033[32m'${info}'\033[0m\n'
      ipv6=$(echo ${ip6} | tr ":" "\n" | head -n 1)
      if [ ! -n "${ipv6}" ]; then
          echo -e '\033[31m未检测到 IPv6 地址,退出脚本 \033[0m\n'
          exit 1
      elif [ "${ipv6}" == "240e" ]; then
          echo -e '生效的 IPv6 运营商是:\033[32m 电信(240e) \033[0m'
      elif [ "${ipv6}" == "2409" ]; then
          echo -e '生效的 IPv6 运营商是:\033[32m 移动(2409) \033[0m'
      elif [ "${ipv6}" == "2408" ]; then
          echo -e '生效的 IPv6 运营商是:\033[32m 联通(2408) \033[0m'
      fi
      ipv6_nat=$(ip6tables -t nat -vnL POSTROUTING --line-number | sed -n "3,3p" | awk '{printf $10}' | awk 'BEGIN{FS="to:"}{printf $2}')
      prefix=$(echo ${ipv6_nat} | tr ":" "\n" | head -n 1)
      if [ "${ipv6}" == "${prefix}" ]; then
          echo -e '\033[32m与 NAT 规则一致 \033[0m'
      else
          echo -e '\033[31m与 NAT 规则不一致 \033[0m'
      fi
      if [ "${1}" ]; then
         if [ "${ipv6}" == "${1}" ]; then
            echo -e '\n\033[32m指定运营商执行成功!\033[0m'
            echo -e '\n\033[31m注意若 IPv6 地址发生变更,或者重启,需重新执行本脚本\033[0m\n'
         else
            echo -e '\n\033[31m指定运营商执行失败!\033[0m'
            echo -e '\n\033[31m生效的运营商与指定的运营商不是同一家,请重新执行本脚本\033[0m\n'
         fi
      fi

}

if [ "${t}" == "default" ]; then
   check_nat 0
   check
   echo -e '\n------------------------------\n'
else
#################### 设置指定的 IPv6 ####################
   echo -e 'IPv6 前缀为:\033[33m '${t}' \033[0m'
   check_net
   if [ ${choose_ip} ]; then
      ip6=${choose_ip}
   else
      ip6=$(ifconfig | grep ${t} | tr " " "\n" | grep ${t} | head -n 1)
   fi
   if [ ! -n "${ip6}" ]; then
      echo -e '\n\033[31m'${ename}'该接口无匹配 IPv6\033[0m'
      enable_ipv6 0
      echo -e '如需重新启用 IPv6 请手动执行命令:\033[36m echo 0 > /proc/sys/net/ipv6/conf/'${ename}'/disable_ipv6\033[0m'
      exit 0
   else
      echo -e '选定的出口 IPv6:\033[33m '${ip6}' \033[0m'
      check_nat 1 ${ename} ${ip6}
      check ${t}
   fi
fi

用法

本脚本支持指定电信、联通、移动三家运营商的 IPv6 地址,使用时执行:

root@ct:~/bash# ./ip6default.sh ct

其中 ct 指电信,cu 指联通,cm 指移动,若不指定,则默认检查当前系统生效的 IPv6 地址。

本脚本可自动识别网卡,也适用于多网卡环境。

本脚本使用 jq 命令,若未安装请先安装。

Windows

命令

::功能:指定出口 IPv6 地址
::用法:直接执行本脚本
::参数:参数 1:ct/cu/cm,用于指定运营商
::     参数 2:y/n,用于确认是否重新获取地址
::     参数 3:y/n,用于确认是否重新检测地址
::场景:网卡分配了多个 IPv6 地址,则使用本脚本可指定出口 IPv6 地址
::说明:若未输入参数,则需要在执行过程中输入
::原理:先通过重置网卡重新获取 IP 地址,然后通过删除不需要的 IPv6 地址来实现指定
::依赖:需安装 GNU 下的 curl、grep、awk、wget 命令

@echo off
title 指定出口 IPv6 地址
echo ------------------------------
echo ------- 指定出口 IPv6 地址------
echo ------------------------------
echo.
set type=%1
set reset=%2
set again=%3
if [%type%]==[] (
  SET /P type=请输入指定 IPv6 编号地址(ct/cu/cm):
)
if "%type%"=="ct" (
  echo 您选择的是:电信(ct)
  set suffix=240e
) else if "%type%"=="cu" (
  echo 您选择的是:联通(cu)
  set suffix=2408
) else if "%type%"=="cm" (
  echo 您选择的是:移动(cm)
  set suffix=2409
) else (
  echo 选择不正确,自动退出
  goto end
)
echo.
if [%reset%]==[] (
  if [%1]==[] (
    SET /P reset=是否重新获取 IP 地址(y/n):
  )
)
if "%reset%"=="y" (
  echo 您选择的是:重新获取(y)
) else (
  echo 您选择的是:不重新获取(n)
)

echo.
echo ------------------------------
echo.
echo 检测生效的网卡 ...
echo.
echo.
FOR /F "tokens=5*" %%i in ('netsh interface ip show interfaces ^| more +3 ^| grep -v "disconnected" ^| grep -v "Loopback"') DO (
   set netname=%%i
   echo 检测到网卡名为:%%i
   echo.
   goto setipv6
)

::重新获取 IP
:resetnetwork
   if "%reset%"=="y" (
     echo ------------------------------
     echo.
     echo 更新本机 IP 地址 ...
::     netsh interface set interface "%netname%" disabled
::     netsh interface set interface "%netname%" enabled
     ipconfig /renew "%netname%"
::     timeout /nobreak /t 8
     echo 更新完成
     echo.
   )

::设置出口 IP
:setipv6
   echo 检测 %netname% 分配的 IPv6 地址 ...
::   FOR /F "tokens=15*" %%a in ('ipconfig ^| find "IPv6"') DO ( 
   FOR /F %%a in ('netsh interface ipv6 show addresses %netname% ^| find "参数" ^| grep -v "fe80" ^| awk "{ print $2 }"') DO ( 
     echo %%a | findstr %suffix% >NUL && (
       echo %%a(匹配,正在设置指定)
     ) || (
        if [%%a]==[] (
          echo 未检测到,自动退出
          goto end
        ) else (
          FOR /F %%j in ('netsh interface ipv6 delete address interface="%netname%" address="%%a"') DO (
            set selddrr=%%j
          )
        )
     )
   )
   echo.

echo ------------------------------
echo.
echo 设置完成,验证是否生效 ...
echo.
::timeout /nobreak /t 5
echo.
set ipaddr=
set IPV6_REGEX="\(\([0-9A-Fa-f]\{1,4\}:\)\{1,\}\)\(\([0-9A-Fa-f]\{1,4\}\)\{0,1\}\)\(\(:[0-9A-Fa-f]\{1,4\}\)\{1,\}\)"
FOR /F %%i in ('curl -6sL http://ip.sb ^| grep -m 1 -o %IPV6_REGEX%') DO (
   set ipaddr=%%i
)
if [%ipaddr%]==[] (
  echo 检测接口未响应数据,更换接口 ...
  goto check
) else (
  echo 检测到生效的地址是:%ipaddr%
  echo.
  echo 确认是否为指定 ...
  echo %ipaddr% | findstr %suffix% >NUL && (
    echo 已确认,操作成功
  ) || (
    echo 非指定运营商
    echo.
    if [%again%]==[] (
      SET /P again=是否重新检测(y/n):
    )
    if "%again%"=="y" (
      echo 您已选择重新检测(y)
      echo 检测中 ...
      goto check
    ) else (
      echo 取消检测,自动退出
      goto end
    )
  )
)

::测试发现,多次运行会出现地址检测不准确的情况,所以再加一个是否重新检测
:check
      FOR /F %%j in ('curl -6sL http://v6.ipv6-test.com/api/myip.php ^| grep -m 1 -o %IPV6_REGEX%') DO (
        set ipaddrr=%%j
      )
      if [%ipaddrr%]==[] (
        echo 检测接口未响应数据,更换接口 ...
        FOR /F %%l in ('wget -q -6 -O - http://speed.neu6.edu.cn/getIP.php') DO (
          set ipaddrr=%%l
        )
        if [%ipaddrr%]==[] (
          echo 多次尝试仍未取得响应数据,退出脚本
          goto end
        )
      )
      
        echo 检测到生效的 IPv6 地址是:%ipaddrr%
        echo.
        echo 确认是否为指定 ...
        echo %ipaddrr% | findstr %suffix% >NUL && (
          echo 已确认,操作成功
        ) || (
          echo 非指定运营商,操作失败
          goto end
        )
        echo.

:end
echo.
echo 路由跟踪确认一下:
tracert -6 -h 5 www.qq.com 
echo.
echo.
Cmd

用法

直接双击打开,根据提示输入即可。

注意,本脚本对多网卡支持度不好,后续再改善。

效果

Linux


84de05c9fe4b40a368f780633184ce6f_2610948993.jpg


Windows


74c3aa8b16eb3343715320294902095d_1402128930.jpg




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/7748.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: