mark-kubacki / systemd-transparent-udp-forwarderd

Transparent socket-activated UDP Proxy/Forwarder
9 stars 4 forks source link

Error binding to destination. (Address already in use) #2

Closed chris13524 closed 6 years ago

chris13524 commented 6 years ago

I have three unit files, factorio.service, factorioproxy.service, and factorio.socket. When I go in Factorio and connect to port 34197, I get this error printed several times under the factorioproxy.service logs: "Error binding to destination. (#98 Address already in use)"

All three services are running according to systemd, but no connections can be made and there is that error.

factorio.service

[Unit]
Description=Factorio server

[Service]
Type=simple
ExecStart=/home/chris13524/worthless/downloads/factorio/bin/x64/factorio --bind 127.0.0.1:34198 --start-server test
WorkingDirectory=/home/chris13524/worthless/downloads/factorio
KillSignal=SIGINT

factorioproxy.service

[Unit]
BindsTo=factorio.service
After=factorio.service

[Service]
Type=notify
ExecStart=/home/chris13524/worthless/downloads/factorio/systemd-transparent-udp-forwarderd 127.0.0.1:34198

factorioproxy.socket

[Socket]
ListenDatagram=0.0.0.0:34197
Transparent=true

[Install]
WantedBy=sockets.target
chris13524 commented 6 years ago

Both port 34197 and 34198 are being listened on, but that's intended, right?

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
udp        0      0 0.0.0.0:34197           0.0.0.0:*                           1/init
udp        0      0 127.0.0.1:34198         0.0.0.0:*                           5138/factorio
mark-kubacki commented 6 years ago

systemd will start the proxy once, and I guess it's the one listed as 34197 ("init" is systemd, though the proxy will be listed as "init" or "udp-forwarder" or similar, depending on its state and systemd's version).
Unless you have a lingering port use (from a crashed or not properly exited process; should be removed by Linux within 15–90 seconds) the error is not caused by this.

That said, your above output and files appear to be correct.

The error is not emitted by the proxy. Therefore I am closing this issue, but feel free to append output of systemctl status factorio.service factorioproxy.{proxy,service} if the error still exists.

I suspect that two Factorio processes are being started: One previously started by you, and the other by systemd. We only want the latter. Any BindsTo=… or Requires=… will start it once, by the way. Kill or stop all factorio processes and try again.


And yes, you cannot have one process listen on <whatever specific>:<port> and another on 0.0.0.0:<port> (same ports) as the latter overshadows the former. (There's exceptions to this, but we don't need to delve into that.) So yes, Factorio and its proxy must listen to different ports.

Please note that you have configured Factorio to bind to localhost (127.0.0.1 here; ::1 with IPv6). Although incoming packets should arrive at Factorio just fine, the process likely won't be able to return packets in response. Especially so if you did forget to enable IP-forwarding.

I recommend you have it bind to your computer's public IP Address (5.5.5.5 or whatever; use a static one if this is within your home network) or Docker bridge if you use Docker. Usually the container environment (Docker bridge or whatever) and later your router/gateway will take care of translating any local address to a public one for outgoing packets. (Such a translation might not be configured for the 127.0.0.1.)

cecton commented 7 months ago

I think I managed to reproduce the issue for this on a completely different game (Palworld).

I had the dedicated server running on a remote machine and I ran the proxy on the local machine alongside the game client. It kept failing with this exact error (Error binding to destination: address already in use).

I added a log that prints out the address and the port it is trying to bind. It was trying to bind on 127.0.0.1 on some random port. When I listed the ports and the processes associated with those ports, I saw wineserver. It looks like the game itself was already binding to that port.

Relevant part of the code with this issue:

    /* spoof the sender */
    if (unlikely(bind(out, (struct sockaddr *)msg->msg_name, (in_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) < 0)) {
        // added by me
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)msg->msg_name;
        char ipAddr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(ipv4->sin_addr), ipAddr, INET_ADDRSTRLEN);
        printf("IPv4 Address: %s\n", ipAddr);
        printf("Port: %d\n", ntohs(ipv4->sin_port)); // ntohs to convert from network byte order to host byte order
        (void) sd_journal_print(LOG_ERR, "Address: %s\n", ipAddr);
        (void) sd_journal_print(LOG_ERR, "Port: %d\n", ntohs(ipv4->sin_port));
        // <----------
        (void) sd_journal_print(LOG_ERR, "Error binding to destination. (#%d %s)\n", errno, strerror(errno));
        (void) close(out);
        return -4;
    }
mark-kubacki commented 7 months ago

Yes, the game needs to listen to a different IP address. In my original use case, that would be one pointing into the container. However, you could configure 127.0.0.8 or 10.0.4.1 or anything else usually non-routable for the same effect to separate your host IP address or 0.0.0.0 from the application's.

0.0.0.0 (or [::]) overlaps with any and all on the host. That's why I recommend a ListenDatagram=a.b.c.d:pp with a.b.c.d being a specified address (like 8.7.6.5).