# Lexical elements

> Every reserved word of the moat language explained with an example, plus comments, names and literal values: integers, addresses, prefixes, ranges, lists, durations and rates.

Source: https://moatfw.dev/docs/lexical-elements

A moat file is plain text, read top to bottom. This one uses most of the
language; every word in it is explained below.

```moat
# 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 called `banned`.
- Field names such as `ip.src` and `tcp.dport` are names too, built in.
  The [Conditions](https://moatfw.dev/docs/conditions) page lists them all.
- Upper and lower case are different: `Banned` is not `banned`.

## 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](https://moatfw.dev/docs/declarations) and [Modules](https://moatfw.dev/docs/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](https://moatfw.dev/docs/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](https://moatfw.dev/docs/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](https://moatfw.dev/docs/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](https://moatfw.dev/docs/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](https://github.com/moatfw/moat/blob/main/docs/spec/grammar.ebnf).
