温馨提示:
本文所述内容具有依赖性,可能因软硬条件不同而与预期有所差异,故请以实际为准,仅供参考。
背景
内网防火墙接了多家宽带,故而主机也分配了多个 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.
用法
直接双击打开,根据提示输入即可。
注意,本脚本对多网卡支持度不好,后续再改善。
Windows 10Chrome 115.0.0.0来自 新加坡 的大神
windows下脚本可改写成如下格式:
netsh interface ip show interfaces | more 3 | findstr /v "disconnected" | findstr /v "Loopback"
另外,该怎么样才能复制长文本,虽然已解除。^_^
Windows 10Chrome 115.0.0.0来自 新加坡 的大神
more 3
感谢分享