Language Lexical elements
Lexical elements the words and values a moat file is made of.
A moat file is plain text, read top to bottom. This one uses most of the language; every word in it is explained below.
# A game server: drop known bad sources, keep queries in check,
# and ban whoever floods the game port.
version 1
default pass
use std.bogons
const game_ports = {27015, 27020-27030}
set blocklist from "blocklist.txt"
set banned addresses ttl 10m size 100k
var under_attack = false
var query_rate = 20
rule known_bad { when ip.src in blocklist or ip.src in banned then drop }
rule bad_flags { when tcp.flags has {syn, fin} then drop }
rule queries {
when udp.dport in game_ports and payload[0:4] == 0xffffffff
then limit query_rate/s per ip.src burst 40
}
rule floods {
when udp.dport in game_ports and not ip.src in {10.0.0.0/8}
then limit 500/s per ip.src/24 burst 1000 on exceed add ip.src to banned ttl 1h, drop
}
rule probes {
when under_attack and tcp and payload contains "GET /admin"
then drop
}
Comments and names
- A comment starts with
#and runs to the end of the line. It can sit on a line of its own or after code. - A name starts with a letter or
_and may contain letters, digits,_and.:game_ports,banned,under_attack. Names you declare (constants, sets, variables, rules) share one namespace, so a set and a rule cannot both be calledbanned. - Field names such as
ip.srcandtcp.dportare names too, built in. The Conditions page lists them all. - Upper and lower case are different:
Bannedis notbanned.
Reserved words
These 32 words have a fixed meaning and cannot be used as names. They are grouped below by where they appear.
The file
| Word | What it does | Example |
|---|---|---|
version | Says which version of the language the file is written in. Must be the first declaration. | version 1 |
default | The verdict for packets no rule decides, pass or drop. Required once. | default pass |
use | Inserts the rules of a standard module at that place. | use std.bogons |
More in Declarations and Modules.
Constants, sets and variables
| Word | What it does | Example |
|---|---|---|
const | Gives a name to a value, a range or a list, to use in conditions. | const game_ports = {27015, 27020-27030} |
set | Declares a set of addresses or networks that rules test with in. | set banned addresses |
from | Fills a set from a file at load time, one address or prefix per line. | set blocklist from "blocklist.txt" |
prefixes | A set of networks, filled from outside with moat set. | set allowed prefixes |
addresses | A set of exact addresses, filled from outside or by rules with add. | set seen addresses |
ttl | How long entries stay in an addresses set. Also used after add. | set banned addresses ttl 10m |
size | How many entries a set can hold. The default is 65536. | set banned addresses size 100k |
var | A value you can change while the rules run, with moat var set. | var query_rate = 20 |
true, false | The two values of an on/off variable, which is a condition on its own. | var under_attack = false |
More in Sets and variables.
Rules
| Word | What it does | Example |
|---|---|---|
rule | Declares a rule, with a name and a body in braces. | rule known_bad { ... } |
when | Starts the condition: which packets the rule is about. | when udp.dport == 53 |
then | Starts the actions: what happens to those packets. | then drop |
More in Rules.
Conditions
| Word | What it does | Example |
|---|---|---|
and | Both sides must hold. | udp and udp.dport == 53 |
or | Either side is enough. | icmp or icmp6 |
not | Turns a test around. | not ip.src in {10.0.0.0/8} |
in | Tests membership of a set, a list, a range or a prefix. | ip.src in banned |
has | TCP flags: true when the packet has these flags set, maybe with others. | tcp.flags has {syn, fin} |
payload | The bytes after the transport header, whole or as a slice. | payload[0:4] == 0xffffffff |
contains | Looks for text or bytes anywhere in the payload. | payload contains "GET /admin" |
and binds tighter than or; use parentheses when in doubt.
tcp.flags == {syn} asks for exactly those flags, has for at least
them. More in Conditions.
Actions
| Word | What it does | Example |
|---|---|---|
pass | Lets the packet through. No later rule sees it. | then pass |
drop | Drops the packet. No later rule sees it. | then drop |
add | Puts the source (or destination) into an addresses set. The next rule still runs. | add ip.src to banned |
to | Names the set add writes to. | add ip.src to banned ttl 1h |
limit | A rate limit. Under the rate the packet goes on to the next rule; over it, the on exceed actions run, or the packet is dropped. | limit 100/s burst 200 |
per | Gives each source, or each source network, its own budget. | limit 500/s per ip.src/24 burst 1000 |
burst | How many packets can arrive at once before the rate applies. Required. | limit 10/s burst 20 |
on, exceed | Always together: what to do with packets over the rate. | on exceed add ip.src to banned, drop |
Several actions are separated by commas, and pass or drop must come
last. More in Actions.
Values
| Kind | Examples | Notes |
|---|---|---|
| integer | 53, 0xffffffff | Decimal or hexadecimal. A hex literal also has a width in bytes, used by payload comparisons: 0xffffffff is 4 bytes. |
| string | "blocklist.txt", "GET /admin" | Double quoted. Escapes: \" and \\. |
| address | 192.0.2.1, 2001:db8::1 | IPv4 or IPv6. |
| prefix | 10.0.0.0/8, 2001:db8::/32 | A network. Host bits must be zero: 10.0.0.1/8 is an error, and moat check suggests 10.0.0.0/8. |
| range | 8000-8100 | Inclusive, both ends count. |
| list | {53, 123, 1000-2000} | Integers, ranges, addresses, prefixes or TCP flag names. A trailing comma is allowed. |
| duration | 500ms, 30s, 10m, 1h | For ttl. |
| size | 64k, 1M, 2G | Thousand, million, billion, for size. A plain integer works too. |
| rate | 1000/s, 10k/s, 10mbit/s | Packets per second, or bits per second with bit, kbit, mbit, gbit. A variable works too: query_rate/s. |
The TCP flag names used in lists are fin, syn, rst, psh, ack,
urg, ece and cwr.
The formal grammar is in grammar.ebnf.