[Linux]基于Linux + 无线网卡实现IP栈切换

待测组网

网络支持双栈:可以支持IPv4和IPv6类型的ip地址访问
网络只支持IPv4协议:只支持IPv4地址访问
网络只支持IPv6协议:只支持IPv6地址访问
网络协议未知:只用域名访问
网络存在DNS劫持

背景信息

公司wifi仅有ipv4,测试“IPv6 Only”或“双栈”需要NAT64/DNS64 转换
测试仅域名访问网络需要构造代理。

测试网络拓扑(精简)

--- config: look: neo theme: neutral --- graph LR subgraph Clients ["测试终端"] Phone["手机 / App"]:::client end subgraph Gateway ["Debian 13 测试网关"] direction TB AP["热点接入 (AP)"]:::phy subgraph Core_Logic ["流量处理核心"] direction TB Unbound["Unbound (DNS、DNS64服务)"]:::service Tayga["Tayga (IPv6 NAT64转换)"]:::service Direct_Route["IPv4 直接转发"]:::core end NAT["网络出口 (NAT)"]:::phy end subgraph Internet ["互联网"] Real_Server["业务服务器"]:::cloud end Phone -->|"1. 连接WiFi"| AP AP -.->|"2. DNS查询"| Unbound Unbound -.->|"3. 返回解析结果"| AP AP ==>|"4a. IPv6流量 (NAT64)"| Tayga Tayga ==>|"5a. 转换后流量"| NAT AP ==>|"4b. IPv4流量 (直连)"| Direct_Route Direct_Route ==>|"5b. 转发流量"| NAT NAT ==>|"6. 访问公网"| Real_Server classDef client fill:#e3f2fd,stroke:#1565c0,stroke-width:2px; classDef phy fill:#fff3e0,stroke:#e65100,stroke-width:2px; classDef service fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px; classDef core fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px; classDef cloud fill:#f5f5f5,stroke:#616161,stroke-width:2px; linkStyle 1,2 stroke:#f44336,stroke-width:2px,stroke-dasharray: 5 5; linkStyle 3,4,5,6,7 stroke:#2e7d32,stroke-width:3px;

环境准备:

物理网络:

ens160:上行网络
wlx40a5ef59b748:供测试机连接的AP
image

逻辑网络:

ipv4测试网关:10.42.0.1
ipv6测试网关:fd00::1
NAT64网关:192.168.255.1
NAT64转换前缀:64:ff9b::/96:当手机想访问 IPv4 的 8.8.8.8 时,会基于转换前缀把目标伪装成 64:ff9b::0808:0808。

驱动无线网卡

如果免驱,会直接识别,这里显示rtw_8822bu是不免驱的,需要手动安装驱动
image
image

方法1:使用apt安装dkms驱动包

sudo apt update
apt search rtl8822bu
apt search rtl88x2bu

image
image
没有找到rtl8822bu专用驱动,但是找到了通用的驱动 firmware-realtek

sudo apt install firmware-realtek
sudo iw list|grep Supported interface modes

安装后使用iw list验证,支持AP模式作为热点使用
image

方法2:源码编译
仓库地址:https://github.com/morrownr/88x2bu-20210702

# 安装构建环境
sudo apt update
sudo apt install -y build-essential dkms git bc linux-headers-$(uname -r)

# 克隆 morrownr 的 8822bu 仓库
git clone https://github.com/morrownr/88x2bu-20210702.git
cd 88x2bu-20210702

# 运行安装脚本 (会自动处理 DKMS)
sudo ./install-driver.sh

构造四种网络栈

安装依赖

sudo apt update
sudo apt install tayga unbound tinyproxy

Tayga (NAT64)
Unbound (DNS、DNS64)
tinyproxy(本地代理,用于屏蔽非域名)

基础配置

开启系统转发

sudo vim /etc/sysctl.conf

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

使之生效

sudo sysctl -p

配置 Tayga (NAT64)编辑配置文件

sudo vim /etc/tayga.conf

tun-device nat64
ipv4-addr 192.168.255.1
ipv6-addr fd00::1
prefix 64:ff9b::/96
dynamic-pool 192.168.255.0/24
data-dir /var/spool/tayga

