|
| 1 | +# luanftables |
| 2 | + |
| 3 | +Lua binding for [libnftables](https://wiki.nftables.org/wiki-nftables/index.php/Building_and_installing_nftables_from_sources). |
| 4 | + |
| 5 | +[API Documentation](https://ring0networks.github.io/luanftables/) |
| 6 | + |
| 7 | +## Build |
| 8 | + |
| 9 | +Requires `libnftables-dev` and Lua (>= 5.4) headers. |
| 10 | + |
| 11 | +``` |
| 12 | +sudo apt install libnftables-dev lua5.4 liblua5.4-dev |
| 13 | +make |
| 14 | +sudo make install |
| 15 | +``` |
| 16 | + |
| 17 | +The build auto-detects the Lua version via `pkg-config` (prefers 5.5, falls back to 5.4). |
| 18 | +To override: `make LUA_VERSION=5.4`. |
| 19 | + |
| 20 | +## Usage |
| 21 | + |
| 22 | +```lua |
| 23 | +local nft = require("nftables") |
| 24 | +local ctx <close> = nft.new() |
| 25 | + |
| 26 | +-- run nftables commands |
| 27 | +local output, err = ctx:cmd("add table bridge dome") |
| 28 | +local output, err = ctx:cmd("list ruleset") |
| 29 | + |
| 30 | +-- run commands from file |
| 31 | +local output, err = ctx:run("rules.nft") |
| 32 | + |
| 33 | +-- multiple commands (atomic) |
| 34 | +ctx:cmd("add table bridge dome\nadd chain bridge dome filter") |
| 35 | + |
| 36 | +-- output flags |
| 37 | +ctx:output("json", true) -- JSON output (and input) |
| 38 | +ctx:output("handle", true) -- include handles in output |
| 39 | +ctx:output("stateless", true) -- omit counters/quotas |
| 40 | +ctx:output("terse", true) -- omit set element contents |
| 41 | +ctx:output("echo", true) -- print changes after commit |
| 42 | + |
| 43 | +print(ctx:output("json")) -- true |
| 44 | + |
| 45 | +-- input flags |
| 46 | +ctx:input("nodns", true) -- no DNS lookups during parsing |
| 47 | +ctx:input("json", true) -- accept JSON input |
| 48 | + |
| 49 | +-- boolean flags |
| 50 | +ctx:dryrun(true) -- validate without applying |
| 51 | +ctx:optimize(true) -- optimize ruleset (nft -o) |
| 52 | + |
| 53 | +print(ctx:dryrun()) -- true |
| 54 | + |
| 55 | +-- variables (accessible as $key in nft scripts) |
| 56 | +ctx:var("IFACE=br-lan") |
| 57 | +ctx:clear_vars() |
| 58 | + |
| 59 | +-- include paths (for nft include directives) |
| 60 | +ctx:include("/etc/nft/") |
| 61 | +ctx:clear_includes() |
| 62 | + |
| 63 | +-- close (also via <close> or __gc) |
| 64 | +ctx:close() |
| 65 | +``` |
| 66 | + |
| 67 | +## JSON |
| 68 | + |
| 69 | +```lua |
| 70 | +local nft = require("nftables") |
| 71 | +local ctx <close> = nft.new() |
| 72 | + |
| 73 | +-- create a table and chain |
| 74 | +ctx:cmd("add table bridge dome") |
| 75 | +ctx:cmd("add chain bridge dome filter { type filter hook forward priority 0; }") |
| 76 | + |
| 77 | +-- list ruleset as JSON |
| 78 | +ctx:output("json", true) |
| 79 | +local json = ctx:cmd("list table bridge dome") |
| 80 | +print(json) |
| 81 | + |
| 82 | +-- submit JSON input |
| 83 | +ctx:input("json", true) |
| 84 | +ctx:cmd([[{"nftables": [ |
| 85 | + {"add": {"rule": { |
| 86 | + "family": "bridge", "table": "dome", "chain": "filter", |
| 87 | + "expr": [ |
| 88 | + {"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, |
| 89 | + "op": "==", "right": 443}}, |
| 90 | + {"counter": null} |
| 91 | + ] |
| 92 | + }}} |
| 93 | +]}]]) |
| 94 | +``` |
| 95 | + |
| 96 | +## Error handling |
| 97 | + |
| 98 | +`ctx:cmd()` and `ctx:run()` return the output string on success, or `nil` + |
| 99 | +error message on failure. |
| 100 | + |
| 101 | +```lua |
| 102 | +local output, err = ctx:cmd("delete table bridge nonexistent") |
| 103 | +if not output then |
| 104 | + print("error: " .. err) |
| 105 | +end |
| 106 | +``` |
| 107 | + |
| 108 | +## Flags |
| 109 | + |
| 110 | +Each flag group has its own method. Call with a value to set, without to get. |
| 111 | +The module also exports flag constants for reference. |
| 112 | + |
| 113 | +| Method | Flags | |
| 114 | +|--------|-------| |
| 115 | +| `ctx:output(name [, bool])` | `reversedns`, `service`, `stateless`, `handle`, `json`, `echo`, `guid`, `numeric_proto`, `numeric_prio`, `numeric_symbol`, `numeric_time`, `terse` | |
| 116 | +| `ctx:input(name [, bool])` | `nodns`, `json` | |
| 117 | +| `ctx:debug(name [, bool])` | `scanner`, `parser`, `evaluation`, `netlink`, `mnl`, `proto_ctx`, `segtree` | |
| 118 | +| `ctx:dryrun([bool])` | validate without applying | |
| 119 | +| `ctx:optimize([bool])` | optimize ruleset | |
| 120 | + |
| 121 | +Constants: `nft.output`, `nft.input`, `nft.debug`. |
| 122 | + |
| 123 | +## API |
| 124 | + |
| 125 | +| Method | Description | |
| 126 | +|--------|-------------| |
| 127 | +| `nft.new()` | Create a new nftables context | |
| 128 | +| `ctx:cmd(str)` | Execute nftables command(s) from string | |
| 129 | +| `ctx:run(file)` | Execute nftables commands from file | |
| 130 | +| `ctx:output(name [, bool])` | Get/set output flag | |
| 131 | +| `ctx:input(name [, bool])` | Get/set input flag | |
| 132 | +| `ctx:debug(name [, bool])` | Get/set debug flag | |
| 133 | +| `ctx:dryrun([bool])` | Get/set dry-run mode | |
| 134 | +| `ctx:optimize([bool])` | Get/set optimization | |
| 135 | +| `ctx:var(kv)` | Define a variable (`"key=value"`) | |
| 136 | +| `ctx:clear_vars()` | Remove all variables | |
| 137 | +| `ctx:include(path)` | Add an include search path | |
| 138 | +| `ctx:clear_includes()` | Remove all include paths | |
| 139 | +| `ctx:close()` | Free the context (idempotent) | |
| 140 | + |
| 141 | +## Requirements |
| 142 | + |
| 143 | +- Lua >= 5.4 |
| 144 | +- libnftables (runtime) |
| 145 | +- libnftables-dev (build) |
| 146 | +- Root or `CAP_NET_ADMIN` to execute nftables commands |
| 147 | + |
| 148 | +## License |
| 149 | + |
| 150 | +MIT |
| 151 | + |
0 commit comments