LIBNFTABLES-JSON(5)

๐Ÿ’– LIBNFTABLES-JSON ๐Ÿ’–

so this is section 5 of the manual, bestie โ€” it's called LIBNFTABLES-JSON, and it's literally the whole JSON schema that libnftables speaks. pull up a chair, grab a snack, we're doing this ๐Ÿ“


๐ŸŒธ NAME ๐ŸŒธ

libnftables-json โ€” the JSON schema supported by libnftables. that's the whole name, no notes ๐Ÿ’…


๐ŸŒˆ SYNOPSIS ๐ŸŒˆ

here's the shape of everything, in grammar form:

{ "nftables": [ OBJECTS ] }

OBJECTS := LIST_OBJECTS | CMD_OBJECTS

LIST_OBJECTS := LIST_OBJECT [ , LIST_OBJECTS ]

CMD_OBJECTS := CMD_OBJECT [ , CMD_OBJECTS ]

CMD_OBJECT := { CMD: LIST_OBJECT } | METAINFO_OBJECT

CMD := "add" | "replace" | "create" | "insert" | "delete" | "list" |
"reset" | "flush" | "rename"

LIST_OBJECT := TABLE | CHAIN | RULE | SET | MAP | ELEMENT | FLOWTABLE |
COUNTER | QUOTA | CT_HELPER | LIMIT | METAINFO_OBJECT | CT_TIMEOUT |
CT_EXPECTATION

โœจ DESCRIPTION โœจ

okay so โ€” libnftables does JSON input and JSON output, both. it's implemented as an alternative frontend to the standard CLI syntax parser, which means the basic behaviour is identical โ€” and for (almost) any operation you can do in standard syntax, there should be an equivalent one in JSON. no cap. ๐Ÿซถ

JSON input can come in two flavours:

  • a single string, passed as the parameter to nft_run_cmd_from_buffer()
  • a file, identified by the filename parameter of the nft_run_cmd_from_filename() function

JSON output, meanwhile, has to be turned on via the nft_ctx_output_set_json() function, which flips the library's standard output into JSON format. heads up though: error output stays exactly the way it was, unaffected. ๐Ÿ’€


๐Ÿฆ‹ GLOBAL STRUCTURE ๐Ÿฆ‹

in general, any JSON input or output is wrapped up in an object with a single property named nftables. its value is an array holding either commands (for input) or ruleset elements (for output).

a command is an object with a single property, and that property's name is what identifies the command. its value is a ruleset element โ€” basically identical to output elements, apart from certain properties which may be interpreted differently, or which are required even though output generally omits them.


๐Ÿ‘‘ METAINFO OBJECT ๐Ÿ‘‘

in output, the very first object in an nftables array is a special one carrying library information. main character energy, honestly. its content looks like this:

{ "metainfo": {
        "version": STRING,
        "release_name": STRING,
        "json_schema_version": NUMBER
}}

the values of version and release_name are equal to the package version and release name as printed by nft -v. and the value of json_schema_version is an integer indicating the schema version.

if you supply this in library input, the parser will verify that the json_schema_version value does not exceed the internally hardcoded one โ€” that's how it makes sure the schema you handed it is fully understood. in future, a lower number than the internal one may activate compatibility mode to parse outdated and incompatible JSON input. ๐Ÿ”ฅ


๐Ÿ’… COMMAND OBJECTS ๐Ÿ’…

the structure accepts an arbitrary amount of commands, and they get interpreted in order of appearance. for instance, this standard syntax input:

flush ruleset
add table inet mytable
add chain inet mytable mychain
add rule inet mytable mychain tcp dport 22 accept

translates into JSON as such:

{ "nftables": [
        { "flush": { "ruleset": null }},
        { "add": { "table": {
                        "family": "inet",
                        "name": "mytable"
        }}},
        { "add": { "chain": {
                        "family": "inet",
                        "table": "mytable",
                        "name": "mychain"
        }}},
        { "add": { "rule": {
                        "family": "inet",
                        "table": "mytable",
                        "chain": "mychain",
                        "expr": [
                                { "match": {
                                        "op": "==",
                                        "left": { "payload": {
                                                        "protocol": "tcp",
                                                        "field": "dport"
                                        }},
                                        "right": 22
                                }},
                                { "accept": null }
                        ]
        }}}
]}

look at her. she ate. ๐ŸŽ€


๐ŸŒŸ ADD ๐ŸŒŸ

{ "add": ADD_OBJECT }

ADD_OBJECT := TABLE | CHAIN | RULE | SET | MAP | ELEMENT |
                FLOWTABLE | COUNTER | QUOTA | CT_HELPER | LIMIT |
                CT_TIMEOUT | CT_EXPECTATION

adds a brand new ruleset element to the kernel. new stuff, new vibes โœจ


๐ŸŒŸ REPLACE ๐ŸŒŸ

{ "replace": RULE }

replaces a rule. and important: inside that RULE, the handle property is mandatory โ€” it's what identifies the rule you're replacing. no handle, no replacement, periodt.


