弄了个 wireguard 补丁,求熟悉 Linux kernel 协议栈的大神帮忙完善。。。。

2022-05-31 10:13:32 +08:00
 wacke

如题,补丁主要解决多 wan 下 wireguard 始终只使用最小跃点数的 wan 作为源 ip 的问题,本人不太熟悉 kernel 的网络协议栈,so 写出来的补丁比较 low ,求大神帮忙完善。

具体的始末可参考我在 github 的 issue:https://github.com/openwrt/packages/issues/9538

补丁如下:

diff -uNr linux-5.15.12_orig/drivers/net/wireguard/socket.c linux-5.15.12_wg/drivers/net/wireguard/socket.c
--- linux-5.15.12_orig/drivers/net/wireguard/socket.c	2021-12-29 19:29:03.000000000 +0800
+++ linux-5.15.12_wg/drivers/net/wireguard/socket.c	2022-05-27 15:27:40.000000000 +0800
@@ -17,6 +17,12 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+int send = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -37,6 +43,13 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if ((receive) && (!send) || (send) && (!receive)) {
+		src_addr = dst_addr;
+	}
+	else {
+		src_addr = 0;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -52,9 +65,11 @@
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
 		if (unlikely(!inet_confirm_addr(sock_net(sock), NULL, 0,
 						fl.saddr, RT_SCOPE_HOST))) {
-			endpoint->src4.s_addr = 0;
-			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			endpoint->src4.s_addr = src_addr;
+			endpoint->src_if4 = src_addr;
+			fl.saddr = src_addr;
+			send = 1;
+			receive = 0;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -62,9 +77,11 @@
 		if (unlikely(endpoint->src_if4 && ((IS_ERR(rt) &&
 			     PTR_ERR(rt) == -EINVAL) || (!IS_ERR(rt) &&
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
-			endpoint->src4.s_addr = 0;
-			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			endpoint->src4.s_addr = src_addr;
+			endpoint->src_if4 = src_addr;
+			fl.saddr = src_addr;
+			send = 1;
+			receive = 0;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +94,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if (receive) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +336,11 @@
 static int wg_receive(struct sock *sk, struct sk_buff *skb)
 {
 	struct wg_device *wg;
+	struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);
+
+	dst_addr = ip_header->daddr;
+	receive = 1;
+	send = 0;
 
 	if (unlikely(!sk))
 		goto err;

补丁主要的核心就是通过读取 kernel skb_buff 中的目的地址,并把该地址应用到 wireguard 的源地址上,补丁我自己测试貌似没啥大问题,只是考虑到我本人的编程水平,求大神帮忙完善。

ps:wireguard 官方貌似不认为这是 bug 。。。

4444 次点击
所在节点    Linux
44 条回复
qakito
2022-06-06 23:45:17 +08:00
@wacke
如果要改代码,最简单的方式就是把 wg_socket_endpoint_from_skb 函数里的 endpoint->src_if4 = skb->skb_iif 这句注掉,这样就可以跳过源地址重新选择的流程
wacke
2022-06-07 08:21:32 +08:00
@qakito
首先非常感谢你的回复,作为一个差不多将编程知识都还给老师的小白,要弄懂 kernel 协议栈,实在有点困难。。。。

1.我要实现的目标是 wireguard 作为服务端在 openwrt 里实现多 pppoe 的情况下,客户端可以任意连接一个 pppoe 实现接入。
2.目前在 openwrt+mwan3 的情况下,fwmark 标记入口数据包无效,手动指定 fwmark 则仅能实现 wireguard 固定选择多个 pppoe 中的一个作为默认路由。
3.从我 github 的 issue 以及 openwrt 论坛的部分讨论帖子来看,这个问题不仅仅是 openwrt+mwan3 特有的,邮件列表里也有很多提交给 wireguard 官方案例,但 wireguard 官方都是没有下文。。。。基于此(主要是补丁肯定很 low ),我也不可能将我的修改提交给 wireguard 。
4.稍晚点我尝试注释 endpoint->src_if4 = skb->skb_iif 这句试试吧。
wacke
2022-06-07 19:53:54 +08:00
@qakito 已尝试删除 endpoint->src_if4 = skb->skb_iif ,重新编译 openwrt 后,测试无效。。。从最小跃点数的 wan 连入工作正常,其他就无法握手了。。。
wind333
345 天前
你好请问问题解决了吗,我的 openwrt wiregurad 多 wan 口也遇到这个问题了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/856378

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX