Skip to content

Commit 9c9f24c

Browse files
lnetoclaude
andcommitted
Lua binding for libnftables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0 parents  commit 9c9f24c

File tree

10 files changed

+833
-0
lines changed

10 files changed

+833
-0
lines changed

.github/workflows/ldoc.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: LDoc
2+
3+
on:
4+
push:
5+
branches: [master]
6+
7+
permissions:
8+
contents: read
9+
pages: write
10+
id-token: write
11+
12+
concurrency:
13+
group: pages
14+
cancel-in-progress: true
15+
16+
jobs:
17+
ldoc:
18+
runs-on: ubuntu-latest
19+
environment:
20+
name: github-pages
21+
url: ${{ steps.deployment.outputs.page_url }}
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Install dependencies
26+
run: |
27+
sudo apt-get update
28+
sudo apt-get install -y lua5.4 luarocks
29+
sudo luarocks install ldoc
30+
31+
- name: Generate documentation
32+
run: ldoc .
33+
34+
- uses: actions/configure-pages@v5
35+
- uses: actions/upload-pages-artifact@v3
36+
with:
37+
path: doc
38+
- uses: actions/deploy-pages@v4
39+
id: deployment
40+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.so
2+
*.swp

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Ring Zero Desenvolvimento de Software LTDA
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
LUA_VERSION ?= $(shell for v in 5.5 5.4; do pkg-config --exists lua$$v 2>/dev/null && echo $$v && break; done)
2+
3+
LUA_INC ?= $(shell pkg-config --cflags lua$(LUA_VERSION) 2>/dev/null || echo -I/usr/include/lua$(LUA_VERSION))
4+
NFT_INC ?= $(shell pkg-config --cflags libnftables 2>/dev/null)
5+
NFT_LIB ?= $(shell pkg-config --libs libnftables 2>/dev/null || echo -lnftables)
6+
CFLAGS ?= -O2 -Wall -Wextra -fPIC
7+
LDFLAGS ?= -shared
8+
9+
LUA_CMOD ?= /usr/local/lib/lua/$(LUA_VERSION)
10+
11+
nftables.so: luanftables.c
12+
$(CC) $(CFLAGS) $(LUA_INC) $(NFT_INC) -o $@ $< $(LDFLAGS) $(NFT_LIB)
13+
14+
install: nftables.so
15+
install -D -m 0755 $< $(DESTDIR)$(LUA_CMOD)/$<
16+
17+
clean:
18+
rm -f nftables.so
19+
20+
.PHONY: install clean
21+

README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+

config.ld

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
project = "luanftables"
2+
title = "luanftables — Lua binding for libnftables"
3+
description = "Programmatic access to nftables via the libnftables C library."
4+
file = {"luanftables.c"}
5+
dir = "doc"
6+
format = "markdown"
7+
backtick_references = true
8+
sort = true
9+

example.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
local nft = require("nftables")
2+
local ctx <close> = nft.new()
3+
4+
assert(ctx:cmd([[
5+
add table bridge teste
6+
add chain bridge teste input { type filter hook input priority 0; }
7+
add rule bridge teste input tcp dport 443 counter
8+
]]))
9+
10+
print(ctx:cmd("list table bridge teste"))
11+

example_json.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
local nft = require("nftables")
2+
local ctx <close> = nft.new()
3+
4+
-- create table and chain
5+
assert(ctx:cmd([[
6+
add table bridge teste_json
7+
add chain bridge teste_json filter { type filter hook forward priority 0; }
8+
add rule bridge teste_json filter tcp dport 443 counter
9+
]]))
10+
11+
-- list as JSON
12+
ctx:output("json", true)
13+
local json = assert(ctx:cmd("list table bridge teste_json"))
14+
print("=== JSON output ===")
15+
print(json)
16+
17+
-- delete and recreate from JSON input
18+
ctx:output("json", false)
19+
ctx:cmd("delete table bridge teste_json")
20+
21+
ctx:input("json", true)
22+
assert(ctx:cmd(json))
23+
ctx:input("json", false)
24+
25+
-- verify it's back
26+
local out = assert(ctx:cmd("list table bridge teste_json"))
27+
print("\n=== after JSON roundtrip ===")
28+
print(out)
29+
30+
-- cleanup
31+
ctx:cmd("delete table bridge teste_json")
32+

0 commit comments

Comments
 (0)