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

wireguard can't handshake with mwan3 #9538

Closed
wackejohn opened this issue Jul 22, 2019 · 90 comments
Closed

wireguard can't handshake with mwan3 #9538

wackejohn opened this issue Jul 22, 2019 · 90 comments
Assignees

Comments

@wackejohn
Copy link

wackejohn commented Jul 22, 2019

Maintainer: @feckert

Environment:

OpenWrt SNAPSHOT, r10551-d616b2c906
MWAN3 	2.8.0-1
Running on turris omnia with four pppoe wan interface,two wireguard vpn client and one wireguard vpn server.

Description:

When I using the OpenWRT as a wireguard server with multi pppoe wan interface, the wireguard 
client failed handshake. And I found that the inbound interface and outbound interface was different.
eg: My wireguard client connect the wireguard server from pppoe-wan4, but the handshake data was sent from pppoe-wan1 to the client.

The tcpdump output:

root@HOME-Router:~# tcpdump -i pppoe-wan4 -vv host  49.94.153.160
tcpdump: listening on pppoe-wan4, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
21:40:42.419873 IP (tos 0x0, ttl 113, id 5756, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:40:47.635469 IP (tos 0x0, ttl 113, id 5757, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:40:52.534148 IP (tos 0x0, ttl 113, id 5758, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:40:57.697818 IP (tos 0x0, ttl 113, id 5759, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:02.728237 IP (tos 0x0, ttl 113, id 5760, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:07.937823 IP (tos 0x0, ttl 113, id 5761, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:13.142458 IP (tos 0x0, ttl 113, id 5762, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:18.267116 IP (tos 0x0, ttl 113, id 5763, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:23.496362 IP (tos 0x0, ttl 113, id 5764, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:28.698172 IP (tos 0x0, ttl 113, id 5765, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:33.800883 IP (tos 0x0, ttl 113, id 5766, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:38.899820 IP (tos 0x0, ttl 113, id 5767, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:43.998023 IP (tos 0x0, ttl 113, id 5768, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:49.254995 IP (tos 0x0, ttl 113, id 5769, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:54.349569 IP (tos 0x0, ttl 113, id 5770, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:41:59.327974 IP (tos 0x0, ttl 113, id 5771, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:42:04.368634 IP (tos 0x0, ttl 113, id 5772, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
21:42:09.491494 IP (tos 0x0, ttl 113, id 5773, offset 0, flags [none], proto UDP (17), length 176)
    49.94.153.160.12572 > 112.3.86.249.5888: [udp sum ok] UDP, length 148
^C
18 packets captured
18 packets received by filter
0 packets dropped by kernel
root@HOME-Router:~# tcpdump -i pppoe-wan1 -vv host  49.94.153.160
tcpdump: listening on pppoe-wan1, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
21:40:42.421591 IP (tos 0x88, ttl 64, id 1206, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:40:47.637268 IP (tos 0x88, ttl 64, id 1711, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:40:52.535810 IP (tos 0x88, ttl 64, id 1987, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:40:57.699682 IP (tos 0x88, ttl 64, id 2312, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:02.729908 IP (tos 0x88, ttl 64, id 2638, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:07.939552 IP (tos 0x88, ttl 64, id 2713, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:13.144141 IP (tos 0x88, ttl 64, id 3211, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:18.268852 IP (tos 0x88, ttl 64, id 3683, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:23.498018 IP (tos 0x88, ttl 64, id 3996, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:23.533291 IP (tos 0x0, ttl 55, id 7594, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 3996, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:28.699810 IP (tos 0x88, ttl 64, id 4312, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:28.728377 IP (tos 0x0, ttl 55, id 7901, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 4312, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:33.802574 IP (tos 0x88, ttl 64, id 4486, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:33.841978 IP (tos 0x0, ttl 55, id 8313, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 4486, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:38.901612 IP (tos 0x88, ttl 64, id 4495, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:38.931917 IP (tos 0x0, ttl 55, id 8498, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 4495, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:43.999737 IP (tos 0x88, ttl 64, id 4549, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:44.025151 IP (tos 0x0, ttl 55, id 8577, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 4549, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:49.256834 IP (tos 0x88, ttl 64, id 4879, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:49.285795 IP (tos 0x0, ttl 55, id 8663, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 4879, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:54.351222 IP (tos 0x88, ttl 64, id 5110, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:54.395633 IP (tos 0x0, ttl 55, id 8927, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 5110, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:59.329684 IP (tos 0x88, ttl 64, id 5361, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:41:59.368025 IP (tos 0x0, ttl 55, id 9212, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 5361, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:42:04.370263 IP (tos 0x88, ttl 64, id 5540, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:42:04.409760 IP (tos 0x0, ttl 55, id 9586, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 5540, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:42:09.493346 IP (tos 0x88, ttl 64, id 5932, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92
21:42:09.532424 IP (tos 0x0, ttl 55, id 9895, offset 0, flags [none], proto ICMP (1), length 148)
    49.94.153.160 > 114.228.200.208: ICMP 49.94.153.160 udp port 12572 unreachable, length 128
        IP (tos 0x0, ttl 55, id 5932, offset 0, flags [none], proto UDP (17), length 120)
    114.228.200.208.5888 > 49.94.153.160.12572: [udp sum ok] UDP, length 92

The output showed that my client(49.94.153.160) connect server (112.3.86.249:5888), but the handshake data was sent from (114.228.200.208). Maybe it's because the wireguard can't bind to specific interface?

@wackejohn wackejohn changed the title wireguard can wireguard can't handshake with mwan3 Jul 22, 2019
@lucize
Copy link
Contributor

lucize commented Jul 22, 2019

did you tried to add a rule so a specific port/udp to only use one interface ? you can find it in web interface

@feckert feckert self-assigned this Jul 22, 2019
@wackejohn
Copy link
Author

wackejohn commented Jul 23, 2019

did you tried to add a rule so a specific port/udp to only use one interface ? you can find it in web interface

The wireguard itself can't bind to specific interface, and I've tried to specific 5888 port only use one interface, but it didn't work.

@feckert
Copy link
Member

feckert commented Aug 13, 2019

This is because the wireguard runs directly on the router. For outgoing router traffic the nat prerouting hook is not passed through by the firewall, because it does not exist! Only for forwarded traffic. So a rule for on mwan3 for router traffic make no sense.
See:
https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

@wackejohn
Copy link
Author

@feckert

This is because the wireguard runs directly on the router. For outgoing router traffic the nat prerouting hook is not passed through by the firewall, because it does not exist! Only for forwarded traffic. So a rule for on mwan3 for router traffic make no sense.
See:
https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

So there is no way to get wireguard working with mwan3?

@yousong
Copy link
Member

yousong commented Aug 14, 2019

My guess is that wireguard kernel module selects a source address to use after looking up routes. It does not record which dst addr it previously received peer traffic and send response from that address. If that's what happened, try to connect to the server by address 114.228.200.208

@wackejohn
Copy link
Author

@yousong

My guess is that wireguard kernel module selects a source address to use after looking up routes. It does not record which dst addr it previously received peer traffic and send response from that address. If that's what happened, try to connect to the server by address 114.228.200.208

The 114.228.200.208 was my wan1 ip address, I've tested that the wireguard only working with wan1.
PS: Are you chinese?

@yousong
Copy link
Member

yousong commented Aug 14, 2019

@yousong

My guess is that wireguard kernel module selects a source address to use after looking up routes. It does not record which dst addr it previously received peer traffic and send response from that address. If that's what happened, try to connect to the server by address 114.228.200.208

The 114.228.200.208 was my wan1 ip address, I've tested that the wireguard only working
with wan1.

Then very likely my guess is right ;) Note that the use of udp as transport plays a role here. Many user space services use IP_PKTINFO for fetching the local address it received the msg and send response from the same address.

The issue will need to be handled by the wireguard kernel module.

PS: Are you chinese?

Yes, I am ;)

@wackejohn
Copy link
Author

@yousong
So you meen that I should report this to the wireguard team?
PS: I'm chinese too, so may I get your qq number or email address? Thanks.

@yousong
Copy link
Member

yousong commented Aug 14, 2019

After searching it a bit more, I found that WireGuard designers already took the issue into consideration [1]. There should be other causes.

[1] see the slide about "Sticky Sockets", https://www.wireguard.com/talks/eindhoven2018-slides.pdf

EDIT: my email address should be available in my github profile page, or the git log

@yousong
Copy link
Member

yousong commented Aug 14, 2019

Maybe try adding a source based policy route rule

ip rule add from 112.3.86.249 lookup N

where N should be the route table number corresponding to pppoe-wan1 as setup by mwan3.

@wackejohn
Copy link
Author

Maybe try adding a source based policy route rule

ip rule add from 112.3.86.249 lookup N

where N should be the route table number corresponding to pppoe-wan1 as setup by mwan3.

Tried this, but it wasn't working. I think wireguard just use the main routing table.

@yousong
Copy link
Member

yousong commented Aug 15, 2019

It's generic linux route lookup. It should work.

Just to be sure, please share the output of the following commands

ip rule
ip route show table N

@wackejohn
Copy link
Author

@yousong
I want to use wireguard as server on pppoe-wan4, so the output:

root@HOME-Router:~# ip rule
0:      from all lookup local
1001:   from all iif pppoe-wan1 lookup 1
1002:   from all iif pppoe-wan2 lookup 2
1003:   from all iif pppoe-wan3 lookup 3
1004:   from all iif pppoe-wan4 lookup 4
1005:   from all iif wana lookup 5
1006:   from all iif wanb lookup 6
2001:   from all fwmark 0x100/0xff00 lookup 1
2002:   from all fwmark 0x200/0xff00 lookup 2
2003:   from all fwmark 0x300/0xff00 lookup 3
2004:   from all fwmark 0x400/0xff00 lookup 4
2005:   from all fwmark 0x500/0xff00 lookup 5
2006:   from all fwmark 0x600/0xff00 lookup 6
2253:   from all fwmark 0xfd00/0xff00 blackhole
2254:   from all fwmark 0xfe00/0xff00 unreachable
32766:  from all lookup main
32767:  from all lookup default
root@HOME-Router:~# ip route show table 4
default via 112.1.96.1 dev pppoe-wan4 metric 21 
112.1.96.1 dev pppoe-wan4 proto kernel scope link src 112.1.99.41 
114.226.46.1 dev pppoe-wan1 proto kernel scope link src 114.226.46.109 
114.228.157.1 dev pppoe-wan2 proto kernel scope link src 114.228.157.158 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 
192.168.120.0/24 dev wana proto static scope link metric 30 
192.168.150.0/24 dev wanb proto static scope link metric 40 
192.168.195.0/24 dev vpn1 proto kernel scope link src 192.168.195.1 
192.168.195.4 dev vpn1 proto static scope link 
192.168.196.0/24 dev tun0 proto kernel scope link src 192.168.196.1 
223.66.36.1 dev pppoe-wan3 proto kernel scope link src 223.66.36.116 

@yousong
Copy link
Member

yousong commented Aug 15, 2019

Hmm, I cannot see where the problem is. The from x.x.x.x lookup N rule should work.

I am curious and had an experiment [1]. Hope it helps.

[1] https://gist.github.com/yousong/e6ebd3c9f838286d6fda3228655c1f90

@wackejohn
Copy link
Author

Hmm, I cannot see where the problem is. The from x.x.x.x lookup N rule should work.

I am curious and had an experiment [1]. Hope it helps.

[1] https://gist.github.com/yousong/e6ebd3c9f838286d6fda3228655c1f90

I think it won't work, in my opinion the source address is fixed by the wireguard when wireguard reply the handshake data, the from x.x.x.x lookup N rule won't change the source address. And that's why the openvpn should listen on lan ip to work with mwan3.

@wackejohn
Copy link
Author

My wireguard config:

root@HOME-Router:/etc/config# wg showconf vpn1
[Interface]
ListenPort = 5888
PrivateKey = XXFzF4RpuJyzv8LLl4jyF8YgkC+11odAEM18QrAOhEs=

[Peer]
PublicKey = XXz3RNu3kvuQkrjNF7MJOHdABwvfwJFNDfD3ul54FR8=
PresharedKey = XX+BdnxHq+up3NIGFCh5tf33m8w+7DScUDrHBKzLrsY=
AllowedIPs = 192.168.195.3/32, dd5d:189b:b5f9:3::3/128
PersistentKeepalive = 25

[Peer]
PublicKey = XXQ3aVzXp2PKObEInwZ1+v4w0mCHsjDQK/6onPLwxUk=
PresharedKey = XXpDE20HLOU2lRwUD0Dfu8H3U4a6XVI1e5s7mziXvyU=
AllowedIPs = 192.168.195.2/32, dd5d:189b:b5f9:3::2/128
PersistentKeepalive = 25

[Peer]
PublicKey = XXJHl6mKCkh+++pWYRVxHkSntaKnZAUuwQsgGDCEuDM=
PresharedKey = XXpDE20HLOU2lRwUD0Dfu8H3U4a6XVI1e5s7mziXvyU=
AllowedIPs = 192.168.195.4/32, dd5d:189b:b5f9:3::4/128

@jkilpatr
Copy link

And the server side configuration?

@wackejohn
Copy link
Author

And the server side configuration?

This is the server side configuration...

@fox85
Copy link

fox85 commented Sep 2, 2019

And the server side configuration?

This is the server side configuration...

Hello, wackejohn, 我遇到与你同样的issue发生在使用UDP协议上的OpenVPN。
我发现该问题产生的条件是在同时跑mwan3和监听着UDP端口的程序上。
我试着把OpenVPN换成TCP协议,问题就消失,数据就能从指定的wan口进出。
但一旦转换成UDP协议,数据就会从wan2入,wan1出。
我试了mwan3-2.17.3 和 2.8.0-2都依旧。

@wackejohn
Copy link
Author

And the server side configuration?

This is the server side configuration...

Hello, wackejohn, 我遇到与你同样的issue发生在使用UDP协议上的OpenVPN。
我发现该问题产生的条件是在同时跑mwan3和监听着UDP端口的程序上。
我试着把OpenVPN换成TCP协议,问题就消失,数据就能从指定的wan口进出。
但一旦转换成UDP协议,数据就会从wan2入,wan1出。
我试了mwan3-2.17.3 和 2.8.0-2都依旧。

配置OpenVPN监听在lan口,该问题就解决了,wireguard目前只能监听在0.0.0.0,所以目前来说似乎无解。。。

mwan3官方文档:https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3#openvpn

@fox85
Copy link

fox85 commented Sep 3, 2019

And the server side configuration?

This is the server side configuration...

Hello, wackejohn, 我遇到与你同样的issue发生在使用UDP协议上的OpenVPN。
我发现该问题产生的条件是在同时跑mwan3和监听着UDP端口的程序上。
我试着把OpenVPN换成TCP协议,问题就消失,数据就能从指定的wan口进出。
但一旦转换成UDP协议,数据就会从wan2入,wan1出。
我试了mwan3-2.17.3 和 2.8.0-2都依旧。

配置OpenVPN监听在lan口,该问题就解决了,wireguard目前只能监听在0.0.0.0,所以目前来说似乎无解。。。

mwan3官方文档:https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3#openvpn

非常感谢你给出的指引。
我调整了OpenVPN的配置,bind在local lan地址上,然后在防火墙做端口转发,这样就能从WAN2入,WAN2出了。
同样地我也调整了shadowsocks server bind lan ip的配置,和在防火墙做端口转发,也能OK了。

@feckert
Copy link
Member

feckert commented Sep 3, 2019

@fox85 @wackejohn
Come on please talk in English!!

@wackejohn
Copy link
Author

@feckert
Sorry, @fox85 got the similar issue on OpenVPN and mwan3, and I told him that OpenVPN should bind to lan and then the issue would gone ...

@fox85
Copy link

fox85 commented Sep 3, 2019

@fox85 @wackejohn
Come on please talk in English!!

Sorry. @feckert , thanks @wackejohn has translated my issue to english. :)

@feckert
Copy link
Member

feckert commented May 29, 2020

@wackejohn @fox85 If the problem persists please reopen the ticket

@feckert feckert closed this as completed May 29, 2020
@psergei
Copy link

psergei commented Nov 28, 2020

I had the same issue with my mwan3 config balancing 2 WAN with 50/50 rule.
This is how I solved it

iptables -t mangle -I mwan3_hook 2 -p udp --dport <your_wireguard_port> -j MARK --set-mark 0x100

Please put corresponding ifmark for your wireguard listening WAN interface.
I added this script to /etc/mwan2.user

if [ "$ACTION" = "ifup" ]; then
	# add Wireguard port rule if not exists already
	if ! iptables -t mangle -S mwan3_hook | grep 9999 &> /dev/null; then
		iptables -t mangle -I mwan3_hook 2 -p udp --dport 9999 -j MARK --set-mark 0x100
	fi
fi

As I have traced the packets, issue happens on PREROUNTING -> wman3_hook chain. Incoming handshake packet from WAN1 came to the router and wman decided to mark it with 0x100 or 0x200 - after that, the whole connection will have same mark.

@wackejohn
Copy link
Author

@psergei
I've tried your script, but did not work. As I posted before, my issue is that the wireguard server always choose the lowest metric wan interface (in my config, wan1 had the lowest metric) to reply the handshake data, no matter where the handshake data come from (just like the openvpn listen on 0.0.0.0, and with mwan3, it should listen on lan), but the wireguard was designed to always listen on 0.0.0.0.

@theAeon
Copy link

theAeon commented Jan 7, 2022 via email

@wackejohn
Copy link
Author

Tried to add a switch for saving the fwmark, or it will break the wireguard outgoing rule configured by mwan3.

wireguard_fwmark_hack.patch :

diff -uNr linux-5.15.12_orig/drivers/net/wireguard/device.h linux-5.15.12/drivers/net/wireguard/device.h
--- linux-5.15.12_orig/drivers/net/wireguard/device.h	2021-12-29 19:29:03.000000000 +0800
+++ linux-5.15.12/drivers/net/wireguard/device.h	2022-01-05 13:32:02.000000000 +0800
@@ -53,6 +53,7 @@
 	atomic_t handshake_queue_len;
 	unsigned int num_peers, device_update_gen;
 	u32 fwmark;
+	u16 savemark;
 	u16 incoming_port;
 };
 
diff -uNr linux-5.15.12_orig/drivers/net/wireguard/netlink.c linux-5.15.12/drivers/net/wireguard/netlink.c
--- linux-5.15.12_orig/drivers/net/wireguard/netlink.c	2021-12-29 19:29:03.000000000 +0800
+++ linux-5.15.12/drivers/net/wireguard/netlink.c	2022-01-05 13:32:55.000000000 +0800
@@ -27,6 +27,7 @@
 	[WGDEVICE_A_FLAGS]		= { .type = NLA_U32 },
 	[WGDEVICE_A_LISTEN_PORT]	= { .type = NLA_U16 },
 	[WGDEVICE_A_FWMARK]		= { .type = NLA_U32 },
+	[WGDEVICE_A_SAVEMARK]		= { .type = NLA_U16 },
 	[WGDEVICE_A_PEERS]		= { .type = NLA_NESTED }
 };
 
@@ -232,6 +233,7 @@
 		if (nla_put_u16(skb, WGDEVICE_A_LISTEN_PORT,
 				wg->incoming_port) ||
 		    nla_put_u32(skb, WGDEVICE_A_FWMARK, wg->fwmark) ||
+		    nla_put_u16(skb, WGDEVICE_A_SAVEMARK, wg->savemark) ||
 		    nla_put_u32(skb, WGDEVICE_A_IFINDEX, wg->dev->ifindex) ||
 		    nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name))
 			goto out;
@@ -531,6 +533,10 @@
 			wg_socket_clear_peer_endpoint_src(peer);
 	}
 
+	if (info->attrs[WGDEVICE_A_SAVEMARK]) {
+		wg->savemark = nla_get_u16(info->attrs[WGDEVICE_A_SAVEMARK]);
+	}
+
 	if (info->attrs[WGDEVICE_A_LISTEN_PORT]) {
 		ret = set_port(wg,
 			nla_get_u16(info->attrs[WGDEVICE_A_LISTEN_PORT]));
diff -uNr linux-5.15.12_orig/drivers/net/wireguard/socket.c linux-5.15.12/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/drivers/net/wireguard/socket.c	2022-01-05 13:30:08.000000000 +0800
@@ -20,11 +20,24 @@
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
+	u32 fwmark;
+	if (!wg->fwmark) {
+		if (!wg->savemark) {
+			fwmark = skb->mark;
+		}
+		else {
+			fwmark = mark;
+		}
+	}
+	else {
+		fwmark = wg->fwmark;
+	}
+
 	struct flowi4 fl = {
 		.saddr = endpoint->src4.s_addr,
 		.daddr = endpoint->addr4.sin_addr.s_addr,
 		.fl4_dport = endpoint->addr4.sin_port,
-		.flowi4_mark = wg->fwmark,
+		.flowi4_mark = fwmark,
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
@@ -33,7 +46,7 @@
 
 	skb_mark_not_on_list(skb);
 	skb->dev = wg->dev;
-	skb->mark = wg->fwmark;
+	skb->mark = fwmark;
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
@@ -98,11 +111,24 @@
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
 #if IS_ENABLED(CONFIG_IPV6)
+	u32 fwmark;
+	if (!wg->fwmark) {
+		if (!wg->savemark) {
+			fwmark = skb->mark;
+		}
+		else {
+			fwmark = mark;
+		}
+	}
+	else {
+		fwmark = wg->fwmark;
+	}
+
 	struct flowi6 fl = {
 		.saddr = endpoint->src6,
 		.daddr = endpoint->addr6.sin6_addr,
 		.fl6_dport = endpoint->addr6.sin6_port,
-		.flowi6_mark = wg->fwmark,
+		.flowi6_mark = fwmark,
 		.flowi6_oif = endpoint->addr6.sin6_scope_id,
 		.flowi6_proto = IPPROTO_UDP
 		/* TODO: addr->sin6_flowinfo */
@@ -113,7 +139,7 @@
 
 	skb_mark_not_on_list(skb);
 	skb->dev = wg->dev;
-	skb->mark = wg->fwmark;
+	skb->mark = fwmark;
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock6);
@@ -321,6 +347,7 @@
 	wg = sk->sk_user_data;
 	if (unlikely(!wg))
 		goto err;
+	mark = skb->mark;
 	skb_mark_not_on_list(skb);
 	wg_packet_receive(wg, skb);
 	return 0;
diff -uNr linux-5.15.12_orig/drivers/net/wireguard/socket.h linux-5.15.12/drivers/net/wireguard/socket.h
--- linux-5.15.12_orig/drivers/net/wireguard/socket.h	2021-12-29 19:29:03.000000000 +0800
+++ linux-5.15.12/drivers/net/wireguard/socket.h	2022-01-05 13:30:38.000000000 +0800
@@ -30,6 +30,8 @@
 					  const struct sk_buff *skb);
 void wg_socket_clear_peer_endpoint_src(struct wg_peer *peer);
 
+static u32 mark;
+
 #if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
 #define net_dbg_skb_ratelimited(fmt, dev, skb, ...) do {                       \
 		struct endpoint __endpoint;                                    \
diff -uNr linux-5.15.12_orig/include/uapi/linux/wireguard.h linux-5.15.12/include/uapi/linux/wireguard.h
--- linux-5.15.12_orig/include/uapi/linux/wireguard.h	2021-12-29 19:29:03.000000000 +0800
+++ linux-5.15.12/include/uapi/linux/wireguard.h	2022-01-05 13:37:11.000000000 +0800
@@ -29,6 +29,7 @@
  *    WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
  *    WGDEVICE_A_LISTEN_PORT: NLA_U16
  *    WGDEVICE_A_FWMARK: NLA_U32
+ *    WGDEVICE_A_SAVEMARK: NLA_U16
  *    WGDEVICE_A_PEERS: NLA_NESTED
  *        0: NLA_NESTED
  *            WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
@@ -83,6 +84,7 @@
  *    WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
  *    WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
  *    WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ *    WGDEVICE_A_SAVEMARK: NLA_U16, 0 to disable
  *    WGDEVICE_A_PEERS: NLA_NESTED
  *        0: NLA_NESTED
  *            WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
@@ -156,6 +158,7 @@
 	WGDEVICE_A_FLAGS,
 	WGDEVICE_A_LISTEN_PORT,
 	WGDEVICE_A_FWMARK,
+	WGDEVICE_A_SAVEMARK,
 	WGDEVICE_A_PEERS,
 	__WGDEVICE_A_LAST
 };

And wireguard-tools also need to patch, wireguard-tools-with-savemark-hack.patch :

diff -uNr wireguard-tools_orig/src/config.c wireguard-tools/src/config.c
--- wireguard-tools_orig/src/config.c	2021-11-16 10:15:23.428374291 +0800
+++ wireguard-tools/src/config.c	2022-01-08 14:13:48.253563197 +0800
@@ -104,6 +104,24 @@
 	return false;
 }
 
+static inline bool parse_savemark(uint16_t *savemark, uint32_t *flags, const char *value)
+{
+	if (strcasecmp(value, "0") == 0) {
+		*savemark = 0;
+		*flags |= WGDEVICE_HAS_SAVEMARK;
+		return true;
+	}
+	else if (strcasecmp(value, "1") == 0) {
+		*savemark = 1;
+		*flags |= WGDEVICE_HAS_SAVEMARK;
+		return true;
+	}
+	else {
+		fprintf(stderr, "Savemark is neither 0 nor 1 : `%s'\n", value);
+		return false;
+	}
+}
+
 static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
 {
 	if (!key_from_base64(key, value)) {
@@ -446,6 +464,8 @@
 			ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
 		else if (key_match("FwMark"))
 			ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
+		else if (key_match("SaveMark"))
+			ret = parse_savemark(&ctx->device->savemark, &ctx->device->flags, value);
 		else if (key_match("PrivateKey")) {
 			ret = parse_key(ctx->device->private_key, value);
 			if (ret)
@@ -582,6 +602,11 @@
 				goto error;
 			argv += 2;
 			argc -= 2;
+		} else if (!strcmp(argv[0], "savemark") && argc >= 2 && !peer) {
+			if (!parse_savemark(&device->savemark, &device->flags, argv[1]))
+				goto error;
+			argv += 2;
+			argc -= 2;
 		} else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
 			if (!parse_keyfile(device->private_key, argv[1]))
 				goto error;
diff -uNr wireguard-tools_orig/src/containers.h wireguard-tools/src/containers.h
--- wireguard-tools_orig/src/containers.h	2021-11-16 10:15:23.428374291 +0800
+++ wireguard-tools/src/containers.h	2022-01-05 13:57:23.000000000 +0800
@@ -71,7 +71,8 @@
 	WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
 	WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
 	WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
-	WGDEVICE_HAS_FWMARK = 1U << 4
+	WGDEVICE_HAS_FWMARK = 1U << 4,
+	WGDEVICE_HAS_SAVEMARK = 1U << 5
 };
 
 struct wgdevice {
@@ -85,6 +86,8 @@
 
 	uint32_t fwmark;
 	uint16_t listen_port;
+    
+	uint16_t savemark;
 
 	struct wgpeer *first_peer, *last_peer;
 };
diff -uNr wireguard-tools_orig/src/ipc-linux.h wireguard-tools/src/ipc-linux.h
--- wireguard-tools_orig/src/ipc-linux.h	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/ipc-linux.h	2022-01-05 13:57:39.000000000 +0800
@@ -165,6 +165,8 @@
 			mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
 		if (dev->flags & WGDEVICE_HAS_FWMARK)
 			mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
+		if (dev->flags & WGDEVICE_HAS_SAVEMARK)
+			mnl_attr_put_u16(nlh, WGDEVICE_A_SAVEMARK, dev->savemark);
 		if (dev->flags & WGDEVICE_REPLACE_PEERS)
 			flags |= WGDEVICE_F_REPLACE_PEERS;
 		if (flags)
@@ -439,6 +441,10 @@
 		if (!mnl_attr_validate(attr, MNL_TYPE_U32))
 			device->fwmark = mnl_attr_get_u32(attr);
 		break;
+	case WGDEVICE_A_SAVEMARK:
+		if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+			device->savemark = mnl_attr_get_u16(attr);
+		break;
 	case WGDEVICE_A_PEERS:
 		return mnl_attr_parse_nested(attr, parse_peers, device);
 	}
diff -uNr wireguard-tools_orig/src/Makefile wireguard-tools/src/Makefile
--- wireguard-tools_orig/src/Makefile	2021-11-16 10:15:23.428374291 +0800
+++ wireguard-tools/src/Makefile	2022-01-05 15:27:15.000000000 +0800
@@ -39,7 +39,7 @@
 
 CFLAGS ?= -O3
 ifneq ($(wildcard uapi/$(PLATFORM)/.),)
-CFLAGS += -idirafter uapi/$(PLATFORM)
+CFLAGS += -Iuapi/$(PLATFORM)
 endif
 CFLAGS += -std=gnu99 -D_GNU_SOURCE
 CFLAGS += -Wall -Wextra
diff -uNr wireguard-tools_orig/src/man/wg.8 wireguard-tools/src/man/wg.8
--- wireguard-tools_orig/src/man/wg.8	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/man/wg.8	2022-01-05 14:31:19.228458980 +0800
@@ -36,7 +36,7 @@
 .SH COMMANDS
 
 .TP
-\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
+\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIsavemark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
 Shows current WireGuard configuration and runtime information of specified \fI<interface>\fP.
 If no \fI<interface>\fP is specified, \fI<interface>\fP defaults to \fIall\fP.
 If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces,
@@ -55,7 +55,7 @@
 Shows the current configuration of \fI<interface>\fP in the format described
 by \fICONFIGURATION FILE FORMAT\fP below.
 .TP
-\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
+\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIsavemark\fP \fI<on off>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
 Sets configuration values for the specified \fI<interface>\fP. Multiple
 \fIpeer\fPs may be specified, and if the \fIremove\fP argument is given
 for a peer, that peer is removed, not configured. If \fIlisten-port\fP
@@ -140,6 +140,9 @@
 FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this
 option is disabled. May be specified in hexadecimal by prepending "0x". Optional.
 .P
+SaveMark \(em a switch for controll outgoing source ip based on fwmark. If set to 0, this
+option is disabled. Optional.
+.P
 The \fIPeer\fP sections may contain the following fields:
 .IP \(bu
 PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a
diff -uNr wireguard-tools_orig/src/set.c wireguard-tools/src/set.c
--- wireguard-tools_orig/src/set.c	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/set.c	2022-01-05 13:58:27.000000000 +0800
@@ -18,7 +18,7 @@
 	int ret = 1;
 
 	if (argc < 3) {
-		fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
+		fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [savemark <on off>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
 		return 1;
 	}
 
diff -uNr wireguard-tools_orig/src/show.c wireguard-tools/src/show.c
--- wireguard-tools_orig/src/show.c	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/show.c	2022-01-07 23:22:08.995211377 +0800
@@ -202,7 +202,7 @@
 static const char *COMMAND_NAME;
 static void show_usage(void)
 {
-	fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
+	fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | savemark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
 }
 
 static void pretty_print(struct wgdevice *device)
@@ -220,6 +220,8 @@
 		terminal_printf("  " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port);
 	if (device->fwmark)
 		terminal_printf("  " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
+	if (device->savemark)
+		terminal_printf("  " TERMINAL_BOLD "savemark" TERMINAL_RESET ": on\n");
 	if (device->first_peer) {
 		sort_peers(device);
 		terminal_printf("\n");
@@ -261,7 +263,11 @@
 	printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
 	printf("%u\t", device->listen_port);
 	if (device->fwmark)
-		printf("0x%x\n", device->fwmark);
+		printf("0x%x\t", device->fwmark);
+	else
+		printf("off\t");
+	if (device->savemark)
+		printf("on\n");
 	else
 		printf("off\n");
 	for_each_wgpeer(device, peer) {
@@ -311,6 +317,13 @@
 			printf("0x%x\n", device->fwmark);
 		else
 			printf("off\n");
+	} else if (!strcmp(param, "savemark")) {
+		if (with_interface)
+			printf("%s\t", device->name);
+		if (device->savemark)
+			printf("on\n");
+		else
+			printf("off\n");
 	} else if (!strcmp(param, "endpoints")) {
 		if (with_interface)
 			printf("%s\t", device->name);
diff -uNr wireguard-tools_orig/src/showconf.c wireguard-tools/src/showconf.c
--- wireguard-tools_orig/src/showconf.c	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/showconf.c	2022-01-07 23:16:58.343602457 +0800
@@ -42,6 +42,8 @@
 		printf("ListenPort = %u\n", device->listen_port);
 	if (device->fwmark)
 		printf("FwMark = 0x%x\n", device->fwmark);
+	if (device->savemark)
+		printf("SaveMark = on\n");
 	if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) {
 		key_to_base64(base64, device->private_key);
 		printf("PrivateKey = %s\n", base64);
diff -uNr wireguard-tools_orig/src/uapi/linux/linux/wireguard.h wireguard-tools/src/uapi/linux/linux/wireguard.h
--- wireguard-tools_orig/src/uapi/linux/linux/wireguard.h	2021-11-16 10:15:23.436374154 +0800
+++ wireguard-tools/src/uapi/linux/linux/wireguard.h	2022-01-05 13:54:08.396086895 +0800
@@ -29,6 +29,7 @@
  *    WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
  *    WGDEVICE_A_LISTEN_PORT: NLA_U16
  *    WGDEVICE_A_FWMARK: NLA_U32
+ *    WGDEVICE_A_SAVEMARK: NLA_U16
  *    WGDEVICE_A_PEERS: NLA_NESTED
  *        0: NLA_NESTED
  *            WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
@@ -83,6 +84,7 @@
  *    WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
  *    WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
  *    WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ *    WGDEVICE_A_SAVEMARK: NLA_U16, 0 to disable
  *    WGDEVICE_A_PEERS: NLA_NESTED
  *        0: NLA_NESTED
  *            WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
@@ -156,6 +158,7 @@
 	WGDEVICE_A_FLAGS,
 	WGDEVICE_A_LISTEN_PORT,
 	WGDEVICE_A_FWMARK,
+	WGDEVICE_A_SAVEMARK,
 	WGDEVICE_A_PEERS,
 	__WGDEVICE_A_LAST
 };

@wackejohn
Copy link
Author

@feckert
Hi,
Now I'm trying to save the incoming IP, and force the wireguard to use it as source IP (the original wireguard just always reset the source IP to 0.0.0.0), so there don't need any other mwan3 rule for this.

I'm not good at coding, my patch was only tested for my use cases, hoping improvements for this patch.

The patch:

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-02-07 09:40:02.369674828 +0800
@@ -17,6 +17,8 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dest_ip;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -45,8 +47,12 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
+		if (dest_ip) {
+			fl.saddr = dest_ip;
+		}
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +60,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = dest_ip;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +70,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = dest_ip;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +83,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if (dest_ip) {
+				fl.saddr = dest_ip;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +325,9 @@
 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);
+
+	dest_ip = ip_header->daddr;
 
 	if (unlikely(!sk))
 		goto err;

@wackejohn
Copy link
Author

Patch improved:

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-02-19 09:51:25.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -28,6 +33,7 @@
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
+	struct rtable *rt_check = NULL;
 	struct sock *sock;
 	int ret = 0;
 
@@ -37,6 +43,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -45,8 +55,10 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+		rt_check = dst_cache_get_ip4(cache, &src_addr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +66,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +76,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +89,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if ((src_addr) && (rt_check)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +331,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;
+	src_addr = dst_addr;
 
 	if (unlikely(!sk))
 		goto err;

The previous patch would break the wireguard out going packets when the wan ip changed. And now this patch can capture the incoming wan ip as the souce ip, and also work with the mwan3 out going rules.

@aaronjg
Copy link
Contributor

aaronjg commented Feb 21, 2022

If you have a patch for wireguard, can you please submit it upstream with the wireguard developers? We are not really equipped to do a proper security audit that would be necessary to accept a P/R that touches the core wireguard code.

@wackejohn
Copy link
Author

If you have a patch for wireguard, can you please submit it upstream with the wireguard developers? We are not really equipped to do a proper security audit that would be necessary to accept a P/R that touches the core wireguard code.

Hi,
I think the wireguard dev won't accept my patch, and for this patch, my only purpose is to share my experience for users who want to use wireguard server with multi wan. So maybe I should close this issue?

@jamesmacwhite
Copy link
Contributor

@wackejohn Most of the patches and changes are over my head in terms of understanding, but certainly appreciate your efforts. I'd hope the Wireguard devs would reconsider them, given I think Wireguard does have issues with multi WAN environments.

@wackejohn
Copy link
Author

wackejohn commented Feb 22, 2022

@jamesmacwhite I've hoped for years already, but nothing happend, then I have to try it myself.

All of my thinking was based on a patch from mailing list:
https://lists.zx2c4.com/pipermail/wireguard/2021-September/007157.html

When receiving connection, the original wireguard will reset the incoming fwmark (but all the incoming connection info is in kernel sk buffer) then use the 0.0.0.0 as responding source ip, use incoming source ip as dest ip to serching the route from main routing table, this cause the wireguard always use the ip of lowest metric as respoding ip.

As above, my patch is trying to save the incoming fwmark or save the incoming dest ip from kernel sk buffer, and then force the wireguard to use them, after a lot of tests (only my use cases), my patch finally working.

@ghost
Copy link

ghost commented May 24, 2022

Patch improved:

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-02-19 09:51:25.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -28,6 +33,7 @@
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
+	struct rtable *rt_check = NULL;
 	struct sock *sock;
 	int ret = 0;
 
@@ -37,6 +43,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -45,8 +55,10 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+		rt_check = dst_cache_get_ip4(cache, &src_addr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +66,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +76,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +89,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if ((src_addr) && (rt_check)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +331,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;
+	src_addr = dst_addr;
 
 	if (unlikely(!sk))
 		goto err;

The previous patch would break the wireguard out going packets when the wan ip changed. And now this patch can capture the incoming wan ip as the souce ip, and also work with the mwan3 out going rules.

Hi @wackejohn

The Patch you have given here is for which wireguard version?
Iam using,
wireguard-linux-compat-1.0.20200611
wireguard-tools-v1.0.20191226

Can this patch be applicable for my version?

@wackejohn
Copy link
Author

Patch improved:

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-02-19 09:51:25.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -28,6 +33,7 @@
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
+	struct rtable *rt_check = NULL;
 	struct sock *sock;
 	int ret = 0;
 
@@ -37,6 +43,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -45,8 +55,10 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+		rt_check = dst_cache_get_ip4(cache, &src_addr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +66,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +76,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +89,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if ((src_addr) && (rt_check)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +331,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;
+	src_addr = dst_addr;
 
 	if (unlikely(!sk))
 		goto err;

The previous patch would break the wireguard out going packets when the wan ip changed. And now this patch can capture the incoming wan ip as the souce ip, and also work with the mwan3 out going rules.

Hi @wackejohn

The Patch you have given here is for which wireguard version? Iam using, wireguard-linux-compat-1.0.20200611 wireguard-tools-v1.0.20191226

Can this patch be applicable for my version?

Hi,
My patch was for the wireguard version that in the kernel tree, not for wireguard compat version, the patch will fail if you apply it directly, but it's very simple to modify.
ps: My patch seems still have issues, and i didn't find out why yet.

@ghost
Copy link

ghost commented May 24, 2022

Patch improved:

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-02-19 09:51:25.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -28,6 +33,7 @@
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
+	struct rtable *rt_check = NULL;
 	struct sock *sock;
 	int ret = 0;
 
@@ -37,6 +43,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -45,8 +55,10 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+		rt_check = dst_cache_get_ip4(cache, &src_addr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +66,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +76,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +89,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if ((src_addr) && (rt_check)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +331,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;
+	src_addr = dst_addr;
 
 	if (unlikely(!sk))
 		goto err;

The previous patch would break the wireguard out going packets when the wan ip changed. And now this patch can capture the incoming wan ip as the souce ip, and also work with the mwan3 out going rules.

Hi @wackejohn
The Patch you have given here is for which wireguard version? Iam using, wireguard-linux-compat-1.0.20200611 wireguard-tools-v1.0.20191226
Can this patch be applicable for my version?

Hi, My patch was for the wireguard version that in the kernel tree, not for wireguard compat version, the patch will fail if you apply it directly, but it's very simple to modify. ps: My patch seems still have issues, and i didn't find out why yet.

Thanks for your info!!.
So Did you got any workaround for this issue.?
As per my observation, I've got one workaround by adding a static route in main RT table. so that the Wireguard packet was able to go via the routed gateway(wan).
But this cant be the better solution.

@wackejohn
Copy link
Author

Patch improved:

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-02-19 09:51:25.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -28,6 +33,7 @@
 		.flowi4_proto = IPPROTO_UDP
 	};
 	struct rtable *rt = NULL;
+	struct rtable *rt_check = NULL;
 	struct sock *sock;
 	int ret = 0;
 
@@ -37,6 +43,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -45,8 +55,10 @@
 
 	fl.fl4_sport = inet_sk(sock)->inet_sport;
 
-	if (cache)
+	if (cache) {
 		rt = dst_cache_get_ip4(cache, &fl.saddr);
+		rt_check = dst_cache_get_ip4(cache, &src_addr);
+	}
 
 	if (!rt) {
 		security_sk_classify_flow(sock, flowi4_to_flowi_common(&fl));
@@ -54,7 +66,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +76,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +89,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if ((src_addr) && (rt_check)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +331,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;
+	src_addr = dst_addr;
 
 	if (unlikely(!sk))
 		goto err;

The previous patch would break the wireguard out going packets when the wan ip changed. And now this patch can capture the incoming wan ip as the souce ip, and also work with the mwan3 out going rules.

Hi @wackejohn
The Patch you have given here is for which wireguard version? Iam using, wireguard-linux-compat-1.0.20200611 wireguard-tools-v1.0.20191226
Can this patch be applicable for my version?

Hi, My patch was for the wireguard version that in the kernel tree, not for wireguard compat version, the patch will fail if you apply it directly, but it's very simple to modify. ps: My patch seems still have issues, and i didn't find out why yet.

Thanks for your info!!. So Did you got any workaround for this issue.? As per my observation, I've got one workaround by adding a static route in main RT table. so that the Wireguard packet was able to go via the routed gateway(wan). But this cant be the better solution.

Hi,
Tried to fix the patch, and tested on my router, working:

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-25 20:29:27.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -37,6 +42,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -54,7 +63,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +73,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +86,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if (inet_confirm_addr(sock_net(sock), NULL, 0, &src_addr, RT_SCOPE_LINK)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +328,10 @@
 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;
 
 	if (unlikely(!sk))
 		goto err;

And patch for your wireguard-linux-compat-1.0.20200611 version:

diff -uNr wireguard-linux-compat-1.0.20200611_orig/src/socket.c wireguard-linux-compat-1.0.20200611/src/socket.c
--- wireguard-linux-compat-1.0.20200611_orig/src/socket.c	2020-06-11 16:17:19.000000000 +0800
+++ wireguard-linux-compat-1.0.20200611/src/socket.c	2022-05-25 21:21:51.000000000 +0800
@@ -17,6 +17,11 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+static u32 dst_addr;
+static u32 src_addr;
+
+int receive = 0;
+
 static int send4(struct wg_device *wg, struct sk_buff *skb,
 		 struct endpoint *endpoint, u8 ds, struct dst_cache *cache)
 {
@@ -37,6 +42,10 @@
 
 	rcu_read_lock_bh();
 	sock = rcu_dereference_bh(wg->sock4);
+    
+	if (receive) {
+		src_addr = dst_addr;
+	}
 
 	if (unlikely(!sock)) {
 		ret = -ENONET;
@@ -54,7 +63,7 @@
 						fl.saddr, RT_SCOPE_HOST))) {
 			endpoint->src4.s_addr = 0;
 			*(__force __be32 *)&endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 		}
@@ -64,7 +73,7 @@
 			     rt->dst.dev->ifindex != endpoint->src_if4)))) {
 			endpoint->src4.s_addr = 0;
 			*(__force __be32 *)&endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			fl.saddr = src_addr;
 			if (cache)
 				dst_cache_reset(cache);
 			if (!IS_ERR(rt))
@@ -77,8 +86,12 @@
 					    wg->dev->name, &endpoint->addr, ret);
 			goto err;
 		}
-		if (cache)
+		if (cache) {
+			if (inet_confirm_addr(sock_net(sock), NULL, 0, &src_addr, RT_SCOPE_LINK)) {
+				fl.saddr = src_addr;
+			}
 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
+		}
 	}
 
 	skb->ignore_df = 1;
@@ -315,6 +328,10 @@
 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;
 
 	if (unlikely(!sk))
 		goto err;

@ghost
Copy link

ghost commented May 26, 2022

Thanks @wackejohn
Appreciating your efforts and guidance!!
Let me add this patch for my Wireguard and check the working behaviour.

@wackejohn
Copy link
Author

wackejohn commented May 27, 2022

Thanks @wackejohn Appreciating your efforts and guidance!! Let me add this patch for my Wireguard and check the working behaviour.

Hi,
Patch updated again, and for my use cases it seems working without issuses, but it still may have issues, hope this patch working
for your use case:

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;
diff -uNr wireguard-linux-compat-1.0.20200611_orig/src/socket.c wireguard-linux-compat-1.0.20200611/src/socket.c
--- wireguard-linux-compat-1.0.20200611_orig/src/socket.c	2020-06-11 16:17:19.000000000 +0800
+++ wireguard-linux-compat-1.0.20200611/src/socket.c	2022-05-27 15:47:06.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(&fl));
 		if (unlikely(!inet_confirm_addr(sock_net(sock), NULL, 0,
 						fl.saddr, RT_SCOPE_HOST))) {
-			endpoint->src4.s_addr = 0;
-			*(__force __be32 *)&endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			endpoint->src4.s_addr = src_addr;
+			*(__force __be32 *)&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;
-			*(__force __be32 *)&endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			endpoint->src4.s_addr = src_addr;
+			*(__force __be32 *)&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;

@ghost
Copy link

ghost commented May 27, 2022

Hi @wackejohn
Thank you.
For wireguard-linux-compat-1.0.20200611, Only the patch with socket.c code is enough?

@wackejohn
Copy link
Author

Hi @wackejohn Thank you. For wireguard-linux-compat-1.0.20200611, Only the patch with socket.c code is enough?

@dashajyo Yes, from my test result, it's enough.

@rkorn86
Copy link

rkorn86 commented May 30, 2022

Thanks for this issue and the patch! I have exactly the same problem!
Is there any binary to update my Ubiquiti EdgeRouter X running OpenWrt 21.02.0 r16279-5cc0535800 with wireguard-tools - 1.0.20210223-2 and kmod-wireguard - 5.4.143-1 to get a wireguard server running with mwan3?

@wackejohn
Copy link
Author

Thanks for this issue and the patch! I have exactly the same problem! Is there any binary to update my Ubiquiti EdgeRouter X running OpenWrt 21.02.0 r16279-5cc0535800 with wireguard-tools - 1.0.20210223-2 and kmod-wireguard - 5.4.143-1 to get a wireguard server running with mwan3?

Hi,
You have to recompile the kernel and wireguard from source with this patch.

@wackejohn
Copy link
Author

Hi,
Patch update again:

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-06-01 08:18:00.990080098 +0800
@@ -17,6 +17,12 @@
 #include <net/udp_tunnel.h>
 #include <net/ipv6.h>
 
+u32 dst_addr;
+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->src4.s_addr = src_addr;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			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->src4.s_addr = src_addr;
 			endpoint->src_if4 = 0;
-			fl.saddr = 0;
+			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;

@kode54
Copy link

kode54 commented Jun 14, 2022

Successfully applied against the 5.4 kernel series. Will test eventually.

@dongc123
Copy link

dongc123 commented Jun 18, 2023

您好!感谢你执着和认真推动了开源社区发展,openwrt我也遇到了这个问题第一次给程序打补丁,请问这个补丁代码是放在openwrt目录保存为xxx.patch文件,然后执行命令patch -p1 < xxx.patch,就可以了吗?我看到上面有几个不同的补丁,请问只需要打2022 年 6 月 9 日最新发布的补丁就可以了吗?

@dongc123
Copy link

dongc123 commented Jun 18, 2023

Hello! Thank you for your dedication and seriousness in promoting the development of the open source community. I also encountered this problem in openwrt. I patched the program for the first time. May I ask if the patch code is placed in the openwrt directory and saved as a xxx.patch file, and then execute the command patch -p1 < xxx .patch, is it ok? I see that there are several different patches above, can I just apply the latest patch released on June 9, 2022?
`debian@debian:~/openwrt/build_dir/toolchain-x86_64_gcc-12.3.0_musl$ patch -p1 < your_patch_file.patch
can't find file to patch at input line 4
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:

|diff -uNr linux-5.15.114/drivers/net/wireguard/socket.c linux-5.15.114_wg/drivers/net/wireguard/socket.c
|--- linux-5.15.114/drivers/net/wireguard/socket.c 2021-12-29 19:29:03.000000000 +0800
|+++ linux-5.15.114_wg/drivers/net/wireguard/socket.c 2022-06-01 08:18:00.990080098 +0800

File to patch: linux-5.15.114/drivers/net/wireguard/socket.c
patching file linux-5.15.114/drivers/net/wireguard/socket.c
patch unexpectedly ends in middle of line
Hunk #6 succeeded at 337 with fuzz 1 (offset 1 line).`
This is my patch, how do you see it?
After recompiling, you can capture the packet and you can see that the source path returns after the data arrives at the high-hop vwan0 port, but the client accepts data 0 and cannot connect. Where is the problem?
``

@dongc123
Copy link

root@OpenWrt:~# tcpdump -i pppoe-vwan0 host 223.147.25.61 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on pppoe-vwan0, link-type LINUX_SLL (Linux cooked v1), snapshot length 262144 bytes 14:37:33.829972 IP 223.147.25.61.9789 > 175.8.41.67.8023: UDP, length 148 14:37:38.431871 IP 223.147.25.61.9792 > 175.8.41.67.8023: UDP, length 148 14:37:38.432291 IP 175.8.41.67.35307 > 223.147.25.61.9792: UDP, length 92 14:37:44.314553 IP 223.147.25.61.9792 > 175.8.41.67.8023: UDP, length 148 14:37:44.314916 IP 175.8.41.67.35307 > 223.147.25.61.9792: UDP, length 92 14:37:49.114660 IP 223.147.25.61.9792 > 175.8.41.67.8023: UDP, length 148 14:37:49.115172 IP 175.8.41.67.35307 > 223.147.25.61.9792: UDP, length 92 14:37:51.435982 IP 223.147.25.61.9796 > 175.8.41.67.8023: UDP, length 148 14:37:56.009329 IP 223.147.25.61.9797 > 175.8.41.67.8023: UDP, length 148
After the patch is recompiled, you can capture packets and you can see that the source path returns after the data arrives at the high-hop vwan0 port, but the client accepts data 0 and cannot connect. Capture the packet and see that the wireguard port of the return address has changed.

@dongc123
Copy link

My system environment firmware version OpenWrt 23.05.0-rc1, kernel version 5.15.114, target platform x86/64. After the patch is applied, the data comes in from the vwan0 port and the vwan0 port goes out, but the port of the wireguard changes inexplicably when it goes out. solve

@dongc123
Copy link

The port of the client will change automatically every time it reconnects to the server

@dongc123
Copy link

listening on pppoe-vwan0, link-type LINUX_SLL (Linux cooked v1), snapshot length 262144 bytes 16:04:15.159322 IP 223.147.25.61.9962 > 175.8.41.67.8023: UDP, length 148 16:04:20.235932 IP 223.147.25.61.9977 > 175.8.41.67.8023: UDP, length 148 16:04:20.236483 IP 175.8.41.67.8048 > 223.147.25.61.9977: UDP, length 92 16:04:25.655931 IP 223.147.25.61.9978 > 175.8.41.67.8023: UDP, length 148 16:04:25.656437 IP 175.8.41.67.8048 > 223.147.25.61.9978: UDP, length 92 16:04:27.474104 IP 223.147.25.61.9979 > 175.8.41.67.8023: UDP, length 148 16:04:30.433133 IP 223.147.25.61.9980 > 175.8.41.67.8023: UDP, length 148 16:04:32.133377 IP 223.147.25.61.9981 > 175.8.41.67.8023: UDP, length 148 16:04:32.133984 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 92 16:04:57.192345 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:02.962336 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:08.712360 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:14.472384 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:20.242379 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:25.352238 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:31.112246 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:36.882333 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:42.632254 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:48.392299 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:53.512236 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148 16:05:59.272367 IP 175.8.41.67.8048 > 223.147.25.61.9981: UDP, length 148

@dongc123
Copy link

Thank you Arthur for solving the problem

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