sudo systemctl edit tayga
在[Service]部分填入以下内容:

[Service]
ExecStartPost=/usr/bin/ip link set nat64 up
ExecStartPost=/usr/bin/ip addr add 192.168.255.1 dev nat64
ExecStartPost=/usr/bin/ip addr add fd00::1 dev nat64
ExecStartPost=/usr/bin/ip route add 192.168.255.0/24 dev nat64
ExecStartPost=/usr/bin/ip route add 64:ff9b::/96 dev nat64

启用并启动 Tayga:

# 启动并设置开机自启
sudo systemctl enable --now tayga
sudo systemctl restart tayga
# 检查状态
sudo systemctl status tayga

ip addr show nat64

image

NAT64出网伪装

Tayga 将 v6 转为 v4 后,流量源IP是 192.168.255.x,必须做一次伪装才能通过物理网卡 ens160 上网。

# 临时生效
sudo apt install iptables iptables-persistent
#将source为192.168.255.0/24且output device为ens160的数据包的源IP地址,伪装为ens160 接口当前的 IP 地址
sudo iptables -t nat -A POSTROUTING -s 192.168.255.0/24 -o ens160 -j MASQUERADE

# 持久化建议:安装 iptables-persistent
sudo netfilter-persistent save

配置 DNS和DNS64

sudo vim /etc/unbound/unbound.conf.d/dns64.conf
监听:
interface: 127.0.0.1
interface: 192.168.255.1
interface: fd00::1
的53端口

server:
    verbosity: 1
    interface: 127.0.0.1
    interface: 192.168.255.1
    interface: fd00::1
    port: 53
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    
    # 允许局域网访问
    access-control: 0.0.0.0/0 allow
    access-control: ::0/0 allow

    # DNS64 模块
    module-config: "dns64 iterator"
    dns64-prefix: 64:ff9b::/96  # 必须与 tayga.conf 的 prefix 一致

forward-zone:
    name: "."
    # 上游 DNS,可以使用阿里云或 Google
    forward-addr: 223.5.5.5
    forward-addr: 8.8.8.8

重启unbound:

sudo systemctl restart unbound
sudo systemctl status unbound

验证本地DNS64解析v6地址

dig @127.0.0.1 baidu.com AAAA +short

image

Test_IPv4_Only网络

除启停外的配置命令,仅需执行一次

export WLAN="wlx40a5ef59b748"

sudo nmcli con add type wifi ifname $WLAN con-name Hotspot_v4 autoconnect no ssid "Test_IPv4_Only"
sudo nmcli con modify Hotspot_v4 802-11-wireless.mode ap wifi-sec.key-mgmt wpa-psk wifi-sec.psk "12345678"
sudo nmcli con modify Hotspot_v4 ipv4.method shared ipv6.method ignore

#如果你是用的是三星手机,需要关闭pmf
sudo nmcli con modify Hotspot_v4 wifi-sec.pmf disable

#启动ap
sudo nmcli con up Hotspot_v4

#关闭ap
sudo nmcli con down Hotspot_v4

image

fe80:IPv4中的链路地址169.254.x.x,未获取DHCP地址时的本地地址。
image

image
使用本地dns解析对比,结果一致

image
手机端的localdns解析也正确

Test_IPv6_Only网络

除启停外的配置命令,仅需执行一次

export WLAN="wlx40a5ef59b748"

sudo nmcli con add type wifi ifname $WLAN con-name Hotspot_v6 autoconnect no ssid "Test_IPv6_Only"
sudo nmcli con modify Hotspot_v6 802-11-wireless.mode ap wifi-sec.key-mgmt wpa-psk wifi-sec.psk "12345678"
# 禁用 IPv4
sudo nmcli con modify Hotspot_v6 ipv4.method disabled
# 手动 IPv6:把自己设为网关
sudo nmcli con modify Hotspot_v6 ipv6.method manual ipv6.addresses fd00::1/64 ipv6.gateway fd00::1 ipv6.dns fd00::1
sudo nmcli con modify Hotspot_v6 ipv6.method shared
# 如果连接失败,重复执行以上两条命令