๐ŸŒŸ CREATE ๐ŸŒŸ

{ "create": ADD_OBJECT }

identical to the add command, but it returns an error if the object already exists. it's giving "i will not be doing duplicates today" ๐Ÿ™ˆ


๐ŸŒŸ INSERT ๐ŸŒŸ

{ "insert": RULE }

this one is identical to add for rules, but instead of appending the rule to the chain by default, it inserts at the first position. and if a handle or index property is given, the rule gets inserted before the rule identified by those properties. precision behaviour, honestly ๐Ÿ’…


๐ŸŒŸ DELETE ๐ŸŒŸ

{ "delete": ADD_OBJECT }

deletes an object from the ruleset. generally you only need the minimal number of properties required to uniquely identify an object inside that ADD_OBJECT. for most ruleset elements that means family and table, plus either handle or name โ€” except rules, since rules don't have a name. iykyk. ๐Ÿซถ


๐ŸŒŸ LIST ๐ŸŒŸ

{ "list": LIST_OBJECT }

LIST_OBJECT := TABLE | TABLES | CHAIN | CHAINS | SET | SETS |
                 MAP | MAPS | COUNTER | COUNTERS | QUOTA | QUOTAS |
                 CT_HELPER | CT_HELPERS | LIMIT | LIMITS | RULESET |
                 METER | METERS | FLOWTABLE | FLOWTABLES |
                 CT_TIMEOUT | CT_EXPECTATION

lists ruleset elements. the plural forms are how you list all objects of that kind โ€” optionally filtered by family, and for some of them, also by table. so many plurals, we love a plural ๐Ÿ‘‘


๐ŸŒŸ RESET ๐ŸŒŸ

{ "reset": RESET_OBJECT }

RESET_OBJECT := COUNTER | COUNTERS | QUOTA | QUOTAS | RULE | RULES | SET | MAP | ELEMENT

resets state in suitable objects โ€” aka it zeroes their internal counter. fresh start energy ๐ŸŒˆ


๐ŸŒŸ FLUSH ๐ŸŒŸ

{ "flush": FLUSH_OBJECT }

FLUSH_OBJECT := TABLE | CHAIN | SET | MAP | METER | RULESET

empties the contents of the given object. like, remove all chains from a given table, or remove all elements from a given set. bye bestie ๐Ÿ‘‹


๐ŸŒŸ RENAME ๐ŸŒŸ

{ "rename": CHAIN }

renames a chain. the new name is expected in a dedicated property named newname. ๐ŸŒธ


๐Ÿงš RULESET ELEMENTS ๐Ÿงš


๐ŸŒŸ RULESET ๐ŸŒŸ

{ "ruleset": RULSET_PROPERTIES }

RULESET_PROPERTIES := null | { "family": STRING }

this is a special object for use with the list and flush commands, which will then operate either on the whole ruleset or on just the parts of it belonging to the given family. versatile icon โœจ


๐ŸŒŸ TABLE ๐ŸŒŸ

{ "table": {
        "family": STRING,
        "name": STRING,
        "handle": NUMBER,
        "flags": TABLE_FLAGS
}}

TABLE_FLAGS := TABLE_FLAG | [ TABLE_FLAG_LIST ]
TABLE_FLAG_LIST := TABLE_FLAG [, TABLE_FLAG_LIST ]
TABLE_FLAG := "dormant" | "owner" | "persist"

this object describes a table. here's the breakdown:

  • family โ€” the table's family, e.g. "ip" or "ip6".
  • name โ€” the table's name.
  • handle โ€” the table's handle. in input, it's used only in the delete command, as an alternative to name.
  • flags โ€” the table's flags.

๐ŸŒŸ CHAIN ๐ŸŒŸ

{ "chain": {
        "family": STRING,
        "table": STRING,
        "name": STRING,
        "newname": STRING,
        "handle": NUMBER,
        "type": STRING,
        "hook": STRING,
        "prio": NUMBER,
        "dev": STRING,
        "policy": STRING
}}

this object describes a chain. let's go property by property:

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the chain's name.
  • handle โ€” the chain's handle. in input, it's used only in the delete command, as an alternative to name.
  • newname โ€” a new name for the chain, only relevant in the rename command.

and the following properties are required for base chains:

  • type โ€” the chain's type.
  • hook โ€” the chain's hook.
  • prio โ€” the chain's priority.
  • dev โ€” the chain's bound interface (if in the netdev family).
  • policy โ€” the chain's policy.

๐ŸŒŸ RULE ๐ŸŒŸ

{ "rule": {
        "family": STRING,
        "table": STRING,
        "chain": STRING,
        "expr": [ STATEMENTS ],
        "handle": NUMBER,
        "index": NUMBER,
        "comment": STRING
}}

STATEMENTS := STATEMENT [, STATEMENTS ]

this object describes a rule. the basic building blocks of rules are statements, and every rule consists of at least one. minimum one, no skipping ๐Ÿซถ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • chain โ€” the chain's name.
  • expr โ€” an array of the statements this rule consists of. in input, it's used in the add/insert/replace commands only.
  • handle โ€” the rule's handle. in the delete/replace commands it serves as an identifier of the rule to delete/replace. in the add/insert commands it serves as an identifier of an existing rule to append/prepend the rule to.
  • index โ€” the rule's position for the add/insert commands. it's used as an alternative to handle then.
  • comment โ€” optional rule comment. ๐ŸŽ€

๐ŸŒŸ SET / MAP ๐ŸŒŸ

{ "set": {
        "family": STRING,
        "table": STRING,
        "name": STRING,
        "handle": NUMBER,
        "type": SET_TYPE,
        "policy": SET_POLICY,
        "flags": SET_FLAGS,
        "elem": SET_ELEMENTS,
        "timeout": NUMBER,
        "gc-interval": NUMBER,
        "size": NUMBER,
        "auto-merge": BOOLEAN
}}

{ "map": {
        "family": STRING,
        "table": STRING,
        "name": STRING,
        "handle": NUMBER,
        "type": SET_TYPE,
        "map": STRING,
        "policy": SET_POLICY,
        "flags": SET_FLAGS,
        "elem": SET_ELEMENTS,
        "timeout": NUMBER,
        "gc-interval": NUMBER,
        "size": NUMBER,
        "auto-merge": BOOLEAN
}}

SET_TYPE := STRING | [ SET_TYPE_LIST ] | { "typeof": EXPRESSION }
SET_TYPE_LIST := STRING [, SET_TYPE_LIST ]
SET_POLICY := "performance" | "memory"
SET_FLAGS := SET_FLAG | [ SET_FLAG_LIST ]
SET_FLAG_LIST := SET_FLAG [, SET_FLAG_LIST ]
SET_FLAG := "constant" | "interval" | "timeout"
SET_ELEMENTS := EXPRESSION | [ EXPRESSION_LIST ]
EXPRESSION_LIST := EXPRESSION [, EXPRESSION_LIST ]

and that's where part one leaves us, bestie โ€” the set and map property breakdown is coming right up ๐Ÿ’–

Okay so these objects describe a named set or map. And bestie, a map is literally a special form of set that translates a unique key to a value. It's giving key-value slay. ๐Ÿ’…

  • family โ€” the table's family. Periodt.
  • table โ€” the table's name. No cap.
  • name โ€” the set's name. Iconic.
  • handle โ€” the set's handle. For input, it's used in the delete command only. So don't come for it otherwise.
  • type โ€” the set's datatype, see below. Bestie, scroll down.
  • map โ€” type of values this set maps to (i.e. this set is a map). Map energy.
  • policy โ€” the set's policy. Slay.
  • flags โ€” the set's flags. ๐Ÿšฉ
  • elem โ€” initial set element(s), see below. Down below.
  • timeout โ€” element timeout in seconds. Clock is ticking.
  • gc-interval โ€” garbage collector interval in seconds. GC doing its thing.
  • size โ€” maximum number of elements supported. Cap on the count.
  • auto-merge โ€” automatic merging of adjacent/overlapping set elements in interval sets. Merge magic. โœจ

TYPE ๐ŸŒˆ

So the set type might be a string, such as "ipv4_addr", or an array consisting of strings (for concatenated types), or a typeof object containing an expression to extract the type from. It's giving type flexibility. ๐Ÿ’–

ELEM ๐Ÿ“

A single set element might be given as a string, integer or boolean value for simple cases. If you need additional properties, you can use a formal elem object. And multiple elements? You can give them in an array. Periodt. ๐Ÿ’…

ELEMENT ๐Ÿ‘‘

           { "element": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "elem": SET_ELEM
           }}

           SET_ELEM := EXPRESSION | [ EXPRESSION_LIST ]
           EXPRESSION_LIST := EXPRESSION [, EXPRESSION ]

Manipulate element(s) in a named set. It's giving set manipulation. โœจ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the set's name.
  • elem โ€” see elem property of set object.

FLOWTABLE ๐ŸŒŠ

           { "flowtable": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "hook": STRING,
                   "prio": NUMBER,
                   "dev": FT_INTERFACE
           }}

           FT_INTERFACE := STRING | [ FT_INTERFACE_LIST ]
           FT_INTERFACE_LIST := STRING [, STRING ]

This object represents a named flowtable. It's giving flow. ๐Ÿ’ง

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the flow table's name.
  • handle โ€” the flow table's handle. In input, it's used by the delete command only.
  • hook โ€” the flow table's hook.
  • prio โ€” the flow table's priority.
  • dev โ€” the flow table's interface(s).

COUNTER ๐Ÿ”ข

           { "counter": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "packets": NUMBER,
                   "bytes": NUMBER
           }}

This object represents a named counter. Counting is iconic. โœจ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the counter's name.
  • handle โ€” the counter's handle. In input, it's used by the delete command only.
  • packets โ€” packet counter value.
  • bytes โ€” byte counter value.

QUOTA ๐Ÿ’ฐ

           { "quota": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "bytes": NUMBER,
                   "used": NUMBER,
                   "inv": BOOLEAN
           }}

This object represents a named quota. Budgeting but make it network. ๐Ÿ’…

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the quota's name.
  • handle โ€” the quota's handle. In input, it's used by the delete command only.
  • bytes โ€” quota threshold.
  • used โ€” quota used so far.
  • inv โ€” if true, match if the quota has been exceeded. So inverted match energy.

CT HELPER ๐Ÿงš

           { "ct helper": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": ... ',
                   "type": 'STRING,
                   "protocol": CTH_PROTO,
                   "l3proto": STRING
           }}

           CTH_PROTO := "tcp" | "udp"

This object represents a named conntrack helper. It's giving conntrack assistance. ๐Ÿซถ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the ct helper's name.
  • handle โ€” the ct helper's handle. In input, it's used by the delete command only.
  • type โ€” the ct helper type name, e.g. "ftp" or "tftp".
  • protocol โ€” the ct helper's layer 4 protocol.
  • l3proto โ€” the ct helper's layer 3 protocol, e.g. "ip" or "ip6".

LIMIT ๐Ÿšฆ

           { "limit": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "rate": NUMBER,
                   "per": STRING,
                   "burst": NUMBER,
                   "unit": LIMIT_UNIT,
                   "inv": BOOLEAN
           }}

           LIMIT_UNIT := "packets" | "bytes"

This object represents a named limit. Setting boundaries, bestie. ๐Ÿ›‘

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the limit's name.
  • handle โ€” the limit's handle. In input, it's used by the delete command only.
  • rate โ€” the limit's rate value.
  • per โ€” time unit to apply the limit to, e.g. "week", "day", "hour", etc. If omitted, defaults to "second".
  • burst โ€” the limit's burst value. If omitted, defaults to 0.
  • unit โ€” unit of rate and burst values. If omitted, defaults to "packets".
  • inv โ€” if true, match if limit was exceeded. If omitted, defaults to false.

CT TIMEOUT โณ

           { "ct timeout": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "protocol": CTH_PROTO,
                   "state": STRING,
                   "value: NUMBER,
                   "l3proto": STRING
           }}

           CTH_PROTO := "tcp" | "udp" | "dccp" | "sctp" | "gre" | "icmpv6" | "icmp" | "generic"

This object represents a named conntrack timeout policy. Timeouts but make it ct. ๐Ÿ˜ญ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the ct timeout object's name.
  • handle โ€” the ct timeout object's handle. In input, it's used by delete command only.
  • protocol โ€” the ct timeout object's layer 4 protocol.
  • state โ€” the connection state name, e.g. "established", "syn_sent", "close" or "close_wait", for which the timeout value has to be updated.
  • value โ€” the updated timeout value for the specified connection state.
  • l3proto โ€” the ct timeout object's layer 3 protocol, e.g. "ip" or "ip6".

CT EXPECTATION ๐Ÿ”ฎ

           { "ct expectation": {
                   "family": STRING,
                   "table": STRING,
                   "name": STRING,
                   "handle": NUMBER,
                   "l3proto": STRING
                   "protocol":* CTH_PROTO,
                   "dport": NUMBER,
                   "timeout: NUMBER,
                   "size: NUMBER,
           *}}

           CTH_PROTO := "tcp" | "udp" | "dccp" | "sctp" | "gre" | "icmpv6" | "icmp" | "generic"

This object represents a named conntrack expectation. Manifesting connections. โœจ

  • family โ€” the table's family.
  • table โ€” the table's name.
  • name โ€” the ct expectation object's name.
  • handle โ€” the ct expectation object's handle. In input, it's used by delete command only.
  • l3proto โ€” the ct expectation object's layer 3 protocol, e.g. "ip" or "ip6".
  • protocol โ€” the ct expectation object's layer 4 protocol.
  • dport โ€” the destination port of the expected connection.
  • timeout โ€” the time in millisecond that this expectation will live.
  • size โ€” the maximum count of expectations to be living in the same time.

โœจ STATEMENTS โœจ

okay so bestie, statements are literally the building blocks for rules. every single rule is made of at least one, no cap. ๐Ÿ’…


๐Ÿ’– VERDICT ๐Ÿ’–

{ "accept": null }
{ "drop": null }
{ "continue": null }
{ "return": null }
{ "jump": { "target": * STRING *}}
{ "goto": { "target": * STRING *}}

A verdict either terminates packet traversal through the current chain or delegates to a different one. fr fr.

jump and goto statements expect a target chain name. ๐Ÿ‘‘


๐ŸŽ€ MATCH ๐ŸŽ€

{ "match": {
        "left": EXPRESSION,
        "right": EXPRESSION,
        "op": STRING
}}

This matches the expression on left hand side (typically a packet header or packet meta info) with the expression on right hand side (typically a constant value). If the statement evaluates to true, the next statement in this rule is considered. If not, processing continues with the next rule in the same chain. periodt.

  • left โ€” Left hand side of this match.
  • right โ€” Right hand side of this match.
  • op โ€” Operator indicating the type of comparison.

๐ŸŒˆ OPERATORS ๐ŸŒˆ

  • == โ€” Equal
  • != โ€” Not equal
  • < โ€” Less than
  • > โ€” Greater than
  • โ‡ โ€” Less than or equal to
  • >= โ€” Greater than or equal to
  • in โ€” Perform a lookup, i.e. test if bits on RHS are contained in LHS value

Unlike with the standard API, the operator is mandatory here. In the standard API, a missing operator may be resolved in two ways, depending on the type of expression on the RHS:

  • If the RHS is a bitmask or a list of bitmasks, the expression resolves into a binary operation with the inequality operator, like this: LHS & RHS != 0.
  • In any other case, the equality operator is simply inserted.

For the non-trivial first case, the JSON API supports the in operator. ๐Ÿฆ‹


๐Ÿ’€ COUNTER ๐Ÿ’€

{ "counter": {
        "packets": NUMBER,
        "bytes": NUMBER
}}

{ "counter": STRING }

This object represents a byte/packet counter. In input, no properties are required. If given, they act as initial values for the counter. lowkey simple.

The first form creates an anonymous counter which lives in the rule it appears in. The second form specifies a reference to a named counter object. ๐Ÿ‘‘

  • packets โ€” Packets counted.
  • bytes โ€” Bytes counted.

๐Ÿงš MANGLE ๐Ÿงš

{ "mangle": {
        "key": EXPRESSION,
        "value": EXPRESSION
}}

This changes the packet data or meta info.

  • key โ€” The packet data to be changed, given as an exthdr, payload, meta, ct or ct helper expression.
  • value โ€” Value to change data to.

๐Ÿ“ QUOTA ๐Ÿ“

{ "quota": {
        "val": NUMBER,
        "val_unit": STRING,
        "used": NUMBER,
        "used_unit": STRING,
        "inv": BOOLEAN
}}

{ "quota": STRING }

The first form creates an anonymous quota which lives in the rule it appears in. The second form specifies a reference to a named quota object. slay.

  • val โ€” Quota value.
  • val_unit โ€” Unit of val, e.g. "kbytes" or "mbytes". If omitted, defaults to "bytes".
  • used โ€” Quota used so far. Optional on input. If given, serves as initial value.
  • used_unit โ€” Unit of used. Defaults to "bytes".
  • inv โ€” If true, will match if quota was exceeded. Defaults to false.

๐Ÿ”ฅ LIMIT ๐Ÿ”ฅ

{ "limit": {
        "rate": NUMBER,
        "rate_unit": STRING,
        "per": STRING,
        "burst": NUMBER,
        "burst_unit": STRING,
        "inv": BOOLEAN
}}

{ "limit": STRING }

The first form creates an anonymous limit which lives in the rule it appears in. The second form specifies a reference to a named limit object. ๐Ÿ’…

  • rate โ€” Rate value to limit to.
  • rate_unit โ€” Unit of rate, e.g. "packets" or "mbytes". Defaults to "packets".
  • per โ€” Denominator of rate, e.g. "week" or "minutes".
  • burst โ€” Burst value. Defaults to 0.
  • burst_unit โ€” Unit of burst, ignored if rate_unit is "packets". Defaults to "bytes".
  • inv โ€” If true, matches if the limit was exceeded. Defaults to false.

๐Ÿ‘‘ FWD ๐Ÿ‘‘

{ "fwd": {
        "dev": EXPRESSION,
        "family": FWD_FAMILY,
        "addr": EXPRESSION
}}

FWD_FAMILY := "ip" | "ip6"

Forward a packet to a different destination. main character energy.

  • dev โ€” Interface to forward the packet on.
  • family โ€” Family of addr.
  • addr โ€” IP(v6) address to forward the packet to.

Both family and addr are optional, but if at least one is given, both must be present. โš ๏ธ


๐ŸŒธ NOTRACK ๐ŸŒธ

{ "notrack": null }

Disable connection tracking for the packet. periodt.


๐Ÿฆ‹ DUP ๐Ÿฆ‹

{ "dup": {
        "addr": EXPRESSION,
        "dev": EXPRESSION
}}

Duplicate a packet to a different destination.

  • addr โ€” Address to duplicate packet to.
  • dev โ€” Interface to duplicate packet on. May be omitted to not specify an interface explicitly.

๐Ÿ’œ NETWORK ADDRESS TRANSLATION ๐Ÿ’œ

{ "snat": {
        "addr": EXPRESSION,
        "family": STRING,
        "port": EXPRESSION,
        "flags": FLAGS
}}

{ "dnat": {
        "addr": EXPRESSION,
        "family": STRING,
        "port": EXPRESSION,
        "flags": FLAGS
}}

{ "masquerade": {
        "port": EXPRESSION,
        "flags": FLAGS
}}

{ "redirect": {
        "port": EXPRESSION,
        "flags": FLAGS
}}

FLAGS := FLAG | [ FLAG_LIST ]
FLAG_LIST := FLAG [, FLAG_LIST ]
FLAG := "random" | "fully-random" | "persistent"

Perform Network Address Translation. it's giving glow-up for your packets. โœจ

  • addr โ€” Address to translate to.
  • family โ€” Family of addr, either ip or ip6. Required in inet table family.
  • port โ€” Port to translate to.
  • flags โ€” Flag(s).

All properties are optional and default to none. ๐Ÿ‘‘


๐Ÿ™ˆ REJECT ๐Ÿ™ˆ

{ "reject": {
        "type": STRING,
        "expr": EXPRESSION
}}

Reject the packet and send the given error reply. not me being the villain. ๐Ÿ’€

  • type โ€” Type of reject, either "tcp reset", "icmpx", "icmp" or "icmpv6".
  • expr โ€” ICMP code to reject with.

All properties are optional.


โญ SET โญ

{ "set": {
        "op": STRING,
        "elem": EXPRESSION,
        "set": STRING
}}

Dynamically add/update elements to a set. lowkey iconic.

  • op โ€” Operator on set, either "add" or "update".
  • elem โ€” Set element to add or update.
  • set โ€” Set reference.

๐ŸŽ€ LOG ๐ŸŽ€

{ "log": {
        "prefix": STRING,
        "group": NUMBER,
        "snaplen": NUMBER,
        "queue-threshold": NUMBER,
        "level": LEVEL,
        "flags": FLAGS
}}

LEVEL := "emerg" | "alert" | "crit" | "err" | "warn" | "notice" |
           "info" | "debug" | "audit"

FLAGS := FLAG | [ FLAG_LIST ]
FLAG_LIST := FLAG [, FLAG_LIST ]
FLAG := "tcp sequence" | "tcp options" | "ip options" | "skuid" |
          "ether" | "all"

Log the packet. iykyk. ๐Ÿ“

  • prefix โ€” Prefix for log entries.
  • group โ€” Log group.
  • snaplen โ€” Snaplen for logging.
  • queue-threshold โ€” Queue threshold.
  • level โ€” Log level. Defaults to "warn".
  • flags โ€” Log flags.

All properties are optional.


๐Ÿ’… CT HELPER ๐Ÿ’…

{ "ct helper": EXPRESSION }

Enable the specified conntrack helper for this packet. no cap.

  • ct helper โ€” CT helper reference.

๐ŸŒˆ METER ๐ŸŒˆ

{ "meter": {
        "name": STRING,
        "key": EXPRESSION,
        "stmt": STATEMENT
}}

Apply a given statement using a meter. slay.

  • name โ€” Meter name.
  • key โ€” Meter key.
  • stmt โ€” Meter statement.

๐Ÿ’– QUEUE ๐Ÿ’–

{ "queue": {
        "num": EXPRESSION,
        "flags": FLAGS
}}

FLAGS := FLAG | [ FLAG_LIST ]
FLAG_LIST := FLAG [, FLAG_LIST ]
FLAG := "bypass" | "fanout"

Queue the packet to userspace. main character energy.

  • num โ€” Queue number.
  • flags โ€” Queue flags.

๐Ÿงš VERDICT MAP ๐Ÿงš

{ "vmap": {
        "key": EXPRESSION,
        "data": EXPRESSION
}}

Apply a verdict conditionally. it's giving choose your own adventure.

  • key โ€” Map key.
  • data โ€” Mapping expression consisting of value/verdict pairs.

๐Ÿ“ CT COUNT ๐Ÿ“

{ "ct count": {
        "val": NUMBER,
        "inv": BOOLEAN
}}

Limit the number of connections using conntrack. bestie, don't get too attached.

  • val โ€” Connection count threshold.
  • inv โ€” If true, match if val was exceeded. If omitted, defaults to false.

๐Ÿ”ฅ CT TIMEOUT ๐Ÿ”ฅ

{ "ct timeout": EXPRESSION }

Assign connection tracking timeout policy. bye felicia. ๐Ÿ‘‹

  • ct timeout โ€” CT timeout reference.

๐ŸŒธ CT EXPECTATION ๐ŸŒธ

{ "ct expectation": EXPRESSION }

Assign connection tracking expectation. manifesting.

  • ct expectation โ€” CT expectation reference.

๐Ÿ’€ XT ๐Ÿ’€

{ "xt": {
        "type": TYPENAME,
        "name": STRING
}}

TYPENAME := match | target | watcher

This represents an xt statement from xtables compat interface. It is a fallback if translation is not available or not complete. lowkey legacy.

Seeing this means the ruleset (or parts of it) were created by iptables-nft and one should use that to manage it. no cap.

BEWARE: nftables wonโ€™t restore these statements. ๐Ÿ’€

โœจ EXPRESSIONS โœจ

Okay bestie, expressions are the building blocks of (most) statements ๐Ÿ’…. In their most basic form they're literally just immediate values, represented as a JSON string, integer or boolean type. That's it, that's the vibe. ๐ŸŽ€

๐ŸŒธ IMMEDIATES ๐ŸŒธ

STRING
NUMBER
BOOLEAN

Immediate expressions are typically used for constant values, lowkey the simplest thing on this whole page ๐Ÿ’–. For strings though, there are two special cases and you need to know them:

  • @STRING โ€” the remaining part is taken as a set name to create a set reference.
  • \* โ€” construct a wildcard expression. Yes, that's the whole thing, iconic behavior fr fr.

๐Ÿฆ‹ LISTS ๐Ÿฆ‹

ARRAY

List expressions are constructed by plain arrays containing an arbitrary number of expressions. Slay, nothing complicated here. โœจ

๐ŸŽ€ CONCAT ๐ŸŽ€

{ "concat": CONCAT }

CONCAT := [ EXPRESSION_LIST ]
EXPRESSION_LIST := EXPRESSION [, EXPRESSION_LIST ]

Concatenate several expressions. String them together like a group chat thread ๐Ÿ’ฌ.

๐Ÿ’– SET ๐Ÿ’–

{ "set": SET }

SET := EXPRESSION | [ EXPRESSION_LIST ]

This object constructs an anonymous set. For mappings, an array of arrays with exactly two elements is expected โ€” no more, no less, the math has to math. ๐Ÿงš

๐ŸŒˆ MAP ๐ŸŒˆ

{ "map": {
        "key": EXPRESSION,
        "data": EXPRESSION
}}

Map a key to a value. It's giving... exactly what it says on the tin.

  • key โ€” map key.
  • data โ€” mapping expression consisting of value/target pairs.

โญ PREFIX โญ

{ "prefix": {
        "addr": EXPRESSION,
        "len": NUMBER
}}

Construct an IPv4 or IPv6 prefix consisting of address part in addr and prefix length in len. Chef's kiss, so clean. ๐Ÿ‘‘

๐Ÿ’œ RANGE ๐Ÿ’œ

{ "range": [ EXPRESSION , EXPRESSION ] }

Construct a range of values. The first array item denotes the lower boundary, the second one the upper boundary. First = floor, second = ceiling, periodt. ๐Ÿซถ

๐Ÿ“ PAYLOAD ๐Ÿ“

{ "payload": {
        "base": BASE,
        "offset": NUMBER,
        "len": NUMBER
}}

{ "payload": {
        "protocol": STRING,
        "field": STRING
}}

BASE := "ll" | "nh" | "th"

Construct a payload expression, i.e. a reference to a certain part of packet data ๐Ÿ“ฆ. The first form creates a raw payload expression to point at a random number (len) of bits at a certain offset from a given reference point (base). The following base values are accepted:

  • "ll" โ€” the offset is relative to Link Layer header start offset.
  • "nh" โ€” the offset is relative to Network Layer header start offset.
  • "th" โ€” the offset is relative to Transport Layer header start offset.

The second form allows one to reference a field by name (field) in a named packet header (protocol). Fancy, right? ๐Ÿ’…

๐Ÿงš EXTHDR ๐Ÿงš

{ "exthdr": {
        "name": STRING,
        "field": STRING,
        "offset": NUMBER
}}

Create a reference to a field (field) in an IPv6 extension header (name). offset is used only for rt0 protocol.

If the field property is not given, the expression is to be used as a header existence check in a match statement with a boolean on the right hand side. Yes bestie, a boolean, on the right hand side, that's the rule. ๐ŸŽ€

๐Ÿ’… TCP OPTION ๐Ÿ’…

{ "tcp option": {
        "name": STRING,
        "field": STRING
}}

Create a reference to a field (field) of a TCP option header (name).

If the field property is not given, the expression is to be used as a TCP option existence check in a match statement with a boolean on the right hand side. Same energy as above, different protocol. ๐Ÿฆ‹

๐ŸŒธ SCTP CHUNK ๐ŸŒธ

{ "sctp chunk": {
        "name": STRING,
        "field": STRING
}}

Create a reference to a field (field) of an SCTP chunk (name).

If the field property is not given, the expression is to be used as an SCTP chunk existence check in a match statement with a boolean on the right hand side. You already know the pattern, iykyk. ๐Ÿ’–

๐Ÿ”ฅ DCCP OPTION ๐Ÿ”ฅ

{ "dccp option": {
        "type": NUMBER*
}}

Create a reference to a DCCP option (type).

The expression is to be used as a DCCP option existence check in a match statement with a boolean on the right hand side. No field property here, just vibes and a boolean. โœจ

๐Ÿ’œ META ๐Ÿ’œ

{ "meta": {
        "key": META_KEY
}}

META_KEY := "length" | "protocol" | "priority" | "random" | "mark" |
              "iif" | "iifname" | "iiftype" | "oif" | "oifname" |
              "oiftype" | "skuid" | "skgid" | "nftrace" |
              "rtclassid" | "ibriport" | "obriport" | "ibridgename" |
              "obridgename" | "pkttype" | "cpu" | "iifgroup" |
              "oifgroup" | "cgroup" | "nfproto" | "l4proto" |
              "secpath"

Create a reference to packet meta data. All the tea about the packet, basically โ˜•.

๐ŸŒˆ RT ๐ŸŒˆ

{ "rt": {
        "key": RT_KEY,
        "family": RT_FAMILY
}}

RT_KEY := "classid" | "nexthop" | "mtu"
RT_FAMILY := "ip" | "ip6"

Create a reference to packet routing data.

The family property is optional and defaults to unspecified. Optional queen, she doesn't have to show up if she doesn't want to. ๐Ÿ’…

๐ŸŽ€ CT ๐ŸŽ€

{ "ct": {
        "key": STRING,
        "family": CT_FAMILY,
        "dir": CT_DIRECTION
}}

CT_FAMILY := "ip" | "ip6"
CT_DIRECTION := "original" | "reply"

Create a reference to packet conntrack data.

Some CT keys do not support a direction. In this case, dir must not be given. Read that again bestie โ€” must not, not "can skip", not "whatever". ๐Ÿšซ

โญ NUMGEN โญ

{ "numgen": {
        "mode": NG_MODE,
        "mod": NUMBER,
        "offset": NUMBER
}}

NG_MODE := "inc" | "random"

Create a number generator.

The offset property is optional and defaults to 0. ๐ŸŽฒ

๐Ÿ“ HASH ๐Ÿ“

{ "jhash": {
        "mod": NUMBER,
        "offset": NUMBER,
        "expr": EXPRESSION,
        "seed": NUMBER
}}

{ "symhash": {
        "mod": NUMBER,
        "offset": NUMBER
}}

Hash packet data.

The offset and seed properties are optional and default to 0. โœจ

๐Ÿงš FIB ๐Ÿงš

{ "fib": {
        "result": FIB_RESULT,
        "flags": FIB_FLAGS
}}

FIB_RESULT := "oif" | "oifname" | "type"

FIB_FLAGS := FIB_FLAG | [ FIB_FLAG_LIST ]
FIB_FLAG_LIST := FIB_FLAG [, FIB_FLAG_LIST ]
FIB_FLAG := "saddr" | "daddr" | "mark" | "iif" | "oif"

Perform kernel Forwarding Information Base lookups. Going straight to the kernel for answers, main character energy fr. ๐Ÿ‘‘

๐Ÿ’– BINARY OPERATION ๐Ÿ’–

{ "|": [ EXPRESSION, EXPRESSIONS ] }
{ "^": [ EXPRESSION, EXPRESSIONS ] }
{ "&": [ EXPRESSION, EXPRESSIONS ] }
{ "<<": [ EXPRESSION, EXPRESSIONS ] }
{ ">>": [ EXPRESSION, EXPRESSIONS ] }
EXPRESSIONS := EXPRESSION | EXPRESSION, EXPRESSIONS

All binary operations expect an array of at least two expressions, of which the first element denotes the left hand side and the second one the right hand side. Extra elements are accepted in the given array and appended to the term accordingly. So like, two is the minimum but the array can absolutely keep going, she's flexible. ๐Ÿ’…

๐Ÿฆ‹ VERDICT ๐Ÿฆ‹

{ "accept": null }
{ "drop": null }
{ "continue": null }
{ "return": null }
{ "jump": { "target": STRING }}
{ "goto": { "target": STRING }}

Same as the verdict statement, but for use in verdict maps.

jump and goto verdicts expect a target chain name. Don't forget the target, bestie. ๐ŸŽฏ

๐ŸŒธ ELEM ๐ŸŒธ

{ "elem": {
        "val": EXPRESSION,
        "timeout": NUMBER,
        "expires": NUMBER,
        "comment": STRING
}}

Explicitly set element object, in case timeout, expires or comment are desired. Otherwise, it may be replaced by the value of val. Shortcut unlocked ๐Ÿ”“.

๐Ÿ’œ SOCKET ๐Ÿ’œ

{ "socket": {
        "key": SOCKET_KEY
}}

SOCKET_KEY := "transparent"

Construct a reference to packet's socket. ๐Ÿงฆ

๐ŸŒˆ OSF ๐ŸŒˆ

{ "osf": {
        "key": OSF_KEY,
        "ttl": OSF_TTL
}}

OSF_KEY := "name"
OSF_TTL := "loose" | "skip"

Perform OS fingerprinting. This expression is typically used in the LHS of a match statement.

  • key โ€” which part of the fingerprint info to match against. At this point, only the OS name is supported.
  • ttl โ€” define how the packet's TTL value is to be matched. This property is optional. If omitted, the TTL value has to match exactly. A value of loose accepts TTL values less than the fingerprint one. A value of skip omits TTL value comparison entirely.

So: omit it and it's exact, loose is the chill one, skip is the one who didn't show up to the exam ๐Ÿ˜ญ.


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Phil Sutter <phil@nwl.cc> โ€” author. Legend behavior, we stan. ๐Ÿ’–


And that's a wrap on the whole page, bestie!! ๐ŸŽ‰ You just read LIBNFTABLES-JSON(5) from the libnftables package, dated 12/05/2025 โ€” go forth and write the most valid JSON ruleset of your life, no cap. โœจ๐Ÿฆ‹๐Ÿ’…

๐Ÿ“ฆ where this page comes from