FreeBSD搭建IPv6 Mostly网络
About
2026.6.14 UPDATE 主要实现完整NAT64路由,以及完善的RA通告,可管理的DNS,提升性能

需要配置的组件,IPFW (NAT64)、Unbound (DNS64)、dhcpcd (获取上游IPv6-PD前缀)、Radvd (IPv6 ND),AdGuardHome(配置缓存上游DNS)
注意!!!!,本片教程仅适用于内网,不要在公网使用,在公网使用配置防火墙之前务必知道自己在做什么。
效果展示:


1、需要配置下本地的配置,为了防止失联,建议使用tty串口连接控制台,FreeBSD配置tty串口
编辑/etc/loader.conf
kern.geom.label.disk_ident.enable="0" kern.geom.label.gptid.enable="0" zfs_load="YES" boot_multicons="YES" boot_serial="YES" comconsole_speed="115200" console="comconsole,vidconsole"
编辑/etc/ttys
ttyu0 "/usr/libexec/getty 3wire.115200" vt100 on secure
开启SSH
sysrc sshd_enable="YES" service sshd restart
2、启用组件配置接口默认IP,编辑/etc/rc.conf,这里看着改,需要启用WAN口这里是net0的ND,不然不会生成默认路由
首先需要额外安装组件,这两个组件在Freebsd中默认不提供
pkg install adguardhome unbound radvd
编辑/etc/rc.conf
hostname="Home0-NAT64-Router" ifconfig_vtnet0="inet 10.10.0.64/24 netmask 255.255.255.0" defaultrouter="10.10.0.13" ifconfig_vtnet0_ipv6="inet6 accept_rtadv" dhcpcd_enable="YES" sshd_enable="YES" moused_nondefault_enable="NO" dumpdev="AUTO" zfs_enable="YES" gateway_enable="YES" ipv6_gateway_enable="YES" pf_enable="NO" radvd_enable="YES" adguardhome_enable="YES" unbound_enable="YES" firewall_enable="YES" firewall_script="/etc/ipfw.rules"
3、编辑IPFW,/etc/ipfw.rules,看不懂的话丢给AI就知道是啥了,AI太好用了知道吗,这个配置的安全性非常低,建议在非公网使用
#!/bin/sh
fwcmd="/sbin/ipfw"
${fwcmd} -f flush
${fwcmd} nat64lsn NAT64 destroy 2>/dev/null
kldstat -q -m ipfw_nat64 || kldload ipfw_nat64 #启用内核组件
ipfw add 105 allow tcp from any to 10.10.0.64 22 in recv vtnet0 setup keep-state #放行SSH
ipfw add 106 allow tcp from any to 10.10.0.64 3000 in recv vtnet0 setup keep-state #放行ADG 端口
${fwcmd} add 50 tcp-setmss 1432 tcp from any to any tcpflags syn setup #计算你的出口MTU
${fwcmd} nat64lsn NAT64 create prefix4 10.10.0.64/32 allow_private
${fwcmd} add 100 nat64lsn NAT64 ip6 from any to 64:ff9b::/96
${fwcmd} add 110 nat64lsn NAT64 ip4 from any to 10.10.0.64 in recv vtnet0
${fwcmd} add 115 allow ip6 from any to any in recv vtnet1 keep-state
${fwcmd} add 116 allow ip from any to 10.10.0.64 in recv vtnet0 keep-state
${fwcmd} add 65000 allow ip from any to any
4、配置Unbound,编辑/usr/local/etc/unbound/unbound.conf,依旧看不懂的话就丢给AI
sserver: chroot: "" username: "unbound" directory: "/usr/local/etc/unbound" interface: 2600:e641::64:64 interface: ::1 interface: fe80::be24:11ff:fe75:1760%vtnet1 port: 53 access-control: 2600:e641::/64 allow access-control: ::1 allow access-control: fe80::/10 allow access-control: ::/0 allow module-config: "dns64 iterator" dns64-prefix: 64:ff9b::/96 # dns64-synthall: yes #启用这个选项会过滤所有IPv6的地址强制使用NAT64 prefetch: yes prefetch-key: yes msg-cache-size: 16m rrset-cache-size: 32m do-not-query-localhost: no # log-queries: no # verbosity: 2 # logfile: "/var/log/unbound.log" forward-zone: name: "." forward-addr: 127.0.0.1@5353
5、配置dhcpcd,从我的RouterOS上划了一段电信的/64,通过DHCP分配过来,但是注意,如果不想给局域网其他设备分配PD前缀,需要使用DUID Bouding,这样默认其他设备申请dhcpv6,RouterOS就会忽略请求,配置例子
/ipv6 dhcp-server add comment=nat64-bsd-publicprefix interface=vlan10-general name=\ nat64-bsd-publicprefix /ipv6 dhcp-server binding add address=::/0 duid=0x0001000131bc22f1bc2411324e57 iaid=1 \ life-time=10m prefix-pool=ipv6-wan server=nat64-bsd-publicprefix
注意RouterOS上游更新之后并不会更新Bindings里面的地址,需要在dhcpv6-client里面添加一个脚本,这样就会重新下发新前缀了
:if ($"pd-valid" = 1) do={
:log info "IPv6 Prefix changed. Updating FreeBSD router binding..."
:local bindId [/ipv6 dhcp-server binding find where comment="freebsd-rt-prefix"]
:if ($bindId != "") do={
/ipv6 dhcp-server binding set $bindId address=::/64
:log info "FreeBSD router binding address has been reset to ::/64"
} else={
:log warning "DHCPv6 binding with comment 'freebsd-rt-prefix' not found."
}
}
DUID FreeBSD安装dhcpcd后使用cat /var/db/dhcpcd/duid获取,删掉冒号写成0x 16进制就行
接下来编辑/usr/local/etc/dhcpcd.conf,从vtnet0获取地址,分配前缀到vtnet1
ipv6only noipv4 duid persistent option rapid_commit require dhcp_server_identifier allowinterfaces vtnet0 interface vtnet0 ipv6rs ia_na 1 ia_pd 2 vtnet1/0
配置一个触发器/usr/local/etc/dhcpcd.exit-hook,如果前缀发生变化会重新发起BOUND6,只要检测到BOUND6重启radvd,使旧的Prefix的lifetime 为 0, 不然客户端不会知道目前的前缀过期了
#!/bin/sh echo "[$(date)] Kernel Single -> interface: $interface | Reason: $reason" >> /var/log/dhcpcd_test.log INT_IFACE="vtnet1" case "$reason" in BOUND6) if [ "$interface" = "$INT_IFACE" ]; then sleep 1 echo "[$(date)] detecting prefix change, reloading...">> /var/log/dhcpcd_test.log /usr/sbin/service radvd restart >/dev/null 2>&1 fi ;; esac
6、最后配置Radvd,这是Freebsd默认的IPv6 NDP,编辑cat /etc/radvd.conf,注意需要有pref64和rdnss参数,IOS和Android客户端才会开启464XLAT模式,使用IPv6传输IPv4
interface vtnet1 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvLinkMTU 1492; #让客户端使用1492 MTU如果你的出口是PPPOE拨号会有这个问题
AdvManagedFlag off;
AdvOtherConfigFlag on;
prefix ::/64 {
AdvOnLink on;
AdvAutonomous on;
AdvValidLifetime 7200;
AdvPreferredLifetime 1800;
DeprecatePrefix on; #当接口地址更新之后将过期地址进行Deprecate
};
RDNSS 2600:e641::64:64 { #设置一个内网的DNS,建议找一个不用打公网前缀,虽然也可以使用link-local,但是部分操作系统不兼容
AdvRDNSSLifetime 1800;
};
nat64prefix 64:ff9b::/96 {
AdvValidLifetime 1800;
};
};
7、最后一步直接reboot重启大法就行了,理论重启玩所有的都OK了,但是注意,ipv6上游前缀更新会导致临时断网,因为ipv4 over ipv6 clat转换,会导致ipv4也会临时瘫痪