nftables-napi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +104 -0
- package/binding.gyp +45 -0
- package/lib/index.d.ts +120 -0
- package/lib/index.js +24 -0
- package/package.json +45 -0
- package/prebuilds/linux-arm64/nftables-napi.node +0 -0
- package/prebuilds/linux-x64/nftables-napi.node +0 -0
- package/src/addon.cpp +9 -0
- package/src/netlink/constants.h +39 -0
- package/src/netlink/nft_config.h +43 -0
- package/src/netlink/nftnl_raii.h +69 -0
- package/src/netlink/nl_batch.cpp +67 -0
- package/src/netlink/nl_batch.h +36 -0
- package/src/netlink/nl_result.h +8 -0
- package/src/netlink/nl_socket.cpp +117 -0
- package/src/netlink/nl_socket.h +28 -0
- package/src/netlink/operation.h +11 -0
- package/src/netlink/parsed_addr.h +14 -0
- package/src/netlink/set_ops.cpp +113 -0
- package/src/netlink/set_ops.h +32 -0
- package/src/netlink/table_ops.cpp +221 -0
- package/src/netlink/table_ops.h +23 -0
- package/src/nft_manager.cpp +366 -0
- package/src/nft_manager.h +44 -0
- package/src/validation.cpp +26 -0
- package/src/validation.h +21 -0
- package/src/workers/nft_worker.cpp +30 -0
- package/src/workers/nft_worker.h +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# nftables-napi
|
|
2
|
+
|
|
3
|
+
Native Node.js binding for nftables via libnftnl + libmnl. Manages IPv4/IPv6 blacklist tables with timeout support through direct netlink communication — no shell commands, no `nft` CLI.
|
|
4
|
+
|
|
5
|
+
Requires Linux with `CAP_NET_ADMIN` or root.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install nftables-napi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Prebuilt binaries are included for `linux-x64` and `linux-arm64`. If a prebuild is not available for your platform, the package will compile from source (requires `libnftnl-dev`, `libmnl-dev`, `pkg-config`, and a C++20 compiler).
|
|
14
|
+
|
|
15
|
+
### Runtime dependencies
|
|
16
|
+
|
|
17
|
+
The module dynamically links against `libnftnl` and `libmnl`. These must be present in the runtime environment. The `nft` CLI is **not** required — the module talks to the kernel directly via netlink.
|
|
18
|
+
|
|
19
|
+
**Alpine:**
|
|
20
|
+
|
|
21
|
+
```dockerfile
|
|
22
|
+
RUN apk add --no-cache libnftnl libmnl
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Debian/Ubuntu:**
|
|
26
|
+
|
|
27
|
+
```dockerfile
|
|
28
|
+
RUN apt-get update && apt-get install -y libnftnl13 libmnl0 && rm -rf /var/lib/apt/lists/*
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const { NftManager } = require("nftables-napi");
|
|
35
|
+
|
|
36
|
+
const nft = new NftManager({
|
|
37
|
+
tableName: "tablename",
|
|
38
|
+
blacklistSetName: "blacklist",
|
|
39
|
+
droplistSetName: "droplist",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await nft.createTable();
|
|
43
|
+
|
|
44
|
+
// Add with timeout (seconds)
|
|
45
|
+
await nft.addAddress({ ip: "1.2.3.4", set: "blacklist", timeout: 1800 });
|
|
46
|
+
await nft.addAddress({ ip: "2001:db8::1", set: "blacklist", timeout: 3600 });
|
|
47
|
+
|
|
48
|
+
// Add permanent (no timeout)
|
|
49
|
+
await nft.addAddress({ ip: "5.6.7.8", set: "droplist" });
|
|
50
|
+
|
|
51
|
+
// Bulk add
|
|
52
|
+
await nft.addAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist", timeout: 7200 });
|
|
53
|
+
|
|
54
|
+
// Remove
|
|
55
|
+
await nft.removeAddress({ ip: "1.2.3.4", set: "blacklist" });
|
|
56
|
+
await nft.removeAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist" });
|
|
57
|
+
|
|
58
|
+
await nft.deleteTable();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### `new NftManager(options)`
|
|
64
|
+
|
|
65
|
+
| Option | Type | Required | Description |
|
|
66
|
+
| ------------------ | -------- | -------- | -------------------------------------------- |
|
|
67
|
+
| `tableName` | `string` | Yes | Base table name (IPv6 auto-appends `'6'`) |
|
|
68
|
+
| `blacklistSetName` | `string` | Yes | Blacklist set name (IPv6 auto-appends `'6'`) |
|
|
69
|
+
| `droplistSetName` | `string` | Yes | Droplist set name (IPv6 auto-appends `'6'`) |
|
|
70
|
+
|
|
71
|
+
Log prefixes are auto-generated: `'{setName}: '` for each set.
|
|
72
|
+
|
|
73
|
+
### Methods
|
|
74
|
+
|
|
75
|
+
All methods return `Promise<void>`.
|
|
76
|
+
|
|
77
|
+
| Method | Description |
|
|
78
|
+
| -------------------------------------- | --------------------------------------------------------------------- |
|
|
79
|
+
| `createTable()` | Create IPv4/IPv6 tables with blacklist and droplist sets. Idempotent. |
|
|
80
|
+
| `deleteTable()` | Delete both tables. Idempotent. |
|
|
81
|
+
| `addAddress({ ip, set, timeout? })` | Add IP to set. `timeout` in seconds, omit for permanent. |
|
|
82
|
+
| `removeAddress({ ip, set })` | Remove IP from set. Idempotent. |
|
|
83
|
+
| `addAddresses({ ips, set, timeout? })` | Bulk add to set. Chunked for efficient netlink communication. |
|
|
84
|
+
| `removeAddresses({ ips, set })` | Bulk remove from set. Idempotent. |
|
|
85
|
+
|
|
86
|
+
## Building from source
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Dependencies (Debian/Ubuntu)
|
|
90
|
+
sudo apt install pkg-config libnftnl-dev libmnl-dev build-essential
|
|
91
|
+
|
|
92
|
+
# Build
|
|
93
|
+
npm run build
|
|
94
|
+
|
|
95
|
+
# Prebuild for current platform
|
|
96
|
+
npx prebuildify --napi --strip
|
|
97
|
+
|
|
98
|
+
# Prebuild for linux/amd64 + linux/arm64 via Docker
|
|
99
|
+
npm run prebuild:all
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
AGPL-3.0-only
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "remnawave_nft",
|
|
5
|
+
"sources": [
|
|
6
|
+
"src/addon.cpp",
|
|
7
|
+
"src/nft_manager.cpp",
|
|
8
|
+
"src/validation.cpp",
|
|
9
|
+
"src/netlink/nl_batch.cpp",
|
|
10
|
+
"src/netlink/nl_socket.cpp",
|
|
11
|
+
"src/netlink/set_ops.cpp",
|
|
12
|
+
"src/netlink/table_ops.cpp",
|
|
13
|
+
"src/workers/nft_worker.cpp"
|
|
14
|
+
],
|
|
15
|
+
"include_dirs": [
|
|
16
|
+
"<!@(node -p \"require('node-addon-api').include\")",
|
|
17
|
+
"<!@(pkg-config --cflags-only-I libnftnl libmnl | sed 's/-I//g')"
|
|
18
|
+
],
|
|
19
|
+
"libraries": [
|
|
20
|
+
"<!@(pkg-config --libs libnftnl libmnl)"
|
|
21
|
+
],
|
|
22
|
+
"defines": [
|
|
23
|
+
"NAPI_VERSION=9",
|
|
24
|
+
"NAPI_DISABLE_CPP_EXCEPTIONS"
|
|
25
|
+
],
|
|
26
|
+
"cflags_cc": [
|
|
27
|
+
"-std=c++20",
|
|
28
|
+
"-Wall",
|
|
29
|
+
"-Wextra",
|
|
30
|
+
"-Wpedantic",
|
|
31
|
+
"-Wno-unused-parameter"
|
|
32
|
+
],
|
|
33
|
+
"ldflags": [
|
|
34
|
+
"-Wl,-z,relro",
|
|
35
|
+
"-Wl,-z,now",
|
|
36
|
+
"-Wl,-z,noexecstack"
|
|
37
|
+
],
|
|
38
|
+
"conditions": [
|
|
39
|
+
["OS!='linux'", {
|
|
40
|
+
"defines": ["UNSUPPORTED_PLATFORM"]
|
|
41
|
+
}]
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native nftables manager for Linux firewall.
|
|
3
|
+
* Manages IPv4/IPv6 blacklist/droplist tables via libnftnl + libmnl (direct netlink, no nft CLI).
|
|
4
|
+
* Requires CAP_NET_ADMIN or root privileges.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Target set for address operations. */
|
|
8
|
+
export type TargetSet = 'blacklist' | 'droplist';
|
|
9
|
+
|
|
10
|
+
/** Constructor options. All fields are required — no defaults. */
|
|
11
|
+
export interface NftManagerOptions {
|
|
12
|
+
/** Base table name. IPv6 table auto-appends '6'. */
|
|
13
|
+
tableName: string;
|
|
14
|
+
/** Blacklist set name. IPv6 set auto-appends '6'. */
|
|
15
|
+
blacklistSetName: string;
|
|
16
|
+
/** Droplist set name. IPv6 set auto-appends '6'. */
|
|
17
|
+
droplistSetName: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Options for adding a single address. */
|
|
21
|
+
export interface AddAddressOptions {
|
|
22
|
+
/** IPv4 or IPv6 address (e.g., "1.2.3.4" or "2001:db8::1"). */
|
|
23
|
+
ip: string;
|
|
24
|
+
/** Target set: 'blacklist' or 'droplist'. */
|
|
25
|
+
set: TargetSet;
|
|
26
|
+
/** Timeout in seconds. Omit for permanent ban. */
|
|
27
|
+
timeout?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Options for removing a single address. */
|
|
31
|
+
export interface RemoveAddressOptions {
|
|
32
|
+
/** IPv4 or IPv6 address to remove. */
|
|
33
|
+
ip: string;
|
|
34
|
+
/** Target set: 'blacklist' or 'droplist'. */
|
|
35
|
+
set: TargetSet;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Options for bulk adding addresses. */
|
|
39
|
+
export interface AddAddressesOptions {
|
|
40
|
+
/** Array of IPv4/IPv6 addresses. */
|
|
41
|
+
ips: string[];
|
|
42
|
+
/** Target set: 'blacklist' or 'droplist'. */
|
|
43
|
+
set: TargetSet;
|
|
44
|
+
/** Timeout in seconds. Omit for permanent ban. */
|
|
45
|
+
timeout?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Options for bulk removing addresses. */
|
|
49
|
+
export interface RemoveAddressesOptions {
|
|
50
|
+
/** Array of IPv4/IPv6 addresses to remove. */
|
|
51
|
+
ips: string[];
|
|
52
|
+
/** Target set: 'blacklist' or 'droplist'. */
|
|
53
|
+
set: TargetSet;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class NftManager {
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new NftManager instance.
|
|
59
|
+
* Opens a netlink socket and validates configuration.
|
|
60
|
+
*
|
|
61
|
+
* @param options - Required configuration with table and set names.
|
|
62
|
+
* @throws {TypeError} if options are missing or have wrong types
|
|
63
|
+
* @throws {Error} if netlink socket cannot be opened (missing CAP_NET_ADMIN)
|
|
64
|
+
*/
|
|
65
|
+
constructor(options: NftManagerOptions);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates IPv4 and IPv6 tables with blacklist/droplist sets and filter chains.
|
|
69
|
+
* Idempotent — destroys existing tables first, then recreates.
|
|
70
|
+
*/
|
|
71
|
+
createTable(): Promise<void>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Deletes both IPv4 and IPv6 tables.
|
|
75
|
+
* Idempotent — no error if tables don't exist.
|
|
76
|
+
*/
|
|
77
|
+
deleteTable(): Promise<void>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Adds an IP address to a set.
|
|
81
|
+
* Auto-detects IPv4 vs IPv6 and routes to the correct table/set.
|
|
82
|
+
*
|
|
83
|
+
* @param options - Address, target set, and optional timeout
|
|
84
|
+
* @throws {TypeError} if options are invalid
|
|
85
|
+
* @throws {Error} if IP is invalid or nftables operation fails
|
|
86
|
+
*/
|
|
87
|
+
addAddress(options: AddAddressOptions): Promise<void>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Removes an IP address from a set.
|
|
91
|
+
* Idempotent — no error if IP is not in the set.
|
|
92
|
+
*
|
|
93
|
+
* @param options - Address and target set
|
|
94
|
+
* @throws {TypeError} if options are invalid
|
|
95
|
+
* @throws {Error} if IP is invalid or nftables operation fails
|
|
96
|
+
*/
|
|
97
|
+
removeAddress(options: RemoveAddressOptions): Promise<void>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Adds multiple IP addresses to a set in bulk.
|
|
101
|
+
* Addresses are chunked for efficient netlink communication.
|
|
102
|
+
* Empty arrays are a no-op.
|
|
103
|
+
*
|
|
104
|
+
* @param options - Addresses, target set, and optional timeout
|
|
105
|
+
* @throws {TypeError} if options are invalid
|
|
106
|
+
* @throws {Error} if any IP is invalid or nftables operation fails
|
|
107
|
+
*/
|
|
108
|
+
addAddresses(options: AddAddressesOptions): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Removes multiple IP addresses from a set in bulk.
|
|
112
|
+
* Idempotent — no error if IPs are not in the set.
|
|
113
|
+
* Empty arrays are a no-op.
|
|
114
|
+
*
|
|
115
|
+
* @param options - Addresses and target set
|
|
116
|
+
* @throws {TypeError} if options are invalid
|
|
117
|
+
* @throws {Error} if any IP is invalid or nftables operation fails
|
|
118
|
+
*/
|
|
119
|
+
removeAddresses(options: RemoveAddressesOptions): Promise<void>;
|
|
120
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
let binding;
|
|
6
|
+
try {
|
|
7
|
+
binding = require('node-gyp-build')(path.join(__dirname, '..'));
|
|
8
|
+
} catch (e) {
|
|
9
|
+
// On non-Linux platforms, the native addon may fail to load
|
|
10
|
+
binding = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (binding) {
|
|
14
|
+
module.exports = binding;
|
|
15
|
+
} else {
|
|
16
|
+
class NftManager {
|
|
17
|
+
constructor() {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'remnawave-nft only works on Linux with CAP_NET_ADMIN. Native binding failed to load.'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
module.exports = { NftManager };
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nftables-napi",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Native Node.js binding for nftables via libnftnl+libmnl — nftables firewall management",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./lib/index.d.ts",
|
|
10
|
+
"default": "./lib/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"gypfile": true,
|
|
14
|
+
"files": [
|
|
15
|
+
"lib/",
|
|
16
|
+
"src/",
|
|
17
|
+
"prebuilds/",
|
|
18
|
+
"binding.gyp"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/kastov/nftables-napi.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/kastov/nftables-napi#readme",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"install": "node-gyp-build || true",
|
|
27
|
+
"build": "node-gyp rebuild",
|
|
28
|
+
"rebuild": "node-gyp rebuild",
|
|
29
|
+
"test": "node --test test/test.mjs",
|
|
30
|
+
"prebuild:all": "./prebuild-all.sh"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"node-gyp-build": "^4.8.4",
|
|
34
|
+
"node-addon-api": "^8.3.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"node-gyp": "^12.2.0",
|
|
38
|
+
"prebuildify": "^6.0.1",
|
|
39
|
+
"@types/node": "^22.0.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=24.0.0"
|
|
43
|
+
},
|
|
44
|
+
"license": "AGPL-3.0-only"
|
|
45
|
+
}
|
|
Binary file
|
|
Binary file
|
package/src/addon.cpp
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
|
|
5
|
+
namespace nft {
|
|
6
|
+
|
|
7
|
+
// Chain names
|
|
8
|
+
inline constexpr const char* CHAIN_INPUT = "input";
|
|
9
|
+
inline constexpr const char* CHAIN_FORWARD = "forward";
|
|
10
|
+
|
|
11
|
+
// Chain type
|
|
12
|
+
inline constexpr const char* CHAIN_TYPE_FILTER = "filter";
|
|
13
|
+
|
|
14
|
+
// Data types
|
|
15
|
+
inline constexpr uint32_t DATATYPE_IPADDR = 7;
|
|
16
|
+
inline constexpr uint32_t DATATYPE_IP6ADDR = 8;
|
|
17
|
+
|
|
18
|
+
// Network header offsets and address lengths
|
|
19
|
+
// Offset of source address in IPv4 header (bytes)
|
|
20
|
+
inline constexpr uint32_t IPV4_SRC_OFFSET = 12;
|
|
21
|
+
// Offset of source address in IPv6 header (bytes)
|
|
22
|
+
inline constexpr uint32_t IPV6_SRC_OFFSET = 8;
|
|
23
|
+
inline constexpr uint32_t IPV4_ADDR_LEN = 4;
|
|
24
|
+
inline constexpr uint32_t IPV6_ADDR_LEN = 16;
|
|
25
|
+
|
|
26
|
+
// Chain priority
|
|
27
|
+
inline constexpr int32_t CHAIN_PRIORITY = -10;
|
|
28
|
+
|
|
29
|
+
// Bulk operation parameters
|
|
30
|
+
// Conservative chunk size for bulk set element operations
|
|
31
|
+
inline constexpr uint32_t BULK_CHUNK_SIZE = 200;
|
|
32
|
+
|
|
33
|
+
// Sequence number block size allocated per batch
|
|
34
|
+
inline constexpr uint32_t SEQ_BLOCK_SIZE = 256;
|
|
35
|
+
|
|
36
|
+
// Number of pages for default batch buffer (pagesize * 32 ≈ 128KB)
|
|
37
|
+
inline constexpr uint32_t DEFAULT_BUF_PAGES = 32;
|
|
38
|
+
|
|
39
|
+
} // namespace nft
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
namespace nft {
|
|
6
|
+
|
|
7
|
+
enum class TargetSet {
|
|
8
|
+
Blacklist,
|
|
9
|
+
Droplist
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
struct NftConfig {
|
|
13
|
+
std::string table_v4;
|
|
14
|
+
std::string table_v6;
|
|
15
|
+
std::string set_v4;
|
|
16
|
+
std::string set_v6;
|
|
17
|
+
std::string drop_set_v4;
|
|
18
|
+
std::string drop_set_v6;
|
|
19
|
+
std::string blacklist_log_prefix;
|
|
20
|
+
std::string droplist_log_prefix;
|
|
21
|
+
|
|
22
|
+
static NftConfig from_names(const std::string& table_name,
|
|
23
|
+
const std::string& blacklist_set_name,
|
|
24
|
+
const std::string& droplist_set_name) {
|
|
25
|
+
return {table_name, table_name + "6",
|
|
26
|
+
blacklist_set_name, blacklist_set_name + "6",
|
|
27
|
+
droplist_set_name, droplist_set_name + "6",
|
|
28
|
+
blacklist_set_name + ": ",
|
|
29
|
+
droplist_set_name + ": "};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const std::string& resolve_set_v4(TargetSet ts) const {
|
|
33
|
+
return (ts == TargetSet::Blacklist) ? set_v4 : drop_set_v4;
|
|
34
|
+
}
|
|
35
|
+
const std::string& resolve_set_v6(TargetSet ts) const {
|
|
36
|
+
return (ts == TargetSet::Blacklist) ? set_v6 : drop_set_v6;
|
|
37
|
+
}
|
|
38
|
+
const std::string& resolve_log_prefix(TargetSet ts) const {
|
|
39
|
+
return (ts == TargetSet::Blacklist) ? blacklist_log_prefix : droplist_log_prefix;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
} // namespace nft
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// RAII wrappers for libnftnl C objects.
|
|
4
|
+
//
|
|
5
|
+
// These wrappers use std::unique_ptr with custom deleters to ensure that
|
|
6
|
+
// libnftnl objects are properly freed when they go out of scope.
|
|
7
|
+
//
|
|
8
|
+
// Wrapped types:
|
|
9
|
+
// - nftnl_table (freed via nftnl_table_free)
|
|
10
|
+
// - nftnl_chain (freed via nftnl_chain_free)
|
|
11
|
+
// - nftnl_set (freed via nftnl_set_free)
|
|
12
|
+
// - nftnl_rule (freed via nftnl_rule_free)
|
|
13
|
+
//
|
|
14
|
+
// Intentionally NOT wrapped:
|
|
15
|
+
// - nftnl_expr — nftnl_rule_add_expr(rule, expr) transfers ownership
|
|
16
|
+
// of the expr to the rule. Wrapping it would cause a
|
|
17
|
+
// double-free when the rule is destroyed.
|
|
18
|
+
// - nftnl_set_elem — nftnl_set_elem_add(set, elem) transfers ownership
|
|
19
|
+
// of the elem to the set. Wrapping it would cause a
|
|
20
|
+
// double-free when the set is destroyed.
|
|
21
|
+
|
|
22
|
+
extern "C" {
|
|
23
|
+
#include <libnftnl/table.h>
|
|
24
|
+
#include <libnftnl/chain.h>
|
|
25
|
+
#include <libnftnl/set.h>
|
|
26
|
+
#include <libnftnl/rule.h>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#include <memory>
|
|
30
|
+
|
|
31
|
+
namespace nft {
|
|
32
|
+
|
|
33
|
+
// --- Table -------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
struct NftnlTableDeleter {
|
|
36
|
+
void operator()(struct nftnl_table* t) const noexcept { nftnl_table_free(t); }
|
|
37
|
+
};
|
|
38
|
+
using UniqueTable = std::unique_ptr<struct nftnl_table, NftnlTableDeleter>;
|
|
39
|
+
|
|
40
|
+
inline UniqueTable make_table() { return UniqueTable(nftnl_table_alloc()); }
|
|
41
|
+
|
|
42
|
+
// --- Chain -------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
struct NftnlChainDeleter {
|
|
45
|
+
void operator()(struct nftnl_chain* c) const noexcept { nftnl_chain_free(c); }
|
|
46
|
+
};
|
|
47
|
+
using UniqueChain = std::unique_ptr<struct nftnl_chain, NftnlChainDeleter>;
|
|
48
|
+
|
|
49
|
+
inline UniqueChain make_chain() { return UniqueChain(nftnl_chain_alloc()); }
|
|
50
|
+
|
|
51
|
+
// --- Set ---------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
struct NftnlSetDeleter {
|
|
54
|
+
void operator()(struct nftnl_set* s) const noexcept { nftnl_set_free(s); }
|
|
55
|
+
};
|
|
56
|
+
using UniqueSet = std::unique_ptr<struct nftnl_set, NftnlSetDeleter>;
|
|
57
|
+
|
|
58
|
+
inline UniqueSet make_set() { return UniqueSet(nftnl_set_alloc()); }
|
|
59
|
+
|
|
60
|
+
// --- Rule --------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
struct NftnlRuleDeleter {
|
|
63
|
+
void operator()(struct nftnl_rule* r) const noexcept { nftnl_rule_free(r); }
|
|
64
|
+
};
|
|
65
|
+
using UniqueRule = std::unique_ptr<struct nftnl_rule, NftnlRuleDeleter>;
|
|
66
|
+
|
|
67
|
+
inline UniqueRule make_rule() { return UniqueRule(nftnl_rule_alloc()); }
|
|
68
|
+
|
|
69
|
+
} // namespace nft
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#include "nl_batch.h"
|
|
2
|
+
#include "constants.h"
|
|
3
|
+
#include "nl_socket.h"
|
|
4
|
+
|
|
5
|
+
#include <atomic>
|
|
6
|
+
#include <ctime>
|
|
7
|
+
#include <unistd.h>
|
|
8
|
+
|
|
9
|
+
static uint32_t alloc_seq_block() {
|
|
10
|
+
static std::atomic<uint32_t> counter{static_cast<uint32_t>(time(nullptr))};
|
|
11
|
+
return counter.fetch_add(nft::SEQ_BLOCK_SIZE);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
NlBatch::NlBatch() : NlBatch([]() -> size_t {
|
|
15
|
+
long ps = sysconf(_SC_PAGESIZE);
|
|
16
|
+
if (ps <= 0) ps = 4096;
|
|
17
|
+
return static_cast<size_t>(ps) * nft::DEFAULT_BUF_PAGES;
|
|
18
|
+
}()) {}
|
|
19
|
+
|
|
20
|
+
NlBatch::NlBatch(size_t buf_size)
|
|
21
|
+
: buf_(std::make_unique<char[]>(buf_size)),
|
|
22
|
+
batch_(nullptr),
|
|
23
|
+
seq_(0) {
|
|
24
|
+
batch_ = mnl_nlmsg_batch_start(buf_.get(), buf_size);
|
|
25
|
+
if (!batch_)
|
|
26
|
+
return;
|
|
27
|
+
|
|
28
|
+
seq_ = alloc_seq_block();
|
|
29
|
+
nftnl_batch_begin(static_cast<char*>(mnl_nlmsg_batch_current(batch_)), seq_++);
|
|
30
|
+
mnl_nlmsg_batch_next(batch_);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
NlBatch::~NlBatch() {
|
|
34
|
+
if (batch_)
|
|
35
|
+
mnl_nlmsg_batch_stop(batch_);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
bool NlBatch::is_valid() const {
|
|
39
|
+
return batch_ != nullptr;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
struct nlmsghdr* NlBatch::add_msg(uint16_t type, uint16_t family, uint16_t flags) {
|
|
43
|
+
if (!batch_) return nullptr;
|
|
44
|
+
|
|
45
|
+
return nftnl_nlmsg_build_hdr(
|
|
46
|
+
static_cast<char*>(mnl_nlmsg_batch_current(batch_)),
|
|
47
|
+
type,
|
|
48
|
+
family,
|
|
49
|
+
flags,
|
|
50
|
+
seq_++
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
bool NlBatch::advance() {
|
|
55
|
+
if (!batch_) return false;
|
|
56
|
+
return mnl_nlmsg_batch_next(batch_);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
NlResult NlBatch::execute(NlSocket& sock, bool ignore_enoent) {
|
|
60
|
+
if (!batch_)
|
|
61
|
+
return {false, "batch not initialized"};
|
|
62
|
+
|
|
63
|
+
nftnl_batch_end(static_cast<char*>(mnl_nlmsg_batch_current(batch_)), seq_++);
|
|
64
|
+
mnl_nlmsg_batch_next(batch_);
|
|
65
|
+
|
|
66
|
+
return sock.send_batch(batch_, ignore_enoent);
|
|
67
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
extern "C" {
|
|
4
|
+
#include <libmnl/libmnl.h>
|
|
5
|
+
#include <libnftnl/batch.h>
|
|
6
|
+
#include <libnftnl/common.h>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#include <cstdint>
|
|
10
|
+
#include <memory>
|
|
11
|
+
|
|
12
|
+
#include "nl_result.h"
|
|
13
|
+
|
|
14
|
+
class NlSocket;
|
|
15
|
+
|
|
16
|
+
class NlBatch {
|
|
17
|
+
public:
|
|
18
|
+
NlBatch();
|
|
19
|
+
explicit NlBatch(size_t buf_size);
|
|
20
|
+
~NlBatch();
|
|
21
|
+
|
|
22
|
+
NlBatch(const NlBatch&) = delete;
|
|
23
|
+
NlBatch& operator=(const NlBatch&) = delete;
|
|
24
|
+
NlBatch(NlBatch&&) = delete;
|
|
25
|
+
NlBatch& operator=(NlBatch&&) = delete;
|
|
26
|
+
|
|
27
|
+
bool is_valid() const;
|
|
28
|
+
struct nlmsghdr* add_msg(uint16_t type, uint16_t family, uint16_t flags);
|
|
29
|
+
bool advance();
|
|
30
|
+
NlResult execute(NlSocket& sock, bool ignore_enoent = false);
|
|
31
|
+
|
|
32
|
+
private:
|
|
33
|
+
std::unique_ptr<char[]> buf_;
|
|
34
|
+
struct mnl_nlmsg_batch* batch_;
|
|
35
|
+
uint32_t seq_;
|
|
36
|
+
};
|