最近遇到个问题,只要 UU 开着 Openwrt 端的主机加速,在外面用 XStreaming 串流的时候延迟就会明显提高。由于 XStreaming 在设备端我让他直连的,起码不是用户端导致串流延迟。
那问题就只可能出在 UU 所在的 Openwrt 上,我目前的拓扑是这样的:
XBOX 192.168.31.13
↓
UU 主机加速 192.168.31.49
↓
旁路由 192.168.31.2
↓
主路由 192.168.31.1
↓
光猫
↓
Internet
很抽象……但确实有必要,因为我的旁路由上运行了其他的调整路由的工具,把 UU 主机加速装在上面会冲突,导致 UU 失效,所以我是在 PVE 上额外开了一个新的 vm 来跑 Openwrt 然后再跑 UU 加速。别问为什么不主路由拨号,问就是客观条件导致不能这么做😡
经过和 GPT 大哥共同分析后,得出了结论,只要 UU 主机加速处于加速状态,原本 XBOX 的远程串流媒体流就会进入 UU Openwrt 的 tun164,然后被封装成另一条 UDP 大流发到本市另一个服务器,大概就是网易租用的服务器……虽然也在本地不至于出境,但这中间就肯定会增加延迟。
既然是这样,那只要让 XBOX 的 9002 端口的媒体流永远不进 tun163/tun164(取决于设备是第一台加速的还是第二台加速的)就好了,那就得动 UU Openwrt 的路由。
😡然后这个时候就想到了我既然要单独改路由为什么不让 9002 的部分直接送主路由 192.168.31.1 呢,这样连中间的延迟都省了😡我靠我怎么才想到😡
由于我是主路由 dmz 到旁路由,旁路由上有 lucky 做端口转发,所以目前的问题在于解决 UU 抢走 9002,以及主路由上需要直接让 9002 转发到 XBOX 而不是被 dmz 到旁路由,同时不影响其他流量继续经过 UU,也就是需要变成这样:
XBOX 192.168.31.13
↓(UDP 9002) ↓(其他流量)
↓ UU 主机加速 192.168.31.49
↓ ↓
↓ 旁路由 192.168.31.2
↓ ↓
主路由 192.168.31.1
↓
光猫
↓
Internet
首先,为了最终的验证,以及实际上不要留下没必要的东西,先需要把原本在旁路由上的端口转发关闭,因为我原本的 9002 端口转发是这样的:
Internet 对我家 IP 9002 端口发起 UDP 请求
↓
光猫接到之后根据光猫上的端口转发设置,转发给主路由 9002 端口
↓
主路由接到之后根据 dmz 配置转发给旁路由 9002 端口
↓
旁路由接到之后根据 Lucky 的配置,转发给 192.168.31.13:9002
所以先要做的,就是把 Lucky 针对 9002 的端口转发关掉,那么在现在的情况下,9002 一路打到旁路由上就会停住,所以需要让主路由在 dmz 之前劫走 UDP 9002 的请求。
你可能会想,后台加一下就行,但是我们聪明的小米有着自己的小脑筋:

😡哈皮吧,Openwrt 又不是做不到,不知道为啥这么设计
所以就需要 ssh 进去路由器了。众所周知,小米用的 Openwrt 18.06,所以就是普通的 iptables:
root@XiaoQiang:~# iptables -t nat -L zone_wan_prerouting -n -v --line-numbers
Chain zone_wan_prerouting (1 references)
num pkts bytes target prot opt in out source destination
1 26447 3437K MINIUPNPD all -- * * 0.0.0.0/0 0.0.0.0/0
2 26447 3437K prerouting_wan_rule all -- * * 0.0.0.0/0 0.0.0.0/0 /* !fw3: Custom wan prerouting rule chain */
3 6131 1287K DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* !fw3: @redirect[0] */ to:192.168.31.2
4 8294 1764K DNAT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp spt:!67 /* !fw3: @redirect[1] */ to:192.168.31.2
所以需要做的就是在主路由层直接转发 9002 端口的 UDP 请求送到 XBOX 上去,并且必须早于 dmz 规则。
在主路由上执行:
iptables -t nat -I zone_wan_prerouting 1 \
-p udp --dport 9002 \
-j DNAT --to-destination 192.168.31.13:9002
规则就会变成:
root@XiaoQiang:~# iptables -t nat -L zone_wan_prerouting -n -v --line-numbers
Chain zone_wan_prerouting (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 DNAT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:9002 to:192.168.31.13:9002
2 26496 3440K MINIUPNPD all -- * * 0.0.0.0/0 0.0.0.0/0
3 26496 3440K prerouting_wan_rule all -- * * 0.0.0.0/0 0.0.0.0/0 /* !fw3: Custom wan prerouting rule chain */
4 6131 1287K DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* !fw3: @redirect[0] */ to:192.168.31.2
5 8308 1767K DNAT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp spt:!67 /* !fw3: @redirect[1] */ to:192.168.31.2
处理完主路由的问题后,就需要处理 UU 的 Openwrt 了。由于 XBOX 的网关和 DNS 是指向 UU Openwrt 的,而 UU 原本的规则会把 XBOX 的 UDP 流量标记为 0x164:
table 164 / 180
↓
tun164
↓
UU 加速节点
如果不处理,虽然主路由进来的流量是直达 XBOX 的,但是 XBOX 出去的流量会变成 XBOX → UU 节点 → 客户端(XStreaming 之类)。
所以现在要做的就是在 UU Openwrt 上添加一个专门的路由表,首先是最初始的配置。
在 UU Openwrt 上执行:
ip route add table 200 \
192.168.31.0/24 dev br-lan scope link
ip route add table 200 \
default via 192.168.31.1 dev br-lan
这两句的目的是,在这个路由表下破坏原本 UU Openwrt 的网络流向。原本由于 UU Openwrt 的网关是 192.168.31.2,因此任何流量原本都是需要让 UU Openwrt 发送到 旁路由,再由 旁路由 送给 主路由 的。这不符合这个文章想做的精简链路,通过这个规则可以绕过原本的默认设计,让流量直接送主路由(当然,必须用了这个路由表的流量才会直接送主路由)
接下来就是用 nftable 建一个表,并建立 PREROUTING 链,用于给 UDP 9002 的流量打上额外的 mark。
在 UU Openwrt 上执行:
nft add table ip RP_BYPASS
nft 'add chain ip RP_BYPASS prerouting { type filter hook prerouting priority 30000; policy accept; }'
nft add rule ip RP_BYPASS prerouting \
ip saddr 192.168.31.13 \
udp sport 9002 \
counter \
meta mark set 0x9002
UU 在启用加速的时候,会有
PREROUTING priority -199,同时给几乎所有 IP 的流量打上mark 0x164(我这里164大概是因为 XBOX 是第二台加速设备,所以从 163 变成 164)。根据 nf 桌子的设计,优先级数字越小越早用上,所以要做的就是设置一个优先级足够大的规则,让流量即使被打上mark 0x164,最后也会被优先级更大的规则覆盖上mark 0x9002。识别条件设置 IP 为
192.168.31.13,端口为9002的UDP流量,够精准也够用,毕竟不可能拿客户端 IP 当识别条件……
有了这俩之后就是把两者相结合了。
在 UU Openwrt 上执行:
ip rule add priority 1 \
from 192.168.31.13 \
fwmark 0x9002 \
lookup 200
目的是让 Openwrt 知道,假设有流量被标记为 mark 0x9002,那就要查 table 200,也就是我们前面的设计,直接流向 192.168.31.1。
通过查询 UU 加速后的规则可以看到:
32762: from 192.168.31.13 lookup 180
32763: from 192.168.31.13 fwmark 0x164 lookup 164
UU 设置了优先级为 32762 和 32793 的规则(这个优先级并不固定,有时候甚至会有小于 100 的),根据 ip rule 的设计,数字越小优先级越高,所以设置这么一个优先级为 1 的就够了。
如此,XBOX 产生的远程串流流量,就会变成这样:
XBOX 远程串流数据上行
↓
[所在 PREROUTING 链优先级为 -199]
UU 给所有来自 192.168.31.13 的 UDP 目标端口 1025-65535 的流量全打上 mark 0x164,
↓
[所在 PREROUTING 链优先级为 30000](数字越小优先级越高)
重新为 192.168.31.13 的 UDP 源端口 9002 的流量覆盖上 mark 0x9002
↓
开始策略路由
↓
[调整前] UU 的优先级 32762 规则会导致开始查询 table 180,没找到有效路由则再试图匹配优先级 32763 的规则查询 table 164
[调整后] 新的规则优先级为 1,数字越小优先级越高,优先执行
↓
查找 table 200
↓
default via 192.168.31.1
↓
直接去主路由
客户端对公网 IP:9002 发送 UDP 数据
↓
光猫的 WAN 9002 端口接收到数据,根据端口转发规则转发到主路由 WAN 9002 端口
↓
主路由进入 NAT PREROUTING
↓
主路由优先命中新 DNAT 规则,UDP 9002 端口的数据全部直接送 192.168.31.13:9002
↓
XBOX 远程串流数据下行
当前面的配置使得远程串流正常进行之后,下一步就是固定我们的配置了。
在主路由上执行:
cat > /etc/firewall.xbox-rp <<'EOF'
#!/bin/sh
iptables -t nat -D zone_wan_prerouting \
-p udp --dport 9002 \
-j DNAT --to-destination 192.168.31.13:9002 \
2>/dev/null
iptables -t nat -I zone_wan_prerouting 1 \
-p udp --dport 9002 \
-j DNAT --to-destination 192.168.31.13:9002
EOF
chmod +x /etc/firewall.xbox-rp
在主路由上执行:
SEC=$(uci add firewall include)
uci set firewall.$SEC.path='/etc/firewall.xbox-rp'
uci set firewall.$SEC.reload='1'
uci commit firewall
可以重启防火墙验证一下。
在主路由上执行:
/etc/init.d/firewall restart
iptables -t nat -L zone_wan_prerouting -n -v --line-numbers
正常情况下就能看到文章前期的 DNAT 规则了(
然后接着就是 UU 所在的 Openwrt 了:
在 UU Openwrt 上执行:
cat > /etc/firewall.xbox-rp <<'EOF'
#!/bin/sh
ip route replace table 200 \
192.168.31.0/24 dev br-lan scope link
ip route replace table 200 \
default via 192.168.31.1 dev br-lan
ip rule del priority 1 \
from 192.168.31.13 \
fwmark 0x9002 \
lookup 200 \
2>/dev/null
ip rule add priority 1 \
from 192.168.31.13 \
fwmark 0x9002 \
lookup 200
nft delete table ip RP_BYPASS 2>/dev/null
nft add table ip RP_BYPASS
nft 'add chain ip RP_BYPASS prerouting { type filter hook prerouting priority 30000; policy accept; }'
nft add rule ip RP_BYPASS prerouting \
ip saddr 192.168.31.13 \
udp sport 9002 \
counter \
meta mark set 0x9002
EOF
chmod +x /etc/firewall.xbox-rp
在 UU Openwrt 上执行:
SEC=$(uci add firewall include)
uci set firewall.$SEC.type='script'
uci set firewall.$SEC.path='/etc/firewall.xbox-rp'
uci set firewall.$SEC.fw4_compatible='1'
uci commit firewall
可以重启防火墙验证一下。
在 UU Openwrt 上执行:
nft list table ip RP_BYPASS
ip -4 rule show
ip -4 route show table 200
正常情况下能看到我们设置的 RP_BYPASS,策略路由里优先级 1 的规则,以及 table 200 的配置了(

共有 0 条评论