Browse code

d/libn/i/nftables: decouple nft handle from table

Afford applying nft commands via libnftables without needing to go
through our table abstraction. Make the table abstraction responsible
for lazily allocating an nft context.

Signed-off-by: Cory Snider <csnider@mirantis.com>

Cory Snider authored on 2026/06/11 01:32:37
Showing 3 changed files
... ...
@@ -20,28 +20,19 @@ import (
20 20
 // #include <nftables/libnftables.h>
21 21
 import "C"
22 22
 
23
-type nftHandle = *C.struct_nft_ctx
23
+type nftCtx C.struct_nft_ctx
24 24
 
25
-// nftApply calls libnftables to execute the nftables commands in nftCmd.
26
-// Acquire t.applyLock before calling this function.
27
-func (t *table) nftApply(ctx context.Context, nftCmd []byte) error {
25
+// Apply calls libnftables to execute the nftables commands in nftCmd.
26
+func (h *nftCtx) Apply(ctx context.Context, nftCmd []byte) error {
28 27
 	ctx, span := otel.Tracer("").Start(ctx, spanPrefix+".nftApply.cgo")
29 28
 	defer span.End()
30 29
 
31
-	if t.nftHandle == nil {
32
-		handle, err := newNftHandle()
33
-		if err != nil {
34
-			return err
35
-		}
36
-		t.nftHandle = handle
37
-	}
38
-
39 30
 	cCmd := C.CString(string(nftCmd))
40 31
 	defer C.free(unsafe.Pointer(cCmd))
41 32
 
42
-	ret := C.nft_run_cmd_from_buffer(t.nftHandle, cCmd)
43
-	stdout := C.GoString(C.nft_ctx_get_output_buffer(t.nftHandle))
44
-	stderr := C.GoString(C.nft_ctx_get_error_buffer(t.nftHandle))
33
+	ret := C.nft_run_cmd_from_buffer((*C.struct_nft_ctx)(h), cCmd)
34
+	stdout := C.GoString(C.nft_ctx_get_output_buffer((*C.struct_nft_ctx)(h)))
35
+	stderr := C.GoString(C.nft_ctx_get_error_buffer((*C.struct_nft_ctx)(h)))
45 36
 	if ret != 0 {
46 37
 		return fmt.Errorf("libnftables: failed to apply commands (code %d), stderr: %s", int(ret), stderr)
47 38
 	}
... ...
@@ -49,7 +40,7 @@ func (t *table) nftApply(ctx context.Context, nftCmd []byte) error {
49 49
 	return nil
50 50
 }
51 51
 
52
-func newNftHandle() (_ *C.struct_nft_ctx, retErr error) {
52
+func newNftCtx() (_ *nftCtx, retErr error) {
53 53
 	handle := C.nft_ctx_new(C.NFT_CTX_DEFAULT)
54 54
 	if handle == nil {
55 55
 		return nil, errors.New("libnftables: failed to create new nft handle")
... ...
@@ -65,14 +56,9 @@ func newNftHandle() (_ *C.struct_nft_ctx, retErr error) {
65 65
 	if ret := C.nft_ctx_buffer_error(handle); ret != 0 {
66 66
 		return nil, fmt.Errorf("libnftables: failed to set error buffer (code %d)", int(ret))
67 67
 	}
68
-	return handle, nil
68
+	return (*nftCtx)(handle), nil
69 69
 }
70 70
 
71
-func (t *table) closeNftHandle() {
72
-	t.applyLock.Lock()
73
-	defer t.applyLock.Unlock()
74
-	if t.nftHandle != nil {
75
-		C.nft_ctx_free(t.nftHandle)
76
-		t.nftHandle = nil
77
-	}
71
+func (h *nftCtx) Close() {
72
+	C.nft_ctx_free((*C.struct_nft_ctx)(h))
78 73
 }
... ...
@@ -15,13 +15,17 @@ import (
15 15
 	"go.opentelemetry.io/otel"
16 16
 )
17 17
 
18
-type nftHandle = struct{}
18
+type nftCtx struct{}
19 19
 
20 20
 var lookPathNSEnter = sync.OnceValues(func() (string, error) {
21 21
 	return exec.LookPath("nsenter")
22 22
 })
23 23
 
24
-func (t *table) nftApply(ctx context.Context, nftCmd []byte) error {
24
+func newNftCtx() (*nftCtx, error) {
25
+	return *nftCtx{}, nil
26
+}
27
+
28
+func (*nftCtx) Apply(ctx context.Context, nftCmd []byte) error {
25 29
 	ctx, span := otel.Tracer("").Start(ctx, spanPrefix+".nftApply.exec")
26 30
 	defer span.End()
27 31
 
... ...
@@ -82,5 +86,5 @@ func (t *table) nftApply(ctx context.Context, nftCmd []byte) error {
82 82
 	return nil
83 83
 }
84 84
 
85
-func (t *table) closeNftHandle() {
85
+func (*nftCtx) Close() {
86 86
 }
... ...
@@ -258,7 +258,20 @@ type table struct {
258 258
 	MustFlush      bool
259 259
 
260 260
 	applyLock sync.Mutex
261
-	nftHandle nftHandle // applyLock must be held to access
261
+	nftHandle *nftCtx // applyLock must be held to access
262
+}
263
+
264
+// nftApply executes the nftables commands in nftCmd.
265
+// Acquire t.applyLock before calling this function.
266
+func (t *table) nftApply(ctx context.Context, nftCmd []byte) error {
267
+	if t.nftHandle == nil {
268
+		h, err := newNftCtx()
269
+		if err != nil {
270
+			return err
271
+		}
272
+		t.nftHandle = h
273
+	}
274
+	return t.nftHandle.Apply(ctx, nftCmd)
262 275
 }
263 276
 
264 277
 // Table is a handle for an nftables table.
... ...
@@ -305,7 +318,12 @@ func NewTable(family Family, name string) (Table, error) {
305 305
 // the underlying nftables table.
306 306
 func (t Table) Close() error {
307 307
 	if t.IsValid() {
308
-		t.t.closeNftHandle()
308
+		t.t.applyLock.Lock()
309
+		defer t.t.applyLock.Unlock()
310
+		if t.t.nftHandle != nil {
311
+			t.t.nftHandle.Close()
312
+			t.t.nftHandle = nil
313
+		}
309 314
 		t.t = nil
310 315
 	}
311 316
 	return nil