Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hnat导致wg/gre over ipv6转发速度极慢 #9

Closed
ru67tm opened this issue Nov 27, 2022 · 13 comments
Closed

hnat导致wg/gre over ipv6转发速度极慢 #9

ru67tm opened this issue Nov 27, 2022 · 13 comments

Comments

@ru67tm
Copy link

ru67tm commented Nov 27, 2022

大致网络拓扑:
PC A====Router A==wg/gre over ipv6==Route B====PC B
192.168.0.0/24 172.16.0.0/24 192.168.1.0/24

由PC A访问PC B的速度只有2~10Mbps,但是使用wireguard over ipv4时网速正常。此前使用mt7621时无此问题。
通过‘echo 0 > /sys/kernel/debug/hnat/hook_toggle’关闭hnat后速度恢复正常。

@hcym
Copy link

hcym commented Nov 27, 2022

大雕那边也是,稀奇古怪的加速能关就关,最好的还是官方的加上插件

@ru67tm
Copy link
Author

ru67tm commented Nov 27, 2022

试试 5g 设备之间互传 速度怎么样

内网各种速度都很正常,就是ipv6隧道不太行

@padavanonly
Copy link
Contributor

大雕那边也是,稀奇古怪的加速能关就关,最好的还是官方的加上插件

觉得官方好那你去用官方呗,跑这里ky很好玩?

@paldier
Copy link

paldier commented Nov 28, 2022

wg和硬nat是冲突的,只能二选一,无解

@ru67tm
Copy link
Author

ru67tm commented Nov 29, 2022

wg和硬nat是冲突的,只能二选一,无解

我在主楼也提到了,开启hnat时,走ipv4的wg速度是完全正常的,只有ipv6上的wg(以及gre、l2tp等)速度有问题

@hanwckf
Copy link
Owner

hanwckf commented Dec 5, 2022

wg和硬nat是冲突的,只能二选一,无解

我在主楼也提到了,开启hnat时,走ipv4的wg速度是完全正常的,只有ipv6上的wg(以及gre、l2tp等)速度有问题

后续将参考小米原厂固件做一个ipv6 hook的开关,可以单独控制v6 hnat加速功能

@1715173329
Copy link
Contributor

后续将参考小米原厂固件做一个ipv6 hook的开关,可以单独控制v6 hnat加速功能

有具体接口吗?hook_toggle 这个好像是全局的,目前 turboacc 已支持配置。

@ubitsp
Copy link

ubitsp commented Dec 26, 2022

划分多个vlan转发到wan也会出现这个bug, 我实测下载2mbps 上传10mbps 关掉硬件加速之后就正常 , 不知道啥原因了

@ftdlyc
Copy link

ftdlyc commented Oct 4, 2023

研究了一下,hnat + wireguard + ipv6会导致client端iperf udp dl每秒只有30pps,从抓包来看,client的网卡每秒是能收到几百pps的,但其中有大部分的包wireguard协议头前12字节会被替换成src mac和dst mac,因此这些包都会解包错误而被丢弃,丢包率90+%

从wireguard代码来看

// ./drivers/net/wireguard/send.c

void wg_packet_encrypt_worker(struct work_struct *work)
{
// ....
		if (likely(encrypt_packet(skb,
				PACKET_CB(first)->keypair))) {
			// 这里会把skb->mac_header指向wireguard协议头
			// 然后调用udp_tunnel6_xmit_skb往wan接口发送这个skb
			wg_reset_packet(skb, true);
		} else {
			state = PACKET_STATE_DEAD;
			break;
		}
}

再看hnat的代码

// ./drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c

static unsigned int mtk_hnat_nf_post_routing(
	struct sk_buff *skb, const struct net_device *out,
	unsigned int (*fn)(struct sk_buff *, const struct net_device *,
			   struct flow_offload_hw_path *),
	const char *func)
{
	// ....

	// 这里会判断skb->mac_header是否有效
	// 此时mac_header是指向wireguard协议头的位置,所以是有效的
	if (unlikely(!skb_mac_header_was_set(skb)))
		return 0;

	// ....

	switch (skb_hnat_reason(skb)) {
	case HIT_UNBIND_RATE_REACH:
		if (entry_hnat_is_bound(entry))
			break;

		if (fn && !mtk_hnat_accel_type(skb))
			break;

		// 每秒大于30pps后,后续的包会走到这里,ipv6 skb的fn指向hnat_ipv6_get_nexthop函数
		if (fn && fn(skb, arp_dev, &hw_path))
			break;

		skb_to_hnat_info(skb, out, entry, &hw_path);
		break;
	// .....
}

static unsigned int hnat_ipv6_get_nexthop(struct sk_buff *skb,
					  const struct net_device *out,
					  struct flow_offload_hw_path *hw_path)
{

	// 这里会往skb->mac_header指向的地址里填充src mac和dst mac
	// 导致client端收到的wireguard包是错的
	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) {
		memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN);
		memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN);
		return 0;
	}

	// ....
}

看起来是一个bug,对hnat并不太了解,不知道是wireguard内核代码的问题,还是hnat代码的问题
按我的理解来看,wireguard驱动wg_packet_encrypt_worker函数那里是不是得把mac_header置为无效的?因为这个时候还没填充mac地址
或者是hnat驱动mtk_hnat_nf_post_routing里是不是得判断下mac_header是否小于network_header,不是的话就认为mac_header非法,直接返回