#如果你是用的是三星手机,需要关闭pmf
sudo nmcli con modify Hotspot_v6 wifi-sec.pmf disable

#启动ap
sudo nmcli con up Hotspot_v6

#关闭ap
sudo nmcli con down Hotspot_v6

image

192.0.0.2:这是苹果为了兼容性而内置的 CLAT (Customer-side Translator) 机制,属于 464XLAT 协议的一部分。
为了防止某些老旧 App(硬编码了 IPv4 或使用旧式 Socket API)在纯 IPv6 网络下崩溃,iOS 在手机内部虚拟了一个 IPv4 接口。

网关是fe80而不是fd00:在 IPv6 的设计哲学中,网关优先使用链路地址fe80而不是子网名fd00。

验证:

# 监听无线网卡,只看 IPv4 流量
sudo tcpdump -i wlx40a5ef59b748 ip

# 监听无线网卡,只看 IPv6 流量
sudo tcpdump -i wlx40a5ef59b748 ip6

# 抓取 wlx 接口,排除多播和广播,只看发往公网的数据
sudo tcpdump -i wlx40a5ef59b748 -n "not broadcast and not multicast"

image

日志可见iOS通过HTTPDNS获取了v6地址
image

双栈网络

除启停外的配置命令,仅需执行一次

export WLAN="wlx40a5ef59b748"

sudo nmcli con add type wifi ifname $WLAN con-name Hotspot_Dual autoconnect no ssid "Test_Dual_Stack"
sudo nmcli con modify Hotspot_Dual 802-11-wireless.mode ap wifi-sec.key-mgmt wpa-psk wifi-sec.psk "12345678"
# IPv4 共享 + IPv6 手动
sudo nmcli con modify Hotspot_Dual ipv4.method shared
sudo nmcli con modify Hotspot_Dual ipv6.method manual ipv6.addresses fd00::1/64 ipv6.gateway fd00::1 ipv6.dns fd00::1

sudo nmcli con modify Hotspot_Dual ipv6.method shared
#如果你是用的是三星手机,需要关闭pmf
sudo nmcli con modify Hotspot_Dual wifi-sec.pmf disable

#启动ap
sudo nmcli con up Hotspot_Dual

#关闭ap
sudo nmcli con down Hotspot_Dual

image

仅域名可用网络

复用ipv4网络,额外配置tinyproxy
sudo vim /etc/tinyproxy/tinyproxy.conf
在allow部分添加

Allow 10.42.0.0/16

然后重启服务

sudo systemctl restart tinyproxy

启动IPv4_Only热点,然后在手机端手动设置代理地址为10.42.0.1:8888

本地DNS污染网络

在 IPv6 Only 模式下,劫持DNS

sudo vim /etc/unbound/unbound.conf.d/poison.conf

server:
    # 定义该域名为静态区域
    local-zone: "offertoday.com." static
    
    # 强制将 A 记录解析为 2.2.2.2
    local-data: "offertoday.com. IN A 2.2.2.2"
    
    # 强制将 AAAA 记录解析为 2001:db8::1
    local-data: "offertoday.com. IN AAAA 2001:db8::1"

重启unbound

sudo systemctl restart unbound

验证劫持效果
image

IPv4 Only 或 双栈 (Dual Stack) 网络下,劫持DNS:

使用iptables拦截来自无线网卡53端口的udp请求,转发到unbound正在监听的nat64网关192.168.255.1

export WLAN="wlx40a5ef59b748"

# 添加 NAT 转发规则
sudo iptables -t nat -I PREROUTING -i $WLAN -p udp --dport 53 -j DNAT --to-destination 192.168.255.1
sudo iptables -t nat -I PREROUTING -i $WLAN -p tcp --dport 53 -j DNAT --to-destination 192.168.255.1

#若需要持久化
sudo netfilter-persistent save

启动需要的热点

sudo nmcli con up Hotspot_*

由于我们拦截的是来自无线网卡的DNS请求,debian本机不受影响,需在手机端验证效果:
web:
image
app:
image

总结

测试网络拓扑(完整):

--- config: look: neo theme: neutral --- graph LR %% ========================================== %% 布局设置 %% ========================================== subgraph Clients ["测试终端 / Clients"] direction TB Phone_v4["测试手机 (IPv4模式)"]:::client Phone_v6["测试手机 (IPv6模式)"]:::client end subgraph Debian_Gateway ["Debian 13 中间人网关"] direction TB %% 接入层 subgraph Layer_Access ["接入层 / Access"] WLAN["无线网卡 wlx...<br/>(AP模式)"]:::phy ETH["有线网卡 ens160<br/>(WAN出口)"]:::phy end %% 拦截层 (修正:仅保留v4劫持和出口SNAT) subgraph Layer_Filter ["拦截层 / Filter"] DNAT_4["iptables DNAT<br/>(IPv4 UDP/53)"]:::firewall SNAT["iptables Masquerade<br/>(SNAT)"]:::firewall end %% 核心服务层 subgraph Layer_Core ["核心层 / Core Services"] Routing{"系统路由表<br/>(Routing Table)"}:::core Unbound["Unbound DNS64<br/>(Listen: fd00::1)"]:::service Tayga["Tayga NAT64<br/>(Tun: nat64)"]:::service end end subgraph Internet ["互联网 / Internet"] direction TB Public_DNS["公网 DNS"]:::cloud Target_Server["业务服务器"]:::cloud end %% ========================================== %% 1. IPv4 业务流量 (绿色粗线) %% ========================================== Phone_v4 ==>|IPv4请求| WLAN WLAN ==>|转发| Routing Routing ==>|默认路由| SNAT SNAT ==>|伪装IP| ETH ETH ==>|物理传输| Target_Server %% ========================================== %% 2. IPv6 (NAT64) 业务流量 (蓝色粗线) %% ========================================== Phone_v6 ==>|"IPv6请求 (64:ff9b::)"| WLAN WLAN ==>|转发| Routing Routing ==>|路由至nat64| Tayga Tayga ==>|剥离v6,封装v4| Routing %% Tayga处理后变为v4流量,逻辑上汇入SNAT流程 %% ========================================== %% 3. DNS 查询路径 (红色虚线) - 逻辑修正版 %% ========================================== %% 3.1 IPv4 DNS: 仍需劫持 (因为手机默认发给网关IP) Phone_v4 -.->|"DNS Query (v4)"| WLAN WLAN -.->|Port 53| DNAT_4 Unbound -.->|若DNS劫持,直接返回错误IP| WLAN DNAT_4 -.->|劫持重定向| Unbound %% 3.2 IPv6 DNS: 直连监听 (因为手机通过RA获知DNS IP) Phone_v6 -.->|"DNS Query (v6)"| WLAN WLAN -.->|Dst: fd00::1| Unbound %% 3.3 Unbound 递归查询出口 Unbound -.->|"递归查询 (Forward)"| Routing Routing -.->|路由| SNAT SNAT -.->|出网| ETH ETH -.->|请求| Public_DNS %% ========================================== %% 样式定义 %% ========================================== classDef client fill:#e3f2fd,stroke:#1565c0,stroke-width:2px; classDef phy fill:#fff3e0,stroke:#e65100,stroke-width:2px; classDef firewall fill:#ffebee,stroke:#c62828,stroke-width:2px,stroke-dasharray: 5 5; classDef core fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,shape:rhombus; classDef service fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px; classDef cloud fill:#f5f5f5,stroke:#616161,stroke-width:2px; %% 连线着色 linkStyle 0,1,2,3,4,8 stroke:#2e7d32,stroke-width:3px; %% IPv4 Data linkStyle 5,6,7 stroke:#1565c0,stroke-width:3px; %% IPv6 Data linkStyle 9,10,11,12,13,14,15,16,17 stroke:#f44336,stroke-width:2px,stroke-dasharray: 5 5; %% DNS
posted @ 2025-12-15 17:16  夜歌乘年少  阅读(7)  评论(0)    收藏  举报