nftables-napi 0.1.1 → 0.3.0
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/README.md +144 -24
- package/binding.gyp +1 -1
- package/lib/index.d.ts +19 -19
- package/lib/index.js +0 -1
- package/package.json +1 -1
- package/prebuilds/linux-arm64/nftables-napi.node +0 -0
- package/prebuilds/linux-x64/nftables-napi.node +0 -0
- package/src/nft_manager.cpp +12 -12
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# nftables-napi
|
|
2
2
|
|
|
3
|
-
Native Node.js binding for nftables via libnftnl + libmnl. Manages IPv4/IPv6 tables with dynamic sets and timeout support through direct netlink communication — no shell commands, no `nft` CLI.
|
|
3
|
+
Native Node.js binding for nftables via libnftnl + libmnl. Manages IPv4/IPv6 firewall tables with dynamic IP sets, port blocking, named counters, and timeout support through direct netlink communication — no shell commands, no `nft` CLI.
|
|
4
4
|
|
|
5
|
-
Requires Linux with `CAP_NET_ADMIN` or root.
|
|
5
|
+
Requires Linux kernel ≥ 5.7 with `CAP_NET_ADMIN` or root.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -34,26 +34,44 @@ RUN apt-get update && apt-get install -y libnftnl13 libmnl0 && rm -rf /var/lib/a
|
|
|
34
34
|
const { NftManager } = require("nftables-napi");
|
|
35
35
|
|
|
36
36
|
const nft = new NftManager({
|
|
37
|
-
tableName: "
|
|
38
|
-
|
|
37
|
+
tableName: "myfw",
|
|
38
|
+
ingressAddrSets: ["blacklist"],
|
|
39
|
+
egressAddrSets: ["blocklist"],
|
|
40
|
+
egressPortSets: ["blocked_ports"],
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
await nft.createTable();
|
|
42
44
|
|
|
43
|
-
//
|
|
45
|
+
// ── IP blocking (input/forward) ──
|
|
46
|
+
|
|
44
47
|
await nft.addAddress({ ip: "1.2.3.4", set: "blacklist", timeout: 1800 });
|
|
45
48
|
await nft.addAddress({ ip: "2001:db8::1", set: "blacklist", timeout: 3600 });
|
|
46
|
-
|
|
47
|
-
// Add permanent (no timeout)
|
|
48
|
-
await nft.addAddress({ ip: "5.6.7.8", set: "droplist" });
|
|
49
|
-
|
|
50
|
-
// Bulk add
|
|
51
49
|
await nft.addAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist", timeout: 7200 });
|
|
52
50
|
|
|
53
|
-
// Remove
|
|
54
51
|
await nft.removeAddress({ ip: "1.2.3.4", set: "blacklist" });
|
|
55
52
|
await nft.removeAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist" });
|
|
56
53
|
|
|
54
|
+
// ── IP blocking (output) ──
|
|
55
|
+
|
|
56
|
+
await nft.addAddress({ ip: "93.184.216.34", set: "blocklist" });
|
|
57
|
+
await nft.removeAddress({ ip: "93.184.216.34", set: "blocklist" });
|
|
58
|
+
|
|
59
|
+
// ── Port blocking (output, tcp/udp) ──
|
|
60
|
+
|
|
61
|
+
// Block port 80 for both TCP and UDP
|
|
62
|
+
await nft.addPort({ port: 80, set: "blocked_ports", timeout: 3600 });
|
|
63
|
+
|
|
64
|
+
// Block port 443 for TCP only
|
|
65
|
+
await nft.addPort({ port: 443, set: "blocked_ports", protocol: "tcp" });
|
|
66
|
+
|
|
67
|
+
// Bulk port operations
|
|
68
|
+
await nft.addPorts({ ports: [8080, 8443], set: "blocked_ports", protocol: "tcp" });
|
|
69
|
+
await nft.removePorts({ ports: [8080, 8443], set: "blocked_ports", protocol: "tcp" });
|
|
70
|
+
|
|
71
|
+
await nft.removePort({ port: 80, set: "blocked_ports" });
|
|
72
|
+
|
|
73
|
+
// ── Cleanup ──
|
|
74
|
+
|
|
57
75
|
await nft.deleteTable();
|
|
58
76
|
```
|
|
59
77
|
|
|
@@ -61,23 +79,119 @@ await nft.deleteTable();
|
|
|
61
79
|
|
|
62
80
|
### `new NftManager(options)`
|
|
63
81
|
|
|
64
|
-
| Option
|
|
65
|
-
|
|
|
66
|
-
| `tableName` | `string`
|
|
67
|
-
| `
|
|
82
|
+
| Option | Type | Required | Description |
|
|
83
|
+
| --- | --- | --- | --- |
|
|
84
|
+
| `tableName` | `string` | Yes | Base table name. IPv6 table auto-appends `'6'`. |
|
|
85
|
+
| `ingressAddrSets` | `string[]` | Yes | Input/forward IP set names (≥1). Block by **source** address on input and forward chains. Rules: log + named counter + drop. IPv6 sets auto-append `'6'`. |
|
|
86
|
+
| `egressAddrSets` | `string[]` | No | Output IP set names. Block by **destination** address on output chain. Rules: named counter + drop (no log). IPv6 sets auto-append `'6'`. |
|
|
87
|
+
| `egressPortSets` | `string[]` | No | Output port set names. Block by **destination port** (TCP/UDP) on output chain using concatenated `inet_proto . inet_service` sets. Ports are added to both IPv4 and IPv6 tables. IPv6 sets auto-append `'6'`. |
|
|
68
88
|
|
|
69
89
|
### Methods
|
|
70
90
|
|
|
71
|
-
All methods return `Promise<void
|
|
91
|
+
All methods return `Promise<void>` and throw on error.
|
|
92
|
+
|
|
93
|
+
#### Table management
|
|
94
|
+
|
|
95
|
+
| Method | Description |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `createTable()` | Create IPv4/IPv6 tables with all configured sets, chains, named counters, and filter rules. Idempotent — deletes existing tables first. |
|
|
98
|
+
| `deleteTable()` | Delete both tables. Idempotent — no error if tables don't exist. |
|
|
99
|
+
|
|
100
|
+
#### IP address operations
|
|
101
|
+
|
|
102
|
+
Work with both `ingressAddrSets` (input/forward) and `egressAddrSets` (output).
|
|
72
103
|
|
|
73
|
-
| Method
|
|
74
|
-
|
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
|
|
80
|
-
|
|
104
|
+
| Method | Description |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `addAddress({ ip, set, timeout? })` | Add IP to set. Auto-detects IPv4/IPv6. `timeout` in seconds, omit for permanent. |
|
|
107
|
+
| `removeAddress({ ip, set })` | Remove IP from set. Idempotent. |
|
|
108
|
+
| `addAddresses({ ips, set, timeout? })` | Bulk add. Chunked internally for efficient netlink communication. Empty array is a no-op. |
|
|
109
|
+
| `removeAddresses({ ips, set })` | Bulk remove. Idempotent. Empty array is a no-op. |
|
|
110
|
+
|
|
111
|
+
#### Port operations
|
|
112
|
+
|
|
113
|
+
Work with `egressPortSets` only. Ports are added to both IPv4 and IPv6 tables.
|
|
114
|
+
|
|
115
|
+
| Method | Description |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `addPort({ port, set, protocol?, timeout? })` | Add port to set. `protocol`: `'tcp'`, `'udp'`, or omit for both. `timeout` in seconds. |
|
|
118
|
+
| `removePort({ port, set, protocol? })` | Remove port from set. Idempotent. |
|
|
119
|
+
| `addPorts({ ports, set, protocol?, timeout? })` | Bulk add ports. Empty array is a no-op. |
|
|
120
|
+
| `removePorts({ ports, set, protocol? })` | Bulk remove ports. Idempotent. Empty array is a no-op. |
|
|
121
|
+
|
|
122
|
+
### What `createTable()` builds
|
|
123
|
+
|
|
124
|
+
For a config with `ingressAddrSets: ["bl"]`, `egressAddrSets: ["out"]`, `egressPortSets: ["ports"]`:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
table ip myfw {
|
|
128
|
+
counter "processed" { packets 0 bytes 0 }
|
|
129
|
+
counter "bl" { packets 0 bytes 0 }
|
|
130
|
+
counter "out" { packets 0 bytes 0 }
|
|
131
|
+
counter "ports" { packets 0 bytes 0 }
|
|
132
|
+
|
|
133
|
+
set bl {
|
|
134
|
+
type ipv4_addr
|
|
135
|
+
flags timeout
|
|
136
|
+
counter
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
set out {
|
|
140
|
+
type ipv4_addr
|
|
141
|
+
flags timeout
|
|
142
|
+
counter
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
set ports {
|
|
146
|
+
type inet_proto . inet_service
|
|
147
|
+
flags timeout
|
|
148
|
+
counter
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
chain input {
|
|
152
|
+
type filter hook input priority -10; policy accept;
|
|
153
|
+
counter name "processed"
|
|
154
|
+
ip saddr @bl log prefix "bl: " counter name "bl" drop
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
chain forward {
|
|
158
|
+
type filter hook forward priority -10; policy accept;
|
|
159
|
+
counter name "processed"
|
|
160
|
+
ip saddr @bl log prefix "bl: " counter name "bl" drop
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
chain output {
|
|
164
|
+
type filter hook output priority -10; policy accept;
|
|
165
|
+
ip daddr @out counter name "out" drop
|
|
166
|
+
meta l4proto . th dport @ports counter name "ports" drop
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
IPv6 table (`myfw6`) mirrors the same structure with `ipv6_addr` sets and corresponding offsets.
|
|
172
|
+
|
|
173
|
+
## Kernel compatibility
|
|
174
|
+
|
|
175
|
+
Minimum: **Linux 5.7**
|
|
176
|
+
|
|
177
|
+
| Feature | Kernel | Used for |
|
|
178
|
+
| --- | --- | --- |
|
|
179
|
+
| nftables core | 3.13 | tables, chains, sets, rules |
|
|
180
|
+
| Set timeouts | 4.1 | element expiration |
|
|
181
|
+
| Named counters | 4.10 | traffic accounting |
|
|
182
|
+
| Concatenated sets | 5.6 | port blocking (`inet_proto . inet_service`) |
|
|
183
|
+
| Per-element set expressions | 5.7 | per-element counters |
|
|
184
|
+
|
|
185
|
+
| Distro | Kernel | Compatible |
|
|
186
|
+
| --- | --- | --- |
|
|
187
|
+
| Ubuntu 22.04+ | 5.15+ | Yes |
|
|
188
|
+
| Ubuntu 20.04 (HWE) | 5.15 | Yes |
|
|
189
|
+
| Ubuntu 20.04 (GA) | 5.4 | No |
|
|
190
|
+
| Debian 11+ | 5.10+ | Yes |
|
|
191
|
+
| Debian 10 | 4.19 | No |
|
|
192
|
+
| RHEL / Rocky 9 | 5.14 | Yes |
|
|
193
|
+
| RHEL / Rocky 8 | 4.18 | No |
|
|
194
|
+
| Alpine 3.16+ | 5.15+ | Yes |
|
|
81
195
|
|
|
82
196
|
## Building from source
|
|
83
197
|
|
|
@@ -85,9 +199,15 @@ All methods return `Promise<void>`.
|
|
|
85
199
|
# Dependencies (Debian/Ubuntu)
|
|
86
200
|
sudo apt install pkg-config libnftnl-dev libmnl-dev build-essential
|
|
87
201
|
|
|
202
|
+
# Dependencies (Alpine)
|
|
203
|
+
apk add pkgconfig libnftnl-dev libmnl-dev build-base python3
|
|
204
|
+
|
|
88
205
|
# Build
|
|
89
206
|
npm run build
|
|
90
207
|
|
|
208
|
+
# Run tests (requires root / CAP_NET_ADMIN)
|
|
209
|
+
npm test
|
|
210
|
+
|
|
91
211
|
# Prebuild for current platform
|
|
92
212
|
npx prebuildify --napi --strip
|
|
93
213
|
|
package/binding.gyp
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -9,31 +9,31 @@ export interface NftManagerOptions {
|
|
|
9
9
|
/** Base table name. IPv6 table auto-appends '6'. */
|
|
10
10
|
tableName: string;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Ingress IP set names (≥1 required). Block by source address.
|
|
13
13
|
* Rules: log prefix "<setName>: " + named counter + drop on input and forward chains.
|
|
14
14
|
* IPv6 sets auto-append '6'.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
ingressAddrSets: string[];
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Egress IP set names (optional). Block by destination address.
|
|
19
19
|
* Rules: named counter + drop on output chain (no log).
|
|
20
20
|
* IPv6 sets auto-append '6'.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
egressAddrSets?: string[];
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Egress port set names (optional). Block by tcp/udp destination port.
|
|
25
25
|
* Rules: single concatenated (proto . port) lookup + named counter + drop on output chain (no log).
|
|
26
26
|
* Port is added to BOTH IPv4 and IPv6 tables (ports are family-independent).
|
|
27
27
|
* IPv6 sets auto-append '6'.
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
egressPortSets?: string[];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/** Options for adding a single address. */
|
|
33
33
|
export interface AddAddressOptions {
|
|
34
34
|
/** IPv4 or IPv6 address (e.g., "1.2.3.4" or "2001:db8::1"). */
|
|
35
35
|
ip: string;
|
|
36
|
-
/** Target set name (must match one from constructor's
|
|
36
|
+
/** Target set name (must match one from constructor's ingressAddrSets or egressAddrSets). */
|
|
37
37
|
set: string;
|
|
38
38
|
/** Timeout in seconds. Omit for permanent. */
|
|
39
39
|
timeout?: number;
|
|
@@ -43,7 +43,7 @@ export interface AddAddressOptions {
|
|
|
43
43
|
export interface RemoveAddressOptions {
|
|
44
44
|
/** IPv4 or IPv6 address to remove. */
|
|
45
45
|
ip: string;
|
|
46
|
-
/** Target set name (must match one from constructor's
|
|
46
|
+
/** Target set name (must match one from constructor's ingressAddrSets or egressAddrSets). */
|
|
47
47
|
set: string;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ export interface RemoveAddressOptions {
|
|
|
51
51
|
export interface AddAddressesOptions {
|
|
52
52
|
/** Array of IPv4/IPv6 addresses. */
|
|
53
53
|
ips: string[];
|
|
54
|
-
/** Target set name (must match one from constructor's
|
|
54
|
+
/** Target set name (must match one from constructor's ingressAddrSets or egressAddrSets). */
|
|
55
55
|
set: string;
|
|
56
56
|
/** Timeout in seconds. Omit for permanent. */
|
|
57
57
|
timeout?: number;
|
|
@@ -61,7 +61,7 @@ export interface AddAddressesOptions {
|
|
|
61
61
|
export interface RemoveAddressesOptions {
|
|
62
62
|
/** Array of IPv4/IPv6 addresses to remove. */
|
|
63
63
|
ips: string[];
|
|
64
|
-
/** Target set name (must match one from constructor's
|
|
64
|
+
/** Target set name (must match one from constructor's ingressAddrSets or egressAddrSets). */
|
|
65
65
|
set: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ export interface RemoveAddressesOptions {
|
|
|
69
69
|
export interface AddPortOptions {
|
|
70
70
|
/** Port number (0-65535). */
|
|
71
71
|
port: number;
|
|
72
|
-
/** Target port set name (must match one from constructor's
|
|
72
|
+
/** Target port set name (must match one from constructor's egressPortSets). */
|
|
73
73
|
set: string;
|
|
74
74
|
/** Protocol: 'tcp', 'udp', or omit for both. Default: both. */
|
|
75
75
|
protocol?: 'tcp' | 'udp';
|
|
@@ -81,7 +81,7 @@ export interface AddPortOptions {
|
|
|
81
81
|
export interface RemovePortOptions {
|
|
82
82
|
/** Port number (0-65535). */
|
|
83
83
|
port: number;
|
|
84
|
-
/** Target port set name (must match one from constructor's
|
|
84
|
+
/** Target port set name (must match one from constructor's egressPortSets). */
|
|
85
85
|
set: string;
|
|
86
86
|
/** Protocol: 'tcp', 'udp', or omit for both. Default: both. */
|
|
87
87
|
protocol?: 'tcp' | 'udp';
|
|
@@ -91,7 +91,7 @@ export interface RemovePortOptions {
|
|
|
91
91
|
export interface AddPortsOptions {
|
|
92
92
|
/** Array of port numbers (0-65535). */
|
|
93
93
|
ports: number[];
|
|
94
|
-
/** Target port set name (must match one from constructor's
|
|
94
|
+
/** Target port set name (must match one from constructor's egressPortSets). */
|
|
95
95
|
set: string;
|
|
96
96
|
/** Protocol: 'tcp', 'udp', or omit for both. Default: both. */
|
|
97
97
|
protocol?: 'tcp' | 'udp';
|
|
@@ -103,7 +103,7 @@ export interface AddPortsOptions {
|
|
|
103
103
|
export interface RemovePortsOptions {
|
|
104
104
|
/** Array of port numbers (0-65535). */
|
|
105
105
|
ports: number[];
|
|
106
|
-
/** Target port set name (must match one from constructor's
|
|
106
|
+
/** Target port set name (must match one from constructor's egressPortSets). */
|
|
107
107
|
set: string;
|
|
108
108
|
/** Protocol: 'tcp', 'udp', or omit for both. Default: both. */
|
|
109
109
|
protocol?: 'tcp' | 'udp';
|
|
@@ -127,9 +127,9 @@ export class NftManager {
|
|
|
127
127
|
* Creates:
|
|
128
128
|
* - Named counter "processed" (global traffic counter per chain)
|
|
129
129
|
* - Named counter per set (blocked traffic counter)
|
|
130
|
-
* - Input chain with log + counter + drop rules (for
|
|
131
|
-
* - Forward chain with log + counter + drop rules (for
|
|
132
|
-
* - Output chain with counter + drop rules (for
|
|
130
|
+
* - Input chain with log + counter + drop rules (for ingressAddrSets)
|
|
131
|
+
* - Forward chain with log + counter + drop rules (for ingressAddrSets)
|
|
132
|
+
* - Output chain with counter + drop rules (for egressAddrSets and egressPortSets, no log)
|
|
133
133
|
* - Per-element counters on all sets
|
|
134
134
|
*
|
|
135
135
|
* @throws {Error} if nftables operation fails
|
|
@@ -147,7 +147,7 @@ export class NftManager {
|
|
|
147
147
|
/**
|
|
148
148
|
* Adds an IP address to a set.
|
|
149
149
|
* Auto-detects IPv4 vs IPv6 and routes to the correct table/set.
|
|
150
|
-
* Works with both input sets (
|
|
150
|
+
* Works with both input sets (ingressAddrSets) and output sets (egressAddrSets).
|
|
151
151
|
*
|
|
152
152
|
* @param options - Address, target set name, and optional timeout.
|
|
153
153
|
* @throws {TypeError} if options or fields have wrong types
|
|
@@ -158,7 +158,7 @@ export class NftManager {
|
|
|
158
158
|
/**
|
|
159
159
|
* Removes an IP address from a set.
|
|
160
160
|
* Idempotent — no error if IP is not in the set.
|
|
161
|
-
* Works with both input sets (
|
|
161
|
+
* Works with both input sets (ingressAddrSets) and output sets (egressAddrSets).
|
|
162
162
|
*
|
|
163
163
|
* @param options - Address and target set name.
|
|
164
164
|
* @throws {TypeError} if options or fields have wrong types
|
package/lib/index.js
CHANGED
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
package/src/nft_manager.cpp
CHANGED
|
@@ -250,7 +250,7 @@ NftManager::NftManager(const Napi::CallbackInfo& info)
|
|
|
250
250
|
|
|
251
251
|
if (info.Length() < 1 || !info[0].IsObject()) {
|
|
252
252
|
Napi::TypeError::New(env,
|
|
253
|
-
"NftManager requires options object with tableName and
|
|
253
|
+
"NftManager requires options object with tableName and ingressAddrSets")
|
|
254
254
|
.ThrowAsJavaScriptException();
|
|
255
255
|
return;
|
|
256
256
|
}
|
|
@@ -264,18 +264,18 @@ NftManager::NftManager(const Napi::CallbackInfo& info)
|
|
|
264
264
|
return;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
//
|
|
268
|
-
if (!opts.Has("
|
|
269
|
-
Napi::TypeError::New(env, "NftManager: '
|
|
267
|
+
// ingressAddrSets — required non-empty array
|
|
268
|
+
if (!opts.Has("ingressAddrSets") || !opts.Get("ingressAddrSets").IsArray()) {
|
|
269
|
+
Napi::TypeError::New(env, "NftManager: 'ingressAddrSets' is required and must be an array of strings")
|
|
270
270
|
.ThrowAsJavaScriptException();
|
|
271
271
|
return;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
Napi::Array sets_arr = opts.Get("
|
|
274
|
+
Napi::Array sets_arr = opts.Get("ingressAddrSets").As<Napi::Array>();
|
|
275
275
|
uint32_t len = sets_arr.Length();
|
|
276
276
|
|
|
277
277
|
if (len == 0) {
|
|
278
|
-
Napi::Error::New(env, "NftManager: '
|
|
278
|
+
Napi::Error::New(env, "NftManager: 'ingressAddrSets' must contain at least one set name")
|
|
279
279
|
.ThrowAsJavaScriptException();
|
|
280
280
|
return;
|
|
281
281
|
}
|
|
@@ -286,25 +286,25 @@ NftManager::NftManager(const Napi::CallbackInfo& info)
|
|
|
286
286
|
for (uint32_t i = 0; i < len; ++i) {
|
|
287
287
|
Napi::Value val = sets_arr[i];
|
|
288
288
|
if (!val.IsString()) {
|
|
289
|
-
Napi::TypeError::New(env, "NftManager: '
|
|
289
|
+
Napi::TypeError::New(env, "NftManager: 'ingressAddrSets[" + std::to_string(i) + "]' must be a string")
|
|
290
290
|
.ThrowAsJavaScriptException();
|
|
291
291
|
return;
|
|
292
292
|
}
|
|
293
293
|
std::string name = val.As<Napi::String>().Utf8Value();
|
|
294
294
|
if (name.empty()) {
|
|
295
|
-
Napi::Error::New(env, "NftManager: '
|
|
295
|
+
Napi::Error::New(env, "NftManager: 'ingressAddrSets[" + std::to_string(i) + "]' must not be empty")
|
|
296
296
|
.ThrowAsJavaScriptException();
|
|
297
297
|
return;
|
|
298
298
|
}
|
|
299
299
|
in_sets.push_back(std::move(name));
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
// Parse optional
|
|
303
|
-
std::vector<std::string> out_sets = parse_optional_string_array(env, opts, "
|
|
302
|
+
// Parse optional egressAddrSets (OutIP)
|
|
303
|
+
std::vector<std::string> out_sets = parse_optional_string_array(env, opts, "egressAddrSets", "NftManager");
|
|
304
304
|
if (env.IsExceptionPending()) return;
|
|
305
305
|
|
|
306
|
-
// Parse optional
|
|
307
|
-
std::vector<std::string> out_port_sets = parse_optional_string_array(env, opts, "
|
|
306
|
+
// Parse optional egressPortSets (OutPort)
|
|
307
|
+
std::vector<std::string> out_port_sets = parse_optional_string_array(env, opts, "egressPortSets", "NftManager");
|
|
308
308
|
if (env.IsExceptionPending()) return;
|
|
309
309
|
|
|
310
310
|
// Cross-array duplicate check: all names must be unique across all arrays
|