按照第一个思路,对wireguard代码做如下改动,测试了下,速率就正常了,但不知道有没有什么风险

if (likely(encrypt_packet(skb,
		PACKET_CB(first)->keypair))) {
	wg_reset_packet(skb, true);

	// yc.lu add for coexistence with mtkhat
	skb_unset_mac_header(skb);
	skb_reset_inner_headers(skb);
}

@paldier
Copy link

paldier commented Oct 4, 2023

// 这里会往skb->mac_header指向的地址里填充src mac和dst mac
	// 导致client端收到的wireguard包是错的

还是给hnat加上wg包识别比较好,各家都是自己处理硬nat和wg的兼容问题,不应该去修改wg代码

@hanwckf
Copy link
Owner

hanwckf commented Oct 5, 2023

研究了一下,hnat + wireguard + ipv6会导致client端iperf udp dl每秒只有30pps,从抓包来看,client的网卡每秒是能收到几百pps的,但其中有大部分的包wireguard协议头前12字节会被替换成src mac和dst mac,因此这些包都会解包错误而被丢弃,丢包率90+%

从wireguard代码来看

// ./drivers/net/wireguard/send.c

void wg_packet_encrypt_worker(struct work_struct *work)
{
// ....
		if (likely(encrypt_packet(skb,
				PACKET_CB(first)->keypair))) {
			// 这里会把skb->mac_header指向wireguard协议头
			// 然后调用udp_tunnel6_xmit_skb往wan接口发送这个skb
			wg_reset_packet(skb, true);
		} else {
			state = PACKET_STATE_DEAD;
			break;
		}
}

再看hnat的代码

// ./drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c

static unsigned int mtk_hnat_nf_post_routing(
	struct sk_buff *skb, const struct net_device *out,
	unsigned int (*fn)(struct sk_buff *, const struct net_device *,
			   struct flow_offload_hw_path *),
	const char *func)
{
	// ....

	// 这里会判断skb->mac_header是否有效
	// 此时mac_header是指向wireguard协议头的位置,所以是有效的
	if (unlikely(!skb_mac_header_was_set(skb)))
		return 0;

	// ....

	switch (skb_hnat_reason(skb)) {
	case HIT_UNBIND_RATE_REACH:
		if (entry_hnat_is_bound(entry))
			break;

		if (fn && !mtk_hnat_accel_type(skb))
			break;

		// 每秒大于30pps后,后续的包会走到这里,ipv6 skb的fn指向hnat_ipv6_get_nexthop函数
		if (fn && fn(skb, arp_dev, &hw_path))
			break;

		skb_to_hnat_info(skb, out, entry, &hw_path);
		break;
	// .....
}

static unsigned int hnat_ipv6_get_nexthop(struct sk_buff *skb,
					  const struct net_device *out,
					  struct flow_offload_hw_path *hw_path)
{

	// 这里会往skb->mac_header指向的地址里填充src mac和dst mac
	// 导致client端收到的wireguard包是错的
	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) {
		memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN);
		memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN);
		return 0;
	}

	// ....
}

看起来是一个bug,对hnat并不太了解,不知道是wireguard内核代码的问题,还是hnat代码的问题 按我的理解来看,wireguard驱动wg_packet_encrypt_worker函数那里是不是得把mac_header置为无效的?因为这个时候还没填充mac地址 或者是hnat驱动mtk_hnat_nf_post_routing里是不是得判断下mac_header是否小于network_header,不是的话就认为mac_header非法,直接返回

按照第一个思路,对wireguard代码做如下改动,测试了下,速率就正常了,但不知道有没有什么风险

if (likely(encrypt_packet(skb,
		PACKET_CB(first)->keypair))) {
	wg_reset_packet(skb, true);

	// yc.lu add for coexistence with mtkhat
	skb_unset_mac_header(skb);
	skb_reset_inner_headers(skb);
}

@ftdlyc 可以尝试下面这样修改hnat驱动。我这边没有wgv6的测试环境,烦请测试下能否正常使用

--- hnat_nf_hook-orig.c 2023-10-05 14:58:07.361987421 +0000
+++ hnat_nf_hook.c      2023-10-05 15:05:07.023465414 +0000
@@ -2159,9 +2159,15 @@ mtk_hnat_ipv6_nf_local_out(void *priv, s
        if (unlikely(!skb_hnat_is_hashed(skb)))
                return NF_ACCEPT;

+       ip6h = ipv6_hdr(skb);
+
+       if (ip6h->nexthdr != NEXTHDR_IPIP) {
+               hnat_set_head_frags(state, skb, 1, hnat_set_alg);
+               return NF_ACCEPT;
+       }
+
        entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
        if (skb_hnat_reason(skb) == HIT_UNBIND_RATE_REACH) {
-               ip6h = ipv6_hdr(skb);
                if (ip6h->nexthdr == NEXTHDR_IPIP) {
                        /* Map-E LAN->WAN: need to record orig info before fn. */
                        if (mape_toggle) {

@ftdlyc
Copy link

ftdlyc commented Oct 5, 2023

@hanwckf 测试了下你的修改,速率正常,能解决问题

@hanwckf
Copy link
Owner

hanwckf commented Oct 6, 2023

@hanwckf 测试了下你的修改,速率正常,能解决问题

好的,晚点改上去

@hanwckf hanwckf closed this as completed in bacf916 Oct 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants