四.ROS高级配置
第一步 设置BGP路由
/routing bgp instance
add as=65002 disabled=no name=bird router-id=192.168.1.1
/routing bgp connection
add afi=ip,ipv6 as=65002 comment=BIRD connect=yes disabled=no instance=bird listen=yes local.address=192.168.1.1 \
.role=ebgp name=bird-else remote.address=192.168.1.2/32 .as=65001 routing-table=bgp-else
add afi=ip,ipv6 as=65002 comment=BIRD connect=yes disabled=no instance=bird listen=yes local.address=fd00:192:168:1::1 \
.role=ebgp name=bird-proxy remote.address=你的OSS eth0 ULA地址/128 .as=65001 routing-table=bgp-proxy
现在,所有的路由已经配置完毕,可以在winbox-ip-routes中查看
第二步 设置WireGurad
首先在CF中添加对ether2 GUA地址的DNS解析,假设为wg.123.xyz,这里以手机端为例
添加wiregurad:
/interface/wireguard/add
/ip address
add address=192.168.100.1/24 interface=wg1 network=192.168.100.0
/ipv6 address
add address=fd00:192:168:100::1 advertise=no interface=wg1
/interface wireguard peers
add allowed-address=192.168.100.2/32,fd00:192:168:100::2/128 client-allowed-address=::/0 comment=iPhone-Client interface=\
wg1 name=peer1 persistent-keepalive=25s public-key="手机端生成的公钥"
/ip firewall address-list
add address=192.168.100.0/24 list=local_subnet_ipv4
/ipv6 firewall address-list
add address=fd00:192:168:100::/64 list=local_ipv6
手机设置:
局域网IP地址为192.168.100.2/32,fd00:192:168:100::2/128
路由的IP地址可以填:192.168.0.0/16,fc00::/7,198.18.0.0/15,185.76.151.0/24, 149.154.160.0/20, 95.161.64.0/20, 91.108.56.0/22, 91.108.16.0/21, 91.108.8.0/21, 91.108.4.0/22, 91.105.192.0/23, 2a0a:f280::/32, 2001:b28:f23f::/48, 2001:b28:f23c::/47, 2001:67c:4e8::/48
手机端打开wg即可连接,可使用内网的DNS服务,代理服务(如果需要使用代理服务,记得将wg网段加入/routing/rule中查找bgp-proxy表).
第三步 设置DDNS
以CF为例,首先要获取API Token和Zone ID.打开 https://dash.cloudflare.com/profile/api-tokens - 点击创建令牌 - 编辑区域DNS 使用模板 - 区域资源 包括 所有区域 继续以显示摘要 - 创建令牌.保存这个API Token.Zone ID可以在 https://dash.cloudflare.com/ ,选择你的域名,往下滑的右下角有个区域ID.
前面已经创建了两个DNS记录,我们还需要查找他们的记录ID.
在OSS运行以下命令,并保存输出的id:
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/你的ZoneID/dns_records?name=*.123.xyz" \
-H "Authorization: Bearer 你的CF_Token" \
-H "Content-Type: application/json" | jq '.result[] | {name: .name, id: .id, content: .content}'
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/你的ZoneID/dns_records?name=wg.123.xyz" \
-H "Authorization: Bearer 你的CF_Token" \
-H "Content-Type: application/json" | jq '.result[] | {name: .name, id: .id, content: .content}'
还需要查找OSS端eth1的ipv6后缀,执行下面的命令,输出的结果即为后缀(包括:冒号):
ip -6 addr show eth1 | awk '/inet6 fd00:192:168:10/ {split($2, a, "/"); sub("fd00:192:168:10", "", a[1]); print a[1]; exit}'
ROS端创建脚本,去WinBox - system - script中修改参数为前面获取的:
/system script
add dont-require-permissions=no name=DDNS owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
source="# ================= \E5\85\A8\E5\B1\80\E5\85\B1\E4\BA\AB\E9\85\8D\E7\BD\AE =================\
\n# \E8\AF\B7\E5\9C\A8\E4\B8\8B\E6\96\B9\E5\BC\95\E5\8F\B7\E5\86\85\E5\A1\AB\E5\85\A5\E6\82\A8\E7\9A\84\E5\AE\9E\E9\99\85\E4\BF\
\A1\E6\81\AF\
\n:local CFToken \"\E8\AF\B7\E5\A1\AB\E5\86\99_Cloudflare_API_Token\"\
\n:local ZoneID \"\E8\AF\B7\E5\A1\AB\E5\86\99_Cloudflare_\E5\90\8C\E4\B8\80\E5\8C\BA\E5\9F\9F\E7\9A\84_Zone_ID\"\
\n\
\n# [\E5\85\B3\E9\94\AE 1] \E8\BF\90\E8\90\A5\E5\95\86\E7\BC\A9\E5\86\99 (\E7\94\A8\E4\BA\8E\E8\87\AA\E5\8A\A8\E6\8B\BC\E6\8E\
\A5 Pool \E5\90\8D\E7\A7\B0\E5\92\8C\E6\97\A5\E5\BF\97\E8\BE\93\E5\87\BA)\
\n:local ISP \"\E8\AF\B7\E5\A1\AB\E5\86\99_\E8\BF\90\E8\90\A5\E5\95\86\E7\BC\A9\E5\86\99_\E4\BE\8B\E5\A6\82_CT_CU\E6\88\96CM\"\
\n\
\n# [\E5\85\B3\E9\94\AE 2] \E5\90\8E\E7\AB\AF\E6\9C\8D\E5\8A\A1\E5\99\A8\E7\9A\84\E9\9D\99\E6\80\81\E5\90\8E\E7\BC\80 (\E4\BB\
\85\E4\BE\9B\E4\BB\BB\E5\8A\A1 1 \E6\8B\BC\E6\8E\A5\E4\BD\BF\E7\94\A8)\
\n:local DDNSSuffix \"\E8\AF\B7\E5\A1\AB\E5\86\99_OSS\E7\AB\AF\E5\90\8E\E7\BC\80\" \
\n# ===============================================\
\n\
\n# \E5\88\9D\E5\A7\8B\E5\8C\96\E5\85\A8\E5\B1\80\E5\8F\98\E9\87\8F (\E5\8E\BB\E9\99\A4\E7\89\B9\E5\AE\9A\E8\BF\90\E8\90\A5\E5\
\95\86\E7\A1\AC\E7\BC\96\E7\A0\81)\
\n:global IP6CURHOST; :if ([:typeof \$IP6CURHOST] = \"nothing\") do={ :set IP6CURHOST \"\" }\
\n:global IP6CURWG; :if ([:typeof \$IP6CURWG] = \"nothing\") do={ :set IP6CURWG \"\" }\
\n\
\n:log info \">>> Start CF-DDNS (Simplified Mode: \$ISP Host & WG) <<<\"\
\n\
\n# =========================================================\
\n# \E4\BB\BB\E5\8A\A1 1: \E6\9C\8D\E5\8A\A1\E5\99\A8 (\E9\9C\80\E6\8B\BC\E6\8E\A5\E5\90\8E\E7\BC\80)\
\n# \E6\8E\A5\E5\8F\A3: ether2\
\n# =========================================================\
\n:do {\
\n :local RecID \"\E8\AF\B7\E5\A1\AB\E5\86\99_\E4\B8\BB\E6\9C\8D\E5\8A\A1\E5\99\A8\E5\9F\9F\E5\90\8D\E7\9A\84_Record_ID\"\
\n :local Domain \"\E8\AF\B7\E5\A1\AB\E5\86\99_\E4\B8\BB\E6\9C\8D\E5\8A\A1\E5\99\A8\E5\9F\9F\E5\90\8D_\E4\BE\8B\E5\A6\82_hos\
t.example.com\"\
\n :local Pool \"IPv6 Public Pool \$ISP\"\
\n \
\n :if ([/interface get [find name=\"ether2\"] running]) do={\
\n :local addrIds [/ipv6 address find interface=\"ether2\" from-pool=\$Pool global]\
\n :if ([:len \$addrIds] > 0) do={\
\n :local rawFullIp [/ipv6 address get [:pick \$addrIds 0] address]\
\n :local slashIdx [:find \$rawFullIp \"/\"]\
\n :local ipOnly; :if ([:typeof \$slashIdx] = \"nil\") do={ :set ipOnly \$rawFullIp } else={ :set ipOnly [:pick \$ra\
wFullIp 0 \$slashIdx] }\
\n \
\n # --- \E6\8B\BC\E6\8E\A5\E9\80\BB\E8\BE\91 ---\
\n :local cleanPrefix \"\"; :local colonCount 0; :local found4thColon false\
\n :for i from=0 to=([:len \$ipOnly] - 1) do={\
\n :local char [:pick \$ipOnly \$i (\$i+1)]\
\n :if (\$found4thColon = false) do={\
\n :if (\$char = \":\") do={ :set colonCount (\$colonCount + 1) }\
\n if (\$colonCount < 4) do={ :set cleanPrefix (\$cleanPrefix . \$char) } else={ :set found4thColon true }\
\n }\
\n }\
\n :while ([:pick \$cleanPrefix ([:len \$cleanPrefix]-1) [:len \$cleanPrefix]] = \":\") do={ :set cleanPrefix [:pick\
\_\$cleanPrefix 0 ([:len \$cleanPrefix]-1)] }\
\n :local cleanSuffix \$DDNSSuffix\
\n :while ([:pick \$cleanSuffix 0 1] = \":\") do={ :set cleanSuffix [:pick \$cleanSuffix 1 [:len \$cleanSuffix]] }\
\n :local suffixColonCount 0\
\n :for i from=0 to=([:len \$cleanSuffix]-1) do={ :if ([:pick \$cleanSuffix \$i (\$i+1)] = \":\") do={ :set suffixCo\
lonCount (\$suffixColonCount + 1) } }\
\n :local finalIP\
\n :if (\$suffixColonCount >= 3) do={ :set finalIP (\$cleanPrefix . \":\" . \$cleanSuffix) } else={ :set finalIP (\$\
cleanPrefix . \"::\" . \$cleanSuffix) }\
\n # ---------------------\
\n\
\n :if (\$finalIP != \$IP6CURHOST) do={\
\n :log info \"[\$ISP-Server] IP Changed to \$finalIP. Updating DNS...\"\
\n :local url (\"https://api.cloudflare.com/client/v4/zones/\$ZoneID/dns_records/\$RecID/\")\
\n :do {\
\n /tool fetch http-method=put mode=https url=\$url check-certificate=no output=none \\\
\n http-header-field=\"Authorization: Bearer \$CFToken,Content-Type: application/json\" \\\
\n http-data=\"{\\\"type\\\":\\\"AAAA\\\",\\\"name\\\":\\\"\$Domain\\\",\\\"content\\\":\\\"\$finalIP\\\",\\\
\"ttl\\\":120,\\\"proxied\\\":false}\"\
\n :log info \"[\$ISP-Server] \$Domain updated.\"\
\n } on-error={ :log error \"[\$ISP-Server] Failed to update \$Domain\" }\
\n :set IP6CURHOST \$finalIP\
\n } else={ :log info \"[\$ISP-Server] Unchanged: \$finalIP\" }\
\n } else={ :log warning \"[\$ISP-Server] No IP found on ether2 from Pool \$Pool\" }\
\n } else={ :log warning \"[\$ISP-Server] Interface ether2 not running\" }\
\n}\
\n\
\n# =========================================================\
\n# \E4\BB\BB\E5\8A\A1 2: WG -> ROS \E6\9C\AC\E6\9C\BA (\E7\9B\B4\E6\8E\A5\E5\8F\96IP)\
\n# \E6\8E\A5\E5\8F\A3: ether2\
\n# =========================================================\
\n:do {\
\n :local RecID \"\E8\AF\B7\E5\A1\AB\E5\86\99_WG\E5\9F\9F\E5\90\8D\E7\9A\84_Record_ID\"\
\n :local Domain \"\E8\AF\B7\E5\A1\AB\E5\86\99_WG\E5\9F\9F\E5\90\8D_\E4\BE\8B\E5\A6\82_wg.example.com\"\
\n :local Pool \"IPv6 Public Pool \$ISP\"\
\n\
\n :if ([/interface get [find name=\"ether2\"] running]) do={\
\n :local addrIds [/ipv6 address find interface=\"ether2\" from-pool=\$Pool global]\
\n :if ([:len \$addrIds] > 0) do={\
\n :local rawFullIp [/ipv6 address get [:pick \$addrIds 0] address]\
\n \
\n # --- \E7\9B\B4\E5\8F\96\E9\80\BB\E8\BE\91\EF\BC\9A\E7\9B\B4\E6\8E\A5\E6\88\AA\E5\8F\96\EF\BC\8C\E4\B8\8D\E6\94\
\B9\E5\90\8E\E7\BC\80 ---\
\n :local slashIdx [:find \$rawFullIp \"/\"]\
\n :local finalIP [:pick \$rawFullIp 0 \$slashIdx]\
\n # --------------------------------\
\n\
\n :if (\$finalIP != \$IP6CURWG) do={\
\n :log info \"[\$ISP-WG] IP Changed to \$finalIP. Updating DNS...\"\
\n :local url (\"https://api.cloudflare.com/client/v4/zones/\$ZoneID/dns_records/\$RecID/\")\
\n :do {\
\n /tool fetch http-method=put mode=https url=\$url check-certificate=no output=none \\\
\n http-header-field=\"Authorization: Bearer \$CFToken,Content-Type: application/json\" \\\
\n http-data=\"{\\\"type\\\":\\\"AAAA\\\",\\\"name\\\":\\\"\$Domain\\\",\\\"content\\\":\\\"\$finalIP\\\",\\\
\"ttl\\\":120,\\\"proxied\\\":false}\"\
\n :log info \"[\$ISP-WG] \$Domain updated.\"\
\n } on-error={ :log error \"[\$ISP-WG] Failed to update \$Domain\" }\
\n :set IP6CURWG \$finalIP\
\n } else={ :log info \"[\$ISP-WG] Unchanged: \$finalIP\" }\
\n } else={ :log warning \"[\$ISP-WG] No IP found on ether2 from Pool \$Pool\" }\
\n } else={ :log warning \"[\$ISP-WG] Interface ether2 not running\" }\
\n}"
如果需要多个运营商,则创建多个脚本如DDNS CT/DDNS CM/DDNS CU
将执行命令插入到winbox - ipv6 - dhcp-client - 对应运营商 - advance的脚本中(最下方如图所示):

这样就可以在前缀变更时,触发式进行DDNS
至此,所有配置结束.因为内容非常的多,所以难免会有一些遗漏的地方,因为虚拟的实验环境IPv6的使用有问题,所以无法验证一些IPv6的操作是否完全正确.因为我本地使用的私有网段为10.0.0.0/8,所以如果看见一些这个网段的内容,那应该是没有改过来,可以自行修改一下.我已经在实验环境中大致复原了一遍三线接入和分流的操作,基本上是没有什么问题的.有什么问题或者更好的方法,欢迎佬们讨论.