Blocking UDP Floods on a CS 1.6 Server with iptables and ipset

March 12, 2026 Daemon666 8 min read 12 Aufrufe

A CS 1.6 server listens on UDP 27015, and UDP is trivially spoofable, so floods are a fact of life: query-spam that pins your CPU, connection floods that fill slots, and outright volumetric attacks. Host-level iptables with ipset will not stop a large volumetric DDoS — that has to be scrubbed upstream — but it handles the everyday junk that would otherwise degrade your server, cheaply and without touching the game. Here is a practical filter set.

Be careful with per-IP limits: many legitimate players sit behind the same carrier NAT or share a home connection, so set thresholds generously and watch for false positives before tightening.

1. Rate-limit the game port with hashlimit

The hashlimit match tracks packet rates per source IP without a separate rule per address. Cap how fast any single IP may hammer the game port:

iptables -A INPUT -p udp --dport 27015 \
  -m hashlimit \
  --hashlimit-name cs_flood \
  --hashlimit-mode srcip \
  --hashlimit-above 40/sec \
  --hashlimit-burst 60 \
  -j DROP

This drops packets from any IP exceeding 40/sec (after a 60-packet burst). Tune the numbers to your traffic — legitimate gameplay is well under that, but a busy query period can spike, so measure before you clamp hard.

2. Absorb A2S_INFO query floods

Server-browser queries (A2S_INFO / A2S_PLAYER) are small requests that trigger larger replies, which is why they get abused for both CPU exhaustion and reflection. Rate-limit query-sized inbound packets separately from gameplay so a query flood cannot starve real players:

iptables -A INPUT -p udp --dport 27015 -m length --length 0:32 \
  -m hashlimit \
  --hashlimit-name cs_query \
  --hashlimit-mode srcip \
  --hashlimit-above 5/sec \
  --hashlimit-burst 10 \
  -j DROP

Small packets are almost always queries or connection attempts; capping them per IP kills query-spam while leaving in-game traffic (larger packets) alone.

3. Maintain a drop list with ipset

Matching against thousands of blocked IPs one rule at a time is slow. ipset stores them in a hash the kernel checks in near-constant time. Create a timed blacklist and drop it at the top of INPUT:

ipset create cs_blacklist hash:ip timeout 3600
iptables -I INPUT -m set --match-set cs_blacklist src -j DROP

Now any process that detects an abuser can ban it for an hour with one command, and the entry expires itself:

ipset add cs_blacklist 203.0.113.7 timeout 3600

Wire your abuse detection (a log watcher, or a hashlimit LOG rule) to feed this set. The timeout means you never have to remember to unban.

4. Drop obviously invalid traffic

Cheap rules that shed junk before it reaches the game:

# invalid/malformed conntrack state
iptables -A INPUT -p udp --dport 27015 -m state --state INVALID -j DROP
# traffic to ports you do not run
iptables -A INPUT -p udp --dport 27016:65535 -j DROP

Keep these narrow — a rule that is too broad will drop legitimate players. Only block ranges you genuinely do not use.

5. Make it survive a reboot

iptables and ipset rules are in-memory. Persist them so a reboot does not drop your protection:

ipset save  > /etc/ipset.rules
iptables-save > /etc/iptables/rules.v4

Restore ipset before iptables on boot, because the iptables rule references the set — if the set does not exist yet, the rule fails to load.

Troubleshooting

  • Legit players get dropped during busy periods — your --hashlimit-above is too aggressive, or several players share one NAT IP. Raise the threshold and burst.
  • Rule fails: "Set cs_blacklist doesn't exist" — the iptables rule loaded before the ipset. Create/restore the set first.
  • Server still lags under attack — this is volumetric and saturating your uplink; host filtering cannot help once the pipe is full. You need upstream scrubbing — see DDoS-protected hosting.
  • Rules vanish after reboot — you did not persist them, or ipset restored after iptables. Fix the boot order.
  • Everything is blocked — a broad DROP range caught the game port itself. Check your port ranges against the CS 1.6 port list.

Verification

Watch the counters while under load — a working rule shows its packet count climbing:

iptables -L INPUT -v -n --line-numbers
ipset list cs_blacklist

Generate a controlled burst of queries from a test host and confirm the query-limit rule's counter rises while a normal player still connects and plays without choke. If the DROP counters stay flat during an obvious flood, your match conditions are not catching the traffic — recheck the port and packet-length filters. For attacks that saturate the link regardless, escalate to upstream DDoS protection.

Mitwirkende: Daemon666 ✦
Teilen: