Protecting SA-MP and open.mp servers from DDoS attacks

Published on 17 min read

SA-MP and open.mp handle game traffic, query and RCON over one single UDP port. This guide shows what you can secure yourself, and from which attack size only the filtering in the network in front of the server still helps.

An SA-MP or open.mp project usually grows along the same curve: the player count climbs, the server moves up the list, and a few days later connections start dropping in batches. The longest part of this guide covers what you can change on your own server at no extra cost. After that comes the point where those measures technically run out, and only then what KernelHost does about it.

Why SA-MP and open.mp in particular get hit so often

The scene is small and competitive. Many roleplay and freeroam servers compete for the same players, and the inhibition threshold for knocking a rival off the list for a few hours is low. On top of that come banned players and extortion attempts against projects that run their own shop.

Technically, the game makes it easy for attackers. All game traffic runs over UDP, and UDP has no handshake that would cost the attacker anything; source addresses can be forged on top of that. The server list entry publishes address and port, so no reconnaissance is needed up front. And because most projects run on a single server, the game server, the database, the user panel and often the voice server all sit on the same address: one hit takes everything down at once.

The ports and protocols involved

  • SA-MP server: UDP 7777 by default, configurable through port in server.cfg.
  • open.mp server: UDP 7777 as well by default, configurable through network.port in config.json.
  • Query: the same UDP port. There is no separate query port. Server browsers, status pages and Discord bots talk to the very port people play on.
  • RCON: the same UDP port again, as its own opcode inside the query protocol, in plain text.
  • The list entry goes outbound to the respective server list, so nothing has to be opened inbound for it.
  • Everything else on the same machine: SSH on TCP 22, MariaDB or MySQL on TCP 3306, the user panel on TCP 80 and 443.

The consequence: you cannot separate query access from game traffic with a firewall, because both sit on the same port. Block UDP 7777 and you block your own players.

A query request starts with eleven bytes: four bytes of signature, four bytes of server address, two bytes of port, one byte of opcode. The opcode decides the answer: i returns the server information, r the rules, c a short player list, d a detailed player list with name, score and ping per player, p echoes four bytes back for ping measurement, x is RCON. On a busy server, eleven bytes of request produce several kilobytes of answer. That makes an open query interface interesting twice over: as a target and as an amplifier against third parties. The article What is a DDoS attack? explains what is behind that attack pattern.

What you can do yourself before spending money

The following steps cost nothing and work against the attacks that make up everyday life: join floods, query floods and individual sources with a high packet rate. All commands assume root, otherwise put sudo in front.

1. Taking stock: what is listening at all?

ss -lnup
ss -lntp

Anything listening on 127.0.0.1 or ::1 needs no firewall rule. Anything bound to 0.0.0.0 or [::] can be reached from outside and needs a reason to be there.

2. Close everything the game does not need

A packet filter does not remove a volumetric attack, but it does shrink the attack surface. A solid starting configuration with UFW:

ufw allow 22/tcp comment 'SSH'
ufw allow 7777/udp comment 'SA-MP / open.mp'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
ufw status verbose

The order is not accidental: the allow rules come before you switch the firewall on, otherwise you lock yourself out. Details including the way back are in the article Setting up the UFW firewall. On KVM root servers and dedicated servers from KernelHost you get back onto the system in an emergency through the VNC console in the customer panel.

The database does not belong on the open internet. If ss -lntp | grep 3306 shows a 0.0.0.0:3306, set bind-address = 127.0.0.1 and restart the service. Bind the game server to a fixed address as well, in SA-MP through bind, in open.mp through network.bind.

3. Defusing query access without locking out your players

In server.cfg, SA-MP offers the switch query 0, after which the server answers no query at all. It works, but the price is high: the server disappears from the browser, player count and rules can no longer be read, and status pages and Discord bots show it as offline. For a closed circle that is an option, for a growing project it is not. In open.mp the same switch sits in the network section of config.json; check the exact key name in your version instead of guessing it.

The realistic route is therefore to limit rather than to switch off. This filter rule shows only the packets carrying the query signature, live:

tcpdump -ni any -c 100 'udp port 7777 and udp[8:4] = 0x53414d50'

Which source addresses send the most traffic to the game port:

tcpdump -nn -q -c 2000 'udp dst port 7777' 2>/dev/null \
  | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head -20

4. Setting rate limits in the network stack

With nftables you cap the packet rate per source address. The following set creates a table of its own so that it does not get in the way of UFW:

nft add table inet gameguard
nft add chain inet gameguard input '{ type filter hook input priority -150 ; policy accept ; }'
nft add rule inet gameguard input udp dport 7777 meter perip '{ ip saddr limit rate over 60/second burst 120 packets }' drop
nft list table inet gameguard

It also makes sense to add an upper limit for the port as a whole, so that a widely distributed flood does not slip through the gap between many individual sources:

nft add rule inet gameguard input udp dport 7777 limit rate over 20000/second burst 5000 packets drop

With iptables the hashlimit module achieves the same thing:

iptables -N SAMPGUARD
iptables -A INPUT -p udp --dport 7777 -j SAMPGUARD
iptables -A SAMPGUARD -m hashlimit --hashlimit-name samp --hashlimit-mode srcip \
  --hashlimit-above 60/sec --hashlimit-burst 120 --hashlimit-htable-expire 30000 -j DROP

These numbers are starting points, not a recommendation for your server. A single player generates a few dozen packets per second from position synchronization alone; in SA-MP you control that rate through onfoot_rate, incar_rate and weapon_rate. It gets critical when several players sit behind the same address, for example in one household or behind the carrier-grade NAT of a mobile provider. A limit that is too tight throws out exactly those players, and that looks like an attack. Measure first, then set the value, then watch the disconnects.

5. Limits in server.cfg and config.json

Both implementations ship protective limits of their own, which are often left at their defaults. For SA-MP, in server.cfg:

lanmode 0
query 1
announce 1
rcon 0
conncookies 1
connseedtime 300000
minconnectiontime 1000
messageslimit 500
messageholelimit 3000
ackslimit 3000
playertimeout 10000

For open.mp the same values live in config.json:

{
  "network": {
    "port": 7777,
    "bind": "",
    "use_lan_mode": false,
    "cookie_reseed_time": 300000,
    "minimum_connection_time": 1000,
    "messages_limit": 500,
    "message_hole_limit": 3000,
    "acks_limit": 3000,
    "player_timeout": 10000,
    "limits_ban_time": 60000
  },
  "rcon": {
    "enable": false
  }
}

What these values do:

  • Connection cookies (conncookies and cookie_reseed_time respectively) require the client to answer a challenge before a slot is taken. A forged source address never gets to see that challenge and therefore cannot answer it. This is the most effective built-in brake against connection floods, so leave it switched on.
  • A minimum interval between connection attempts (minconnectiontime and minimum_connection_time respectively, in milliseconds) stops the same address from opening new connections once a second. Against bot joins this is the second important adjustment.
  • Message, hole and acknowledgement limits (messageslimit, messageholelimit, ackslimit) cap how much an established connection is allowed to send. They protect against manipulated clients, not against volume.
  • The timeout (playertimeout, player_timeout) decides how long a silent connection keeps a slot occupied. A low value frees slots faster during a join flood, but also drops players on a poor connection sooner. The ban duration (limits_ban_time in open.mp) defines how long a suspicious address stays locked out.

Two notes: config.json has to stay valid JSON, and one comma too many stops the server from starting. And open.mp adds missing defaults by itself on startup, so edit the file while the server is stopped.

One more word on RCON: the password travels over UDP in plain text and can be read all along the way. If you do not need RCON, switch it off with rcon 0 or "enable": false, otherwise the rule is a long random password and access only through a VPN.

6. Defenses in the gamemode and in the plugins

SA-MP calls OnIncomingConnection before a player slot is taken. That is where you can count along and block suspicious addresses temporarily:

public OnIncomingConnection(playerid, ip_address[], port)
{
    if (ConnectAttemptsTooHigh(ip_address))
    {
        BlockIpAddress(ip_address, 60000);
    }
    return 1;
}

ConnectAttemptsTooHigh is deliberately your own counting function: sensible thresholds depend on your player count. BlockIpAddress expects the ban duration in milliseconds, UnBlockIpAddress lifts it early. The block list lives in memory (RAM) and is empty after a restart.

Two more tools belong in every project. The plugin crashdetect shows the affected function and the line in the gamemode when a crash happens; without it, a runtime error in your own code looks like an attack from the outside. A well-maintained anti-cheat such as Nex-AC covers client-side manipulation, but works exclusively on connected players inside the game logic. A flood of forged packets never becomes a player and passes it by. Those are two different problems.

Keep your includes and plugins up to date as well: several known SA-MP crash methods rely on out-of-range values being passed to native functions. And never hand unchecked player input to SendRconCommand or to a database query.

7. The server list entry and your real address

The list entry makes you findable, for players and for attackers alike. With announce 0 you disappear from both lists, and with them from your organic influx of players. That is a trade-off, not an insider tip.

Putting a domain in front does not help: the client resolves the name once and then talks to the address directly, and anyone can resolve that name. Check instead what else gives your address away: old A and AAAA records in DNS, the user panel on the same machine, the status display of a Discord bot, an exposed database web interface, TLS certificates carrying old hostnames, and forum posts from the early days.

From that follows a rule many projects learn too late: when you move to a protected address, change the origin address at the same time. Otherwise the old one sits in every scanner database, and the attack simply walks past the protection.

8. Whitelists and closed operation

For the game port a whitelist is rarely practical, because players come from changing addresses. A server password (password in both implementations) turns the server into a closed circle with no effort, while the list entry stays in place. For the administrative services a whitelist is mandatory, so for SSH, the database, the panel and, if you keep it, RCON:

ufw allow from 203.0.113.10 to any port 22 proto tcp comment 'Admin'
ufw delete allow 22/tcp
ufw status numbered

If your own connection address changes, a VPN is cleaner than an ever-growing exception list.

9. Logging: measure first, act second

SA-MP writes its log to server_log.txt in the server directory, open.mp to the file configured in the logging section. Which addresses knock most often:

grep "Incoming connection" server_log.txt \
  | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
  | sort | uniq -c | sort -rn | head -20

A high number of requested connection cookies points to a join flood:

grep -c "requests connection cookie" server_log.txt

The most telling value, though, is not in the game log but in the kernel. If the server process does not pull the packets out of the receive buffer fast enough, the kernel counts the losses for you:

nstat -az | grep -E 'Udp(InDatagrams|InErrors|NoPorts|RcvbufErrors)'
ip -s -s link show

That gives you the single most important distinction of all: if the buffer errors rise while CPU load stays low, more traffic is arriving than the process can work through. If instead one core is pinned while the traffic looks normal, the problem is in the gamemode and not in the network. The article Detecting a DDoS attack on the server describes how to tell the two cases apart.

Where these measures stop working

Every step so far only takes effect once the packets have already crossed your line: the kernel drops them after they arrive. That sets a hard ceiling which has nothing to do with the quality of your rules.

A 1 Gbps uplink accepts around 1.49 million packets per second at the smallest possible packet size, and physically nothing more fits through. For comparison, two attacks that were measured and filtered on KernelHost servers: over 473.4 Gbps at over 41.5 million packets per second against a voice server on UDP 9987, and over 112.2 Gbps at over 8.7 million packets per second against a game server on UDP 7777. The first case is roughly 473 times the bandwidth and about 28 times the packet rate a 1 Gbps line can take at all. Even a perfect filter on the server changes nothing there, because the packets never reach it: the uplink in front of it is full, and the packets of your players go down with it.

Two further limits bite even earlier. First, the game server reads the port in a single thread. A query flood can keep that thread so busy that the synchronization packets of real players expire in the receive buffer, long before the line is saturated. The process does not crash, it only gets slow, and the players see rubberbanding. Second, source addresses are forgeable with UDP; bans by address then hit uninvolved people and miss the attacker entirely.

To put it plainly: your work on the server decides whether a small attack gets through. Whether a large attack gets through is decided by the network in front of the server.

What KernelHost does about it

Included on every server: the two-layer always-on protection

Every server at KernelHost sits behind permanently active, two-layer filtering:

  • Layer 1: a global scrubbing network with 17 Tbps of mitigation capacity. Volumetric attacks are intercepted and scrubbed close to their source, before they ever reach the datacenter in Frankfurt am Main.
  • Layer 2: Arbor real-time filtering with 3.2 Tbps on site in Frankfurt am Main. Directly in front of the server, protocol-specific patterns are detected and dropped packet by packet.

Three properties are decisive here. The protection is permanently active, so there is no detection phase during which your server goes offline. No null-routing is used: the attacked address stays in the network and only the malicious packets are dropped, while the connections of real players keep running. And it costs nothing extra, because it is included in every server package, from the KVM root server through the game server to the dedicated server. Filtering happens on layers 3, 4 and 7 on every TCP or UDP port, so on UDP 7777 as well. All of this runs in the maincubes datacenter in Frankfurt am Main, Germany, operated by KernelHost GmbH, based in Vienna, Austria. The article Game server DDoS protection in real time shows which games and protocols have profiles of their own.

For projects under constant fire: Advanced DDoS Protection

Some projects are not hit occasionally but targeted for weeks on end. For that case there is Advanced DDoS Protection from €50.00 per month, PrePaid and with no minimum term. It adds three things to the always-on protection:

  • A dedicated protected IP from the Frankfurt core. Your server is switched over to it inside the KernelHost network, so you rebuild nothing on your side.
  • Self-managed protection rules per port and protocol in the customer panel. Changes take effect in real time, with no ticket and no waiting, so you can fine-tune in the middle of an attack.
  • A protection profile that matches the game. Ready-made profiles for more than 40 games, services and protocols, among them SA-MP and open.mp as well as your own TCP and UDP applications. The user panel, the voice server and a VPN fit behind the same protected address too.

The two layers compared

Feature Included always-on protection Advanced DDoS Protection
Price no surcharge, part of every server package from €50.00 per month, PrePaid
Activation active from provisioning, nothing to set up order it, receive the protected IP, the server is switched over
Filtering capacity 17 Tbps global scrubbing, plus 3.2 Tbps Arbor real-time filtering in Frankfurt am Main the same infrastructure, extended with your own rules
Address the server IP of the package an additional dedicated protected IP
Rule management preconfigured and automatic self-managed in the customer panel per port and protocol, changes take effect in real time
Protection profiles automatic pattern detection profile selectable per game, more than 40 games and protocols
Null-routing no no
Suited to the normal case, including occasional attacks projects under constant, targeted fire
Term tied to the server package PrePaid, no minimum term, no notice period

Common mistakes and how to fix them

"The server is gone, so it must be an attack." Check first whether the process is still running. A runtime error in the gamemode looks identical from the outside. With crashdetect the cause is in the log, without it you are guessing.

"We blocked the query port." There is no separate query port. Block UDP 7777 and you block the game itself. What is actually meant is either query 0 (the server disappears from the list) or a rate limit on that same port.

"We changed the IP and we are back online." Without closing the leak, the new address is public again within hours. Old DNS records, the panel on the same machine and the status display of a Discord bot give it away reliably.

"We set a rate limit of 20 packets per second per address." That is too tight. A single player is already above it, and several players behind one NAT address share the same budget. What you are doing is throwing out your own players.

"We locked ourselves out with the firewall." A reboot does not help, because UFW restores its rules at boot. At KernelHost you open the VNC console in the customer panel and run ufw disable there. KVM root servers and dedicated servers have no IPMI and no iDRAC, so the way in leads through the VNC console.

"The RCON password is in the team chat." RCON runs over UDP in plain text and can be read all along the way. If you do not need it, switch it off, otherwise the rule is a long random password and access only through a VPN.

"We will simply sit the attack out." Attacks that work get repeated. Document the time, the duration, the peak values and the affected ports. Those are exactly the details a support ticket needs so that the filtering can be tightened where it matters.

If you are under attack right now

If your project already runs at KernelHost, the filtering is permanently active and there is nothing for you to switch on. If you still notice something unusual, open a support ticket with the time window, the port and the behavior you observed, so the rules for your address can be fine-tuned. During an ongoing attack you can also reach us on the WhatsApp emergency chat at +43 650 8209883.

Frequently asked questions

Which port does an SA-MP or open.mp server run on?
On UDP 7777 by default, configurable through 'port' in server.cfg for SA-MP and through 'network.port' in config.json for open.mp. Query and RCON use the same port, there is no second one.
Can I block query access without blocking the server?
Not with a firewall, because game traffic and query sit on the same port. In SA-MP you can switch the query off entirely with 'query 0', but then the server disappears from the list. The practical route is a rate limit on UDP 7777.
My server is gone: attack or crash?
Check first whether the process is running. If the UDP buffer errors (nstat -az | grep Udp) rise while CPU load is low, more traffic is arriving than the process can work through. If one core is pinned while traffic looks normal, the gamemode is the cause. The crashdetect plugin then names the line.
Which settings slow down a join flood immediately?
Leave connection cookies switched on (conncookies and cookie_reseed_time respectively) and set a minimum interval between connection attempts (minconnectiontime and minimum_connection_time respectively, in milliseconds). Both take effect before a slot is taken.
Is a firewall on the server enough against DDoS attacks?
Against small floods yes, against large ones no. A 1 Gbps uplink accepts around 1.49 million packets per second. Attacks measured in practice reach over 41.5 million packets per second. The packets then never even get as far as the filter on the server.
Does changing the IP address help?
Only together with the cause. Old DNS records, the user panel on the same machine and the status display of a Discord bot make the new address public again within hours. Address and leak have to be dealt with together.
Is DDoS protection included in the price at KernelHost?
Yes, in every server package with no surcharge and permanently active. It works in two layers: 17 Tbps of mitigation capacity in the global scrubbing network and 3.2 Tbps of Arbor real-time filtering on site in Frankfurt am Main. Null-routing is not used, so the attacked address stays in the network.
When is Advanced DDoS Protection worth it?
When a project is under constant, targeted fire. It costs from €50.00 per month, PrePaid with no minimum term, and gives you a dedicated protected IP plus protection rules per port and protocol that you manage yourself in the customer panel. Changes take effect in real time, and there are protection profiles for SA-MP and open.mp as well.

SA-MP open.mp DDoS protection Game server UDP 7777 Query port Rate limit Frankfurt am Main