These are the principles around networking.
These are the principles around networking.
These are general principles to keep in mind when thinking about the networking layer. These guided the existing design.
- Do not assume long-lived connections. Instead, assume that the network is busy and flaky. You won't get much bandwidth, and any connections you do make will be dropped frequently. Don't build any component that relies on long-lived connections. Connection dropping and re- establishment should be transparent to the player.
- Assume messages will be lost. The client and server will do their best to deliver messages, but software and hardware layers along with other factors such as competing traffic will mean that many messages will be lost. Never assume that if you send a message that the other party will hear it! Protocols and behaviors should ensure that the system (server and all clients) catch up eventually.
- Assume messages will arrive out of order. We already mentioned that many messages will be lost. For those that do arrive, assume that some of them will arrive in a different order than they were sent.
- Assume the network is being inspected. That is, there are people watching all packets going in and out of the client and server. No personal or private information should be sent in clear text.
- Assume clients are malicious. In practice, I doubt this is a system worth hacking. But any code reading data off the network should assume that a hacker is trying to take advantage of the system. For example, you should assume that remote hosts:
- will attempt to cheat (this only applies to clients). For instance, clients will attempt to provide inaccurate position information, or claim they always have more ammunition, etc. Never allow a client to be able to cheat about anything.
- will spawn Denial of Service (DOS) attacks by sending many commands that are expensive for the local host to deal with.
- will send malformed requests to the local host in order to crash it.
- will send malformed requests to hack into the local computer. This includes:
- attempting to read/write the local filesystem
- attempting to execute local binaries or shell commands
- attempting to cause malicious behavior by buffer overflow
The AESOP client and server code is intended to be running on people's machines. This could include corporate, university, and home networks. This software sends and receives UDP packets, and opens TCP ports. As such, it is a potential target for hackers and should be extremely secure. See the security notes at http://wavepacket-lib.sourceforge.net/group__principles.html
Design Consequences of the Above:
- Use UDP datagrams for all state exchanges. UDP has no connection, so there isn't a worry about dealing with dropped connections, and we typically don't need the overhead that comes with a guaranteed protocol such as TCP. But this has a consequence:
- All state exchange messages should fit in a single physical network packet. No message should ever span multiple UDP packets. The size of physical network packets can vary (see http://en.wikipedia.org/wiki/Maximum_transmission_unit) but we assume a maximum size of 1500 bytes or less.
- Any message that needs to span multiple packets (that is, any message larger than 1500 bytes) should use TCP. The AESOP framework will set up TCP connections between the client and server as a convenience, but these should be rarely used. At the moment, these are only used by Conversations (see Conversation Library). Any code using TCP should expect the connection to fail often, and deal with it gracefully.
- To deal with dropped messages, no component relies on guaranteed messages (not even TCP-based components, since the connection could fail!). The server holds authoritative state for practicaly everything, and the client does its best to stay in sync. The client is responsible for requesting updated data. The server is under no obligation to report interesting state changes. The client should occasionally poll etc. to make sure it converges to the correct state over time. Put another way: there are no responses to messages. If you fire a request or command at a remote host, it may or may not receive it, and may or may not update its state as a result, and it certainly won't reply. If you care about the updated state, you'll need to poll for it or wait for a regularly scheduled state update.
- To deal with out-of-order messages, all UDP messages have a sequence number. When a component (client or server) receives a message with an older sequence number, the message is simply dropped. This happens right away in message processing, so downstream message parsing code can safely assume they are working on the most recent message received.
- To protect against network sniffers (people watching all data crossing the wire), all sensitive data is encrypted using standard encryption (see the Wavepacket library cryptography routines at http://wavepacket-lib.sourceforge.net/group__crypto.html). Each host has its own cryptographic key that is not persisted, so hosts cannot eavesdrop on traffic meant for other hosts.
- To protect against malicious attacks, these are some of the common defensive techniques. These are common to all networked games and software in general:
- The client is never allowed to be authoritative for interesting data, and therefore can never "cheat". The client can only make requests to the server to change state. The server is responsible for taking in requests from client, and making only legitimate state changes.
- The client and server use network request queues (see http://wavepacket-lib.sourceforge.net/group__netrq.html) to avoid having repetitive messages or requests result in excessive work. This helps guard against DOS attacks (which will probably be due to misbehaving clients rather than real DOS hackers).
- All code should carefully inspect incoming messages and aggressively verify that the data is syntactically or semantically incorrect. If invalid, the message should be ignored, or if that isn't convenient or appropriate, an exception should be thrown. This way malformed network packets won't cause crashes etc. In general, assume every message is malformed or corrupted and parsing code should be very defensive!
- Use standard security defenses to block hackers. These include but are not limited to:
- Not allowing the client to get access to local files/directories.
- Not allowing the client to execute code or get access to a shell.
- Providing a strict, limited vocabulary for all communications and enforcing it rigorously.
- Always check the size of incoming data (for instance, float or string arrays) and make sure you can receive it safely.
There are a lot of good resources out on the Internet about how to build networked games that are fast and secure. Here are some links that I put together quickly: