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 thedeletecommand, as an alternative toname.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 thedeletecommand, as an alternative toname.newnameโ a new name for the chain, only relevant in therenamecommand.
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 thenetdevfamily).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 theadd/insert/replacecommands only.handleโ the rule's handle. in thedelete/replacecommands it serves as an identifier of the rule to delete/replace. in theadd/insertcommands it serves as an identifier of an existing rule to append/prepend the rule to.indexโ the rule's position for theadd/insertcommands. it's used as an alternative tohandlethen.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 thedeletecommand 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โ seeelemproperty 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 thedeletecommand 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 thedeletecommand 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 thedeletecommand 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 thedeletecommand 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 thedeletecommand 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 to0.unitโ unit of rate and burst values. If omitted, defaults to"packets".invโ if true, match if limit was exceeded. If omitted, defaults tofalse.
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 bydeletecommand 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 bydeletecommand 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 toinโ 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 ofval, 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 ofused. 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 ofrate, e.g."packets"or"mbytes". Defaults to"packets".perโ Denominator ofrate, e.g."week"or"minutes".burstโ Burst value. Defaults to 0.burst_unitโ Unit ofburst, ignored ifrate_unitis"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 ofaddr.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 ofaddr, eitheriporip6. Required ininettable 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 ifvalwas 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 oflooseaccepts TTL values less than the fingerprint one. A value ofskipomits 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
- alpine 3.24: nftables-doc 1.1.6-r1 ยท GPL-2.0-or-later
- fedora 44: nftables 1:1.1.6-2.fc44 ยท GPL-2.0-only
- fedora 44: nftables 1:1.1.6-2.fc44 ยท GPL-2.0-only
- nixos 26.05: nftables 1.1.6
- ubuntu resolute: libnftables1 1.1.6-1 ยท GPL-2.0-only
- ubuntu stonking: libnftables1 1.1.6-1 ยท GPL-2.0-only
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms