UDP does not ask permission
TCP opens a connection by agreement. Three packets are exchanged, and a server that is unwilling or unable to talk can decline before anything is allocated. That handshake is a cost for the attacker and a decision point for you.
UDP has neither. RFC 768 is three pages long and describes an eight-byte header stapled to a payload: source port, destination port, length, checksum. No handshake, no sequence numbers, no acknowledgements, no state at either end. A sender transmits; whether anything is listening is not its problem.
That is the entire attack. A UDP flood is simply a very large number of UDP packets sent at your address. There is no protocol step to fail, no queue that fills with half-finished work, no clever abuse of a specification. The first thing your server can do about a UDP packet is receive it — and receiving it is already the damage.
This is why UDP floods remain, decades on, one of the most common attack shapes in the wild: they need no vulnerability, no reconnaissance, and nothing more sophisticated than bandwidth.
Two resources fail, and they hide from each other
"UDP flood" describes a method, not a size, and the same method breaks three different things depending on how the packets are shaped.
| Flood shape | Typical packet | What runs out first | Measured in |
|---|---|---|---|
| Bandwidth exhaustion | 1,000–1,500 bytes, often padded to the MTU | Your uplink, or the shared host port you sit behind | Gigabits per second |
| Packet-rate exhaustion | 64–128 bytes | Interrupt and softirq budget, per-packet rule evaluation, connection-tracking inserts | Millions of packets per second |
| Service exhaustion | Whatever the real protocol on that port expects | The application's receive buffer and its own parsing cost | Requests per second |
The arithmetic is worth internalising, because a flood optimised for one of these is close to invisible on the graph you would use to spot the other. A 64-byte Ethernet frame occupies 84 bytes of wire time once the preamble and inter-frame gap are counted, so:
| Packets/sec (64-byte) | Wire rate | What it typically takes out first |
|---|---|---|
| 100,000 | ~67 Mbps | A small VM's virtual NIC; any single-core packet path |
| 1,000,000 | ~0.67 Gbps | Connection tracking, and most software firewalls doing per-packet rule chains |
| 5,000,000 | ~3.4 Gbps | Mid-range appliances and load balancers |
| 14,880,000 | 10 Gbps — line rate | Everything that is not line-rate filtering upstream of you |
Read the second column again. A packet rate that destroys most origins is a bit rate your uplink will not notice. If your monitoring plots megabits and not packets per second — which is the default nearly everywhere — a packet-rate flood looks like your server mysteriously becoming unreachable while the traffic graph stays calm. That misdiagnosis sends people restarting services for an hour.
The inverse case is the one people expect: 1,500-byte packets at the same 10 Gbps are only about 820,000 packets a second, trivial to process and completely fatal, because the link is full before any of them reach software at all.
Direct floods, reflection and amplification
The packets have to come from somewhere, and the three ways of arranging that differ in what you can do about them.
| Variant | What arrives | Why attackers choose it | What you can block |
|---|---|---|---|
| Direct flood | UDP from the botnet's own addresses | Simplest to launch; works with any rented capacity | The sources — this is the only variant where a blocklist is meaningful |
| Spoofed flood | UDP with forged source addresses, usually random per packet | There is no attacker address to block, and every per-source counter you keep sees one packet per "client" | Nothing by source; only by shape, port and protocol validity |
| Reflection / amplification | Large replies from innocent public servers | The attacker spends 1 byte and you receive 50 | The ports the reflectors answer from — the replies are unsolicited by definition |
Spoofing is possible at all because UDP never verifies the return address, and because ingress filtering (BCP 38) — the practice of networks refusing to forward packets with source addresses that cannot belong to their customers — was first recommended in 1998 and re-issued as a best current practice in 2000, and remains unevenly deployed a quarter of a century later.
Amplification is the industrial version of the same primitive, and it has its own page: DNS amplification attacks covers the reflector ecosystem, the amplification factors by protocol, and why the flood arrives from thousands of blameless servers. The distinction that matters here is that a direct UDP flood needs no reflectors, so there is no clean "unsolicited reply from source port 53" signature to filter on. The packets look like what they are: traffic to a port, from somewhere.
A third of the way between them, and increasingly common, is the subnet-spread flood: the same aggregate volume divided across every address in your /24 rather than concentrated on one host. Per-IP detection thresholds never trip, while the aggregate still saturates the upstream link everyone in that range shares.
The ICMP reply is part of the attack
Here is the detail that makes UDP floods uniquely self-reinforcing, and the one most server guides omit.
When a UDP packet arrives at a port where nothing is listening, a correctly behaving host does not stay silent — it replies with ICMP type 3, code 3: destination unreachable, port unreachable. The flood is therefore not merely ignored. Each packet costs you a reply: a new packet constructed, a route looked up, egress bandwidth consumed.
And if the source addresses were spoofed, those replies go to strangers. Your server becomes a reflector in someone else's direction — the phenomenon known as backscatter — which is how a victim ends up receiving abuse complaints during an attack on itself.
Linux rate-limits these replies (net.ipv4.icmp_ratelimit, with icmp_ratemask selecting which types are limited), which bounds the damage but does not remove the per-packet cost of deciding to suppress them. The practical consequence is a rule of thumb worth writing down: filter unwanted UDP with drop, never reject. A reject is an ICMP reply with extra steps.
The second quiet multiplier is connection tracking. Stateful firewalls track UDP "flows" by four-tuple even though UDP has no connections, and the default nf_conntrack_udp_timeout is 30 seconds. A spoofed flood with a random source address and port on every packet therefore creates a new table entry per packet, each held for half a minute. Against a default nf_conntrack_max of 262,144, a flood of one million packets per second fills the table in roughly a quarter of a second — after which the kernel drops traffic indiscriminately, including the connections carrying your real visitors. The log line is nf_conntrack: table full, dropping packet, and it is routinely misread as a server fault.
Confirming that it is a UDP flood
Two minutes of evidence before you change anything:
- Count packets to closed ports.
nstat -az | grep -i udpornetstat -su. A climbingUdpNoPortsmeans packets are arriving for ports you do not serve — close to diagnostic on its own.RcvbufErrorsandInErrorsclimbing instead means the flood is hitting a port you do serve, and the application cannot drain its socket fast enough. - Know what you actually listen on.
ss -lunlists your real UDP listeners. Most servers are surprised by this output. - Compare packet rate against bit rate.
sar -n DEV 1orip -s link. High packets with flat bandwidth means a packet-rate flood; pinned bandwidth at an ordinary packet rate means large packets or amplification. - Check the NIC, not just the kernel.
ethtool -S eth0 | grep -iE 'drop|miss|fifo'. Risingrx_droppedorrx_missed_errorsmeans packets died in the ring buffer before the kernel ever saw them, which no firewall rule can influence. - Look at 100 packets.
tcpdump -ni eth0 udp -c 100 -nn. Identical payload lengths and a fixed destination port say "generator". Source ports of 53, 123, 389, 1900 or 11211 say "reflection", and send you to the amplification guide instead. - Read your application log — it will be empty. If you serve a website or an API, your traffic is TCP. A UDP flood produces no entries at all: no slow requests, no errors, nothing. An access log full of plausible requests during an outage means you are looking at a Layer 7 attack, which is a different problem with a different answer.
That last check is the fastest fork in the road, and getting it wrong sends people writing WAF rules while a packet flood keeps the machine off the network.
What you can do on the server, and the ceiling it hits
All of this is worth doing on any internet-facing host. None of it decides the outcome, which is the point of the next section.
Default-deny UDP, with drop. Permit the handful of ports you genuinely serve and silently discard the rest. This removes the ICMP reply cost and the backscatter at once.
Keep UDP out of connection tracking. A notrack rule for your public UDP ports — or no stateful tracking on that interface at all — takes the table off the list of things that can fail first. Raising nf_conntrack_max helps proportionally; exempting the traffic helps categorically.
Give the NIC room and more than one core. ethtool -G raises the receive ring, and RSS/RPS spreads interrupts across cores so a flood does not pin core 0 while fifteen others idle. A single-queue virtual NIC is a hard ceiling that no sysctl moves.
Size the receive buffer of the service you do run. net.core.rmem_max plus an explicit SO_RCVBUF buys a deeper queue for bursts. It buys nothing for a sustained flood; it only moves where the drop happens.
Drop in the driver, not in the stack. An eBPF program at the XDP hook discards a packet before an skb is even allocated, which is roughly an order of magnitude cheaper than an iptables rule. If you are going to filter locally, filter here.
Per-source rate limits work against a direct flood from real addresses and do essentially nothing against a spoofed one, because every packet presents a different "source".
Taken together these might raise the packet rate you survive by ten or twenty times. What none of them change is the sentence that governs every volumetric attack: once the packet is on your link, the bandwidth is already spent. Every rule you add is work you perform per attack packet, on hardware that an attacker can out-scale for a few euros an hour. This is the same ceiling described in what DDoS mitigation actually is, and UDP is where it is hit hardest.
There is one more consequence worth planning for before it happens. Most hosting providers protect their own network before they protect you: when an attack on your address threatens shared capacity, the standard response is to null-route the IP — discard all traffic to it, upstream, for an hour or several. Your server is then perfectly healthy, idle, and unreachable. That is not a provider failing; it is the documented policy of nearly every budget host, and it is why "my origin has a firewall" is not a plan.
Where a UDP flood is actually stopped
Upstream, and in a specific order — cheapest decision first, because a defense costing more per packet than the attack does is only a slower way to lose.
- Absorption capacity in the carrier network. The flood has to land somewhere with room for it, before the last mile that leads to you. For volumetric UDP there is no substitute for this, and no amount of cleverness replaces it.
- Stateless filtering at line rate. UDP to a port nothing serves, a reply from a reflector port that was never queried, a malformed length field, a fragment that cannot be reassembled: all droppable on sight, in the driver, with no state kept and no memory allocated. This is where tens of millions of packets per second are dealt with.
- Protocol identity per flow. A filter that knows which protocol legitimately belongs on a port can discard anything that is not that protocol in the first packet, rather than rate-limiting a port it does not understand.
- No exposed UDP service at all. The strongest defense is structural rather than clever. If the thing you are protecting speaks HTTP, there is no reason for any UDP port on your origin to be reachable from the internet — and a service that cannot be addressed cannot be flooded.
- Per-source caps and kernel drops for proven sources, which matter for unspoofed floods and are a waste of table space against spoofed ones.
And one prerequisite that sits outside the list because it silently voids all of it: your origin must be unreachable except through the protection network. If an attacker can still address packets to your server's real IP, none of the layers above are in the path — the flood goes around them. That is the most common reason protection "did not work", and the full checklist is in how to hide your origin IP address.
The QUIC exception
"Block all inbound UDP" has been safe advice for most web servers for thirty years. HTTP/3 ends that: QUIC runs over UDP, normally on port 443, so a modern web stack may genuinely need a UDP listener.
QUIC was designed with this in mind — an endpoint must validate the client's address before sending much, via a Retry packet, and RFC 9000 caps what a server may send to an unvalidated address at three times what it received, which bounds reflection by specification. What it does not bound is the per-packet cost on your side: a QUIC endpoint performs real cryptographic work early in a connection, so a flood of plausible initial packets is a CPU attack as much as a bandwidth one. If you serve HTTP/3 from your own hardware, that UDP port deserves the same upstream treatment as everything else on this page.
How Itnetic handles UDP floods
Worth being precise here, because UDP is the place where it is easiest for a vendor to imply more than it does.
| Layer | What it does |
|---|---|
| Upstream absorption | Volumetric L3/L4 floods are soaked up before our edge, let alone yours: Frankfurt sits behind X4B, a network with 500 Gbps of mitigation capacity; Prague runs on Eternyx behind its own network-level protection; Toronto and Singapore run on Linode (Akamai) behind Linode's network-level protection. The current list is on the network page |
| No UDP surface to flood | Itnetic's edge serves HTTP and HTTPS — websites, APIs and WebSocket traffic. Visitors arrive at the point of presence nearest them; your origin is then firewalled to accept only the protection network. The published address is one with absorption capacity behind it, and your origin has no internet-facing UDP service left to attack |
| Packet filtering in the driver | An eBPF filter at the XDP hook enforces protocol identity per flow and drops packet floods before a socket is allocated, so an attacker's packets cost us far less than they cost them |
| Kernel drops for proven sources | Repeat offenders are held in an nftables set and cost nothing per packet from then on |
| Evidence | A packet flood generates no requests, so the honest artifact is the absence: the attack timeline shows origin health flat through the window, next to the Layer 7 wave that so often arrives with it — because multi-vector attacks are the norm, not the exception |
Two commercial details matter as much as the packet path. Scrubbed attack traffic is never metered against your bandwidth quota, on any plan including the free one, so a week-long flood does not arrive as an invoice. And every feature is on every plan — WAF, custom rules, rate limiting, waiting room, per-request logs — so an incident never turns into an upgrade decision. Going live is two DNS records at your existing provider: no nameserver move, about five minutes, one hostname at a time if you want to watch it first. Itnetic's DDoS protection has the rest of the mechanics, and pricing has the tier lines, such as they are.
And the limitation, stated plainly. If the thing you need online speaks UDP directly to the internet — a Minecraft Bedrock server on RakNet, a Source-engine game server, a voice server, a QUIC endpoint on your own hardware — then an HTTP reverse proxy is the wrong shape of product, ours included. Itnetic's game protection is a filtering proxy for Minecraft Java Edition, which is TCP; Bedrock is not supported today. For arbitrary UDP services you want a provider that will announce your prefix over BGP or give you a UDP forwarding path, and you should discount anyone who tells you otherwise. If what you are protecting is a Minecraft Java server, start with Minecraft server DDoS protection and ping floods and server queues instead.
A seven-point checklist
- Run
ss -lunand write down every UDP port you actually serve. Default-deny the rest withdrop, neverreject. - Put packets per second on a dashboard next to bandwidth, and alert on both. You cannot respond to the metric you do not plot.
- Add
UdpNoPortsandRcvbufErrorsfromnstatto that dashboard — they tell you which of the two floods you are in. - Exempt your public UDP ports from connection tracking, or verify
nf_conntrack_maxagainst a flood rate rather than a traffic rate. - Check
ethtool -Sfor ring-buffer drops, raise the receive ring, and confirm interrupts are spread across more than one core. - Firewall the origin to accept traffic only from your protection network — otherwise every upstream layer is optional from the attacker's point of view.
- Read your host's DDoS policy today and find out how long they null-route an attacked address. Knowing that number before an incident changes what you build.
If you are in the middle of one right now, stop reading and work through how to stop a DDoS attack; it is written for the incident rather than for the reading list. When the wave has passed, how to test your DDoS protection covers how to confirm — safely and legally — that the next one lands somewhere other than your uplink.