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
There is no verification on packets in reliable transfer mode #39176
Comments
cc @Faless |
I'm currently working on implementing a checksum into ENet in 3.2 stable for my own project using the already included XXHash algorithm. This can be easily injected into |
If it crash the engine, that's a bug. We solved few decode crash in the past while auditing the marshalls code, but you find new ones please post the stack trace. Regarding packet tampering. If the packet is received and dropped with an error in console, that is expected. UDP has checksum (for malformed packets), on top of the data link one. If any of those fails, the OS drop the packet. If a packet get tampered by e.g. a succesful MITM attack (similar to the case of you packet scrambler), there is nothing we can really do. |
Is not the checksum optional for UDP? |
Okay so you are saying the theoretically if a UDP packet is malformed due to not matching a checksum, the packet should be dropped? Because I'm reporting that what I am seeing is that none of the packets are dropped at all, even though they are definitely getting malformed (and the checksum is not getting recomputed, meaning it should fail). I think what is happening is that there is actually no checksum on the UDP layer Godot is using because it is disabled at the ENet level since a null ptr is passed in for the checksum function. I should note that what started this investigation is that I was seeing actual malformed packets in real multiplayer testing getting through, and since it is really improbable those real world packet errors were somehow also messing with the checksum in just the right way, I think the checksum feature is disabled for our UDP connection from Godot. |
I found this in the ENet changelog.. could be relevant: |
@Faless You gave me an idea to check the UDP traffic of Godot with Wireshark and it looks like you are correct, there is definitely a CRC checksum being sent and it looks like the OS is responsible for filtering it. Unfortunately it looks like the 16-bit checksum that UDP uses is not good enough for my needs since I had real issues in actual multiplayer. I might see if I can implement a 64-bit checksum using xxHash on top of the networking system or maybe DTLS will be enough. Maybe it would still be worth explosing the checksum function pointer of ENet to users of the engine so they could enable better validation? I'll leave that up to you. Thanks for your input and help! |
I am getting that [UDK CHECKSUM INCORRECT]-status on absolutely all of the packets, not just some. (I have not been able to test for the case where the packet leaves and reenters my network card though). |
Are you getting that with a packet scrambler on or just with normal networking? Also are you using the loopback sniffer of Wireshark or are you inspecting something else? |
Yeah, UDP checksum in IPv4 is indeed optional, so if the sent packet has a On the other end, if the checksum value is non- I guess clumsy sets it to |
No scrambler. Loopback, yes -- so it may be that there was never any hardware to compute the checksum. |
Of course all checksums can fail, although it should be quite rare (again, data link has error correction too, so it must be an undetectable error in the intersection of data link/UDP error correction failure). |
I never made the connection between Pigeonhole and that, pretty neat. It's possible that the issues I was seeing was due to something else then, maybe multithreading issues. If not though, even if it should be rare to have corrupted data pass through, apparently it's not rare enough. If I can't continue causing any of these issues with normal play I'll probably just write my own small wrapper around ENet and pass in a bigger hash function for the ENet checksum callback. |
I'm not sure I understand, you are tampering the packet without changing the checksum and wireshark still says checksum is correct? Maybe without the redo checksum option clumsy does, on purpose, generate data that has the same checksum then? I couldn't find any proper description of how that function works in their doc/manual :(. |
Given ENet has that option for extra checksum (probably to apply it on fragmented packets as a whole) maybe we can expose that, without touching the higher level multiplayer API. Will need some investigation on how it works. |
I looked at the source code for clumsy a bit and it appears that without I was saying that I can see from Wireshark that the UDP protocal we are using with Godot is sending checksums just like you said it should, but that clumsy isn't zero-ing it out like you guessed. From the results in Wireshark it looks like a vast majority of the time, with packet tampering on but with |
I'm pretty new to the actual engine, but unfortunately when I tried to pass a value in for the I think also some of my early results I just got confused by |
By a quick look at the enet source code, it seems that length computation is done automatically for us, so this simple patch seems to work (and should add the extra ENet I am still baffled by the fact that you are actually hitting such a problem, which I've never experienced myself. But maybe you can try implementing this extra hash function and see if that solves it completely. diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 406eb467f0..6e8589862b 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -33,6 +33,10 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
+enet_uint32 enet_checksum_compute(const ENetBuffer *buffers, size_t bufferCount) {
+ return 42;
+}
+
void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode;
@@ -106,6 +110,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
p_out_bandwidth /* limit outgoing bandwidth if > 0 */);
ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create an ENet multiplayer server.");
+ host->checksum = enet_checksum_compute;
#ifdef GODOT_ENET
if (dtls_enabled) {
enet_host_dtls_server_setup(host, dtls_key.ptr(), dtls_cert.ptr());
@@ -162,6 +167,7 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por
}
ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create the ENet client host.");
+ host->checksum = enet_checksum_compute;
#ifdef GODOT_ENET
if (dtls_enabled) {
enet_host_dtls_client_setup(host, dtls_cert.ptr(), dtls_verify, p_address.utf8().get_data()); |
@Faless Wow you are amazing. when I get a chance I will try that. Honestly I'm starting to think the whole issue I experienced was just something else the whole time. I'm basically unable to see the original issue anymore (unless By returnin Also, for my own sanity, the high-level multiplayer features are not thread-safe right? Thanks again for all your help. If I find myself with the money sometime, I'd like to support Godot's development. I'm really impressed both with the engine and with the community. |
Yeah, I didn't implement a real hashing function.
No, it is not thread safe, you should avoid calling RPCs in threads unless you know what you are doing. |
Okay, awesome. I'll keep looking and open a conversation up if I notice anything off, but I'd say with all the investigation here I think I can comfortably say that the bug I thought I was reporting is just something else (probably an issue with bad synchronization). I'll probably implement a concurrent queue messaging service and pass all of my RPCs into that instead. You can close this if you like, or if you want to expose the callback in the future, change it to that. Either way I think it's safe to say this is resolved though. |
Now that DTLS support is available for multiplayer encryption, this can be closed. |
Godot version:
v3.2.1.stable
OS/device including version:
Windows 10
Issue description:
Using reliable transfer mode with the high-level networking API there is no checksum capability exposed to the engine. Corrupted packets are always accepted happily on reliable mode either resulting in crashes due to marshalling errors:
or corrupted data that silently fails (without compalint), for example this world data:
Steps to reproduce:
rpc(...)
Minimal reproduction project:
NetworkTest.zip
The text was updated successfully, but these errors were encountered: