zia-server does not distinguish between different clients
The problem is with this line: https://github.com/MarcelCoding/zia/blob/1a6301b53c2f15bdce2cd52b543e11c84f257dd1/zia-server/src/main.rs#L125 There is only one UDP socket to connect to UDP upstream in the whole program. I want to have different instances of zia-client on my laptop and smartphone to proxy WG traffic, but apparently zia-server does not support this feature. WG requires different UDP addresses for different peers.
Here is an example:
$ ./zia-server -l 127.0.0.1:5000 -u 127.0.0.1:5050
The above command spawns the websocket server on port 5000 and creates some ephemeral client UDP socket (only one) to connect to UDP upstream with port 5050.
Next I create two clients to imitate laptop and smartphone:
$ ./zia-client -l 127.0.0.1:5051 -u ws://127.0.0.1:5000 -c 1
$ ./zia-client -l 127.0.0.1:5052 -u ws://127.0.0.1:5000 -c 1
Here I have two UDP server sockets: 5051 and 5052 where I can send data. Now imagine the following python program is a WG server:
Spoiler warning
import argparse
import socket
def main():
parser = argparse.ArgumentParser(
description="Simple UDP echo server to debug addresses",
)
parser.add_argument(
"-b",
"--bind-address",
help="UDP host and port where to bind server socket",
required=True,
)
args = parser.parse_args()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
(host, port) = args.bind_address.rsplit(":", maxsplit=1)
sock.bind((host, int(port)))
print(f"Server bound to {sock.getsockname()}/udp")
while True:
try:
(bytes_data, address) = sock.recvfrom(2**16)
except KeyboardInterrupt:
print("Stopped")
break
msg = bytes_data.decode("utf-8")
print(f"Recieved {len(bytes_data)} bytes from {address}: {msg[:20]}")
reply = f"{msg}; your address = {address}".encode("utf-8")
sock.sendto(reply, address)
if __name__ == "__main__":
main()
Respective client:
Spoiler warning
import socket
import argparse
def main():
parser = argparse.ArgumentParser(
description="UDP client",
)
parser.add_argument(
"-b",
"--bind-address",
help="UDP host and port where to bind a client socket",
required=True,
)
parser.add_argument(
"-s",
"--server-address",
help="Address of an UDP server",
required=True,
)
parser.add_argument("-d", "--data", help="Additional data to send", default="")
args = parser.parse_args()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
(local_host, local_port) = args.bind_address.rsplit(":", maxsplit=1)
sock.bind((local_host, int(local_port)))
print(f"Client bound to {sock.getsockname()}/udp")
(remote_host, remote_port) = args.server_address.rsplit(":", maxsplit=1)
sock.connect((remote_host, int(remote_port)))
print(f"Connected to UDP server at {sock.getpeername()}")
send_msg = args.data.encode("utf-8")
send_count = sock.send(send_msg)
if send_count != len(send_msg):
raise RuntimeError("Did not send entire UDP datagram")
recv_str = sock.recv(2**16).decode("utf-8")
print(f"Client received: {recv_str}")
if __name__ == "__main__":
main()
Here is client output without zia proxy:
$ python client.py -b 127.0.0.1:10123 -s 127.0.0.1:5050 -d hello
Client bound to ('127.0.0.1', 10123)/udp
Connected to UDP server at ('127.0.0.1', 5050)
Client received: hello; your address = ('127.0.0.1', 10123)
$ python server.py -b 127.0.0.1:5050
Server bound to ('127.0.0.1', 5050)/udp
Recieved 5 bytes from ('127.0.0.1', 10123): hello
Forward via 5051 zia-client:
$ python client.py -b 127.0.0.1:10123 -s 127.0.0.1:5051 -d hello
Client bound to ('127.0.0.1', 10123)/udp
Connected to UDP server at ('127.0.0.1', 5051)
Client received: hello; your address = ('127.0.0.1', 41994)
Forward via 5052:
$ python client.py -b 127.0.0.1:10123 -s 127.0.0.1:5052 -d hello
Client bound to ('127.0.0.1', 10123)/udp
Connected to UDP server at ('127.0.0.1', 5052)
Client received: hello; your address = ('127.0.0.1', 41994)
As you can see zia-server allocated only one UDP socket with port 41994 for both clients. WG could not be able to distinguish those peers and route traffic between them.
The more correct solution would be to create temporary client UDP sockets for every TCP client on zia-server as in process_socket function here. But here arises another problem: how do you distinguish TCP streams between different clients with --count option?
That would mean that there must be some kind of protocol on top of websocket. Currently as you already noticed. Every websocket connection uses the same udp socket as for multiplexing reasons. Using the multiplexing I am able to reach about 200Mbit and without only between 10 and 25. I just deploy multiple instances of Zia for different wireguard endpoints and friends.