@decentnetwork/lan 0.1.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/LICENSE +31 -0
- package/README.md +296 -0
- package/bin/tun-helper-darwin-amd64 +0 -0
- package/bin/tun-helper-darwin-arm64 +0 -0
- package/bin/tun-helper-linux-amd64 +0 -0
- package/bin/tun-helper-linux-arm64 +0 -0
- package/dist/acl/acl-engine.d.ts +43 -0
- package/dist/acl/acl-engine.js +189 -0
- package/dist/acl/audit.d.ts +70 -0
- package/dist/acl/audit.js +144 -0
- package/dist/acl/index.d.ts +4 -0
- package/dist/acl/index.js +3 -0
- package/dist/acl/policy.d.ts +31 -0
- package/dist/acl/policy.js +102 -0
- package/dist/acl/types.d.ts +18 -0
- package/dist/acl/types.js +4 -0
- package/dist/carrier/frame.d.ts +18 -0
- package/dist/carrier/frame.js +66 -0
- package/dist/carrier/index.d.ts +5 -0
- package/dist/carrier/index.js +4 -0
- package/dist/carrier/packet-session.d.ts +32 -0
- package/dist/carrier/packet-session.js +151 -0
- package/dist/carrier/peer-manager.d.ts +113 -0
- package/dist/carrier/peer-manager.js +392 -0
- package/dist/carrier/types.d.ts +10 -0
- package/dist/carrier/types.js +11 -0
- package/dist/cli/commands.d.ts +223 -0
- package/dist/cli/commands.js +932 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +196 -0
- package/dist/config/loader.d.ts +10 -0
- package/dist/config/loader.js +152 -0
- package/dist/daemon/index.d.ts +1 -0
- package/dist/daemon/index.js +1 -0
- package/dist/daemon/ipc.d.ts +60 -0
- package/dist/daemon/ipc.js +144 -0
- package/dist/daemon/server.d.ts +63 -0
- package/dist/daemon/server.js +510 -0
- package/dist/dns/index.d.ts +1 -0
- package/dist/dns/index.js +1 -0
- package/dist/dns/resolver.d.ts +44 -0
- package/dist/dns/resolver.js +82 -0
- package/dist/dns/server.d.ts +70 -0
- package/dist/dns/server.js +393 -0
- package/dist/dora/dora-integration.d.ts +90 -0
- package/dist/dora/dora-integration.js +325 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +15 -0
- package/dist/ipam/index.d.ts +1 -0
- package/dist/ipam/index.js +1 -0
- package/dist/ipam/ipam.d.ts +99 -0
- package/dist/ipam/ipam.js +254 -0
- package/dist/proxy/connect-proxy.d.ts +78 -0
- package/dist/proxy/connect-proxy.js +204 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.js +4 -0
- package/dist/router/ip-parser.d.ts +36 -0
- package/dist/router/ip-parser.js +127 -0
- package/dist/router/packet-router.d.ts +49 -0
- package/dist/router/packet-router.js +251 -0
- package/dist/router/session-manager.d.ts +50 -0
- package/dist/router/session-manager.js +138 -0
- package/dist/router/types.d.ts +21 -0
- package/dist/router/types.js +6 -0
- package/dist/tun/index.d.ts +3 -0
- package/dist/tun/index.js +2 -0
- package/dist/tun/route-manager.d.ts +59 -0
- package/dist/tun/route-manager.js +353 -0
- package/dist/tun/tun-device.d.ts +45 -0
- package/dist/tun/tun-device.js +265 -0
- package/dist/tun/types.d.ts +28 -0
- package/dist/tun/types.js +4 -0
- package/dist/types.d.ts +176 -0
- package/dist/types.js +4 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.js +43 -0
- package/docs/CONFIGURATION.md +197 -0
- package/docs/INSTALL.md +145 -0
- package/package.json +93 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUN device + route management.
|
|
3
|
+
* Linux uses `ip` (iproute2). macOS uses `ifconfig` + `route`.
|
|
4
|
+
*/
|
|
5
|
+
import type { TunDeviceConfig } from "./types.js";
|
|
6
|
+
export declare class RouteManager {
|
|
7
|
+
private logger;
|
|
8
|
+
private createdInterface;
|
|
9
|
+
private platform;
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Create and configure TUN interface (Linux only — on macOS the helper
|
|
13
|
+
* binary creates the utun device implicitly).
|
|
14
|
+
*/
|
|
15
|
+
createTun(config: TunDeviceConfig): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Configure an existing TUN interface (assign IP, bring up, add route).
|
|
18
|
+
* `name` here should be the ACTUAL device name (e.g. utun12 on macOS).
|
|
19
|
+
*/
|
|
20
|
+
configureTun(config: TunDeviceConfig): Promise<void>;
|
|
21
|
+
private configureTunLinux;
|
|
22
|
+
private configureTunDarwin;
|
|
23
|
+
/**
|
|
24
|
+
* Remove TUN interface (Linux only — on macOS utun goes away when helper exits).
|
|
25
|
+
*/
|
|
26
|
+
removeTun(name: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Cleanup all routes and interface. Pass the daemon's actual TUN
|
|
29
|
+
* IP so we can also remove the macOS lo0 self-alias we added in
|
|
30
|
+
* configureTun; without that, the alias persists across daemon
|
|
31
|
+
* restarts (harmless, but leaks state).
|
|
32
|
+
*/
|
|
33
|
+
cleanup(name: string, subnet: string, ip?: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Enable IP forwarding (Linux only)
|
|
36
|
+
*/
|
|
37
|
+
enableIpForwarding(): Promise<void>;
|
|
38
|
+
disableIpForwarding(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Setup NAT/masquerade — Linux iptables only (gateway mode).
|
|
41
|
+
* Not implemented on macOS in MVP.
|
|
42
|
+
*/
|
|
43
|
+
setupMasquerade(subnet: string, outInterface: string, tunInterface: string): Promise<void>;
|
|
44
|
+
cleanupMasquerade(subnet: string, outInterface: string, tunInterface: string): Promise<void>;
|
|
45
|
+
private exec;
|
|
46
|
+
/**
|
|
47
|
+
* Convert "10.86.0.0/16" -> 16
|
|
48
|
+
*/
|
|
49
|
+
private subnetToCidr;
|
|
50
|
+
/**
|
|
51
|
+
* Convert "10.86.0.0/16" -> "255.255.0.0"
|
|
52
|
+
*/
|
|
53
|
+
private subnetToNetmask;
|
|
54
|
+
/**
|
|
55
|
+
* Convert "10.86.0.0/16" -> "10.86.0.0" (network address)
|
|
56
|
+
*/
|
|
57
|
+
private subnetBase;
|
|
58
|
+
getCreatedInterface(): string | null;
|
|
59
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUN device + route management.
|
|
3
|
+
* Linux uses `ip` (iproute2). macOS uses `ifconfig` + `route`.
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { Logger } from "../utils/logger.js";
|
|
7
|
+
export class RouteManager {
|
|
8
|
+
logger;
|
|
9
|
+
createdInterface = null;
|
|
10
|
+
platform;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.logger = new Logger({ prefix: "RouteManager" });
|
|
13
|
+
if (process.platform === "darwin") {
|
|
14
|
+
this.platform = "darwin";
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
this.platform = "linux";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create and configure TUN interface (Linux only — on macOS the helper
|
|
22
|
+
* binary creates the utun device implicitly).
|
|
23
|
+
*/
|
|
24
|
+
async createTun(config) {
|
|
25
|
+
if (this.platform === "darwin") {
|
|
26
|
+
// utun is auto-created by the helper; just configure
|
|
27
|
+
await this.configureTun(config);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const { name } = config;
|
|
31
|
+
try {
|
|
32
|
+
this.logger.info(`Creating TUN interface: ${name}`);
|
|
33
|
+
this.exec(`ip tuntap add dev ${name} mode tun`);
|
|
34
|
+
this.createdInterface = name;
|
|
35
|
+
this.logger.info(`TUN device created: ${name}`);
|
|
36
|
+
await this.configureTun(config);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
this.logger.error(`Failed to create TUN: ${error}`);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Configure an existing TUN interface (assign IP, bring up, add route).
|
|
45
|
+
* `name` here should be the ACTUAL device name (e.g. utun12 on macOS).
|
|
46
|
+
*/
|
|
47
|
+
async configureTun(config) {
|
|
48
|
+
if (this.platform === "darwin") {
|
|
49
|
+
return this.configureTunDarwin(config);
|
|
50
|
+
}
|
|
51
|
+
return this.configureTunLinux(config);
|
|
52
|
+
}
|
|
53
|
+
async configureTunLinux(config) {
|
|
54
|
+
const { name, ip, subnet } = config;
|
|
55
|
+
const mtu = config.mtu ?? 900;
|
|
56
|
+
this.exec(`ip link set ${name} mtu ${mtu}`);
|
|
57
|
+
this.exec(`ip link set ${name} up`);
|
|
58
|
+
this.logger.info(`Interface ${name} brought up (mtu=${mtu})`);
|
|
59
|
+
const cidr = this.subnetToCidr(subnet);
|
|
60
|
+
try {
|
|
61
|
+
this.exec(`ip addr add ${ip}/${cidr} dev ${name}`);
|
|
62
|
+
this.logger.info(`Assigned IP ${ip}/${cidr} to ${name}`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const msg = String(error);
|
|
66
|
+
if (msg.includes("File exists")) {
|
|
67
|
+
this.logger.debug(`IP ${ip}/${cidr} already assigned`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
this.exec(`ip route add ${subnet} dev ${name}`);
|
|
75
|
+
this.logger.info(`Added route ${subnet} -> ${name}`);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const msg = String(error);
|
|
79
|
+
if (msg.includes("File exists") || msg.includes("RTNETLINK answers: File exists")) {
|
|
80
|
+
this.logger.debug(`Route ${subnet} already exists`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async configureTunDarwin(config) {
|
|
88
|
+
const { name, ip, subnet } = config;
|
|
89
|
+
const netmask = this.subnetToNetmask(subnet);
|
|
90
|
+
const subnetBase = this.subnetBase(subnet);
|
|
91
|
+
const mtu = config.mtu ?? 900;
|
|
92
|
+
// utun on macOS is point-to-point. Standard pattern (WireGuard/Tailscale):
|
|
93
|
+
// assign IP as /32 host address, then add an explicit subnet route.
|
|
94
|
+
this.exec(`ifconfig ${name} inet ${ip} ${ip} netmask 255.255.255.255 up`);
|
|
95
|
+
// macOS ifconfig doesn't accept `mtu` as part of the inet assignment;
|
|
96
|
+
// it must be a separate invocation.
|
|
97
|
+
this.exec(`ifconfig ${name} mtu ${mtu}`);
|
|
98
|
+
this.logger.info(`Assigned IP ${ip}/32 to ${name} (point-to-point, mtu=${mtu})`);
|
|
99
|
+
// Add subnet route via the TUN interface
|
|
100
|
+
try {
|
|
101
|
+
this.exec(`route -n add -net ${subnetBase} -netmask ${netmask} -interface ${name}`);
|
|
102
|
+
this.logger.info(`Added route ${subnet} -> ${name}`);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const msg = String(error);
|
|
106
|
+
if (msg.includes("File exists") || msg.includes("route already in table")) {
|
|
107
|
+
this.logger.debug(`Route ${subnet} already exists`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// macOS doesn't auto-insert a host route for the TUN's own IP
|
|
114
|
+
// through lo0 the way Linux does, so a self-issued packet to
|
|
115
|
+
// <my-tun-ip> goes OUT through the utun device, arrives at our
|
|
116
|
+
// packet-router, and gets dropped (the daemon doesn't friend
|
|
117
|
+
// itself).
|
|
118
|
+
//
|
|
119
|
+
// Two pieces are needed to actually fix self-ping on macOS:
|
|
120
|
+
// 1) Add the IP as a /32 alias on lo0 (so the kernel CAN
|
|
121
|
+
// deliver a packet for that IP locally).
|
|
122
|
+
// 2) Replace the auto-added utun host route with a lo0 host
|
|
123
|
+
// route — `ifconfig <utun> inet <ip> <ip> netmask
|
|
124
|
+
// 255.255.255.255 up` implicitly inserts `<ip>/32 -> utun`
|
|
125
|
+
// which beats the lo0 alias on specificity.
|
|
126
|
+
// Without (2), `route get <ip>` still says utun and self-pings
|
|
127
|
+
// time out. Same trick as Tailscale.
|
|
128
|
+
try {
|
|
129
|
+
this.exec(`ifconfig lo0 alias ${ip}/32`);
|
|
130
|
+
this.logger.debug(`Added lo0 alias for ${ip}`);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const msg = String(error);
|
|
134
|
+
if (!msg.includes("File exists") && !msg.includes("already in list")) {
|
|
135
|
+
this.logger.warn(`lo0 alias ${ip} failed (self-ping won't work): ${msg}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
// macOS `route -n change -host <ip> -interface lo0` is misleading:
|
|
140
|
+
// it sets the GATEWAY column to "lo0" but leaves the Netif column
|
|
141
|
+
// pointing at utun10 — packets still get sent out the utun. The
|
|
142
|
+
// pattern that actually flips Netif is to specify a gateway IP
|
|
143
|
+
// that already lives on lo0 (127.0.0.1). The kernel then picks
|
|
144
|
+
// lo0 as the egress interface from the gateway's address.
|
|
145
|
+
// Delete-then-add is more reliable than `change` for this since
|
|
146
|
+
// an existing route with the wrong Netif can't be rewritten in
|
|
147
|
+
// one shot on all macOS versions.
|
|
148
|
+
try {
|
|
149
|
+
this.exec(`route -n delete -host ${ip}`);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// route wasn't there, fine
|
|
153
|
+
}
|
|
154
|
+
this.exec(`route -n add -host ${ip} 127.0.0.1`);
|
|
155
|
+
this.logger.info(`Routed self-IP ${ip} via lo0 (self-traffic shortcut)`);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.logger.warn(`Could not redirect self-IP ${ip} to lo0 — self-pings will time out: ${error}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Remove TUN interface (Linux only — on macOS utun goes away when helper exits).
|
|
163
|
+
*/
|
|
164
|
+
async removeTun(name) {
|
|
165
|
+
if (this.platform === "darwin") {
|
|
166
|
+
// utun is auto-destroyed when the helper subprocess exits
|
|
167
|
+
this.logger.debug(`macOS utun ${name} cleaned up by helper exit`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
this.logger.info(`Removing TUN interface: ${name}`);
|
|
172
|
+
try {
|
|
173
|
+
this.exec(`ip link set ${name} down`);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// already down
|
|
177
|
+
}
|
|
178
|
+
this.exec(`ip tuntap del dev ${name} mode tun`);
|
|
179
|
+
this.createdInterface = null;
|
|
180
|
+
this.logger.info(`TUN device removed: ${name}`);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
this.logger.error(`Failed to remove TUN: ${error}`);
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Cleanup all routes and interface. Pass the daemon's actual TUN
|
|
189
|
+
* IP so we can also remove the macOS lo0 self-alias we added in
|
|
190
|
+
* configureTun; without that, the alias persists across daemon
|
|
191
|
+
* restarts (harmless, but leaks state).
|
|
192
|
+
*/
|
|
193
|
+
async cleanup(name, subnet, ip) {
|
|
194
|
+
try {
|
|
195
|
+
this.logger.info(`Cleaning up TUN and routes`);
|
|
196
|
+
if (this.platform === "darwin") {
|
|
197
|
+
const subnetBase = this.subnetBase(subnet);
|
|
198
|
+
const netmask = this.subnetToNetmask(subnet);
|
|
199
|
+
try {
|
|
200
|
+
this.exec(`route -n delete -net ${subnetBase} -netmask ${netmask}`);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// route might not exist
|
|
204
|
+
}
|
|
205
|
+
if (ip) {
|
|
206
|
+
try {
|
|
207
|
+
this.exec(`route -n delete -host ${ip}`);
|
|
208
|
+
this.logger.debug(`Removed host route ${ip}`);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// route wasn't there
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
this.exec(`ifconfig lo0 -alias ${ip}`);
|
|
215
|
+
this.logger.debug(`Removed lo0 alias ${ip}`);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// alias wasn't there
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// utun device cleanup is handled when helper exits
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
this.exec(`ip route del ${subnet} dev ${name}`);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Route might not exist
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
await this.removeTun(name);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// Interface might not exist
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
this.logger.error(`Cleanup error: ${error}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Enable IP forwarding (Linux only)
|
|
243
|
+
*/
|
|
244
|
+
async enableIpForwarding() {
|
|
245
|
+
if (this.platform === "darwin") {
|
|
246
|
+
try {
|
|
247
|
+
this.exec("sysctl -w net.inet.ip.forwarding=1");
|
|
248
|
+
this.logger.info("IP forwarding enabled (macOS)");
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this.logger.error(`Failed to enable IP forwarding: ${error}`);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
this.exec("sysctl -w net.ipv4.ip_forward=1");
|
|
258
|
+
this.logger.info("IP forwarding enabled");
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
this.logger.error(`Failed to enable IP forwarding: ${error}`);
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async disableIpForwarding() {
|
|
266
|
+
try {
|
|
267
|
+
if (this.platform === "darwin") {
|
|
268
|
+
this.exec("sysctl -w net.inet.ip.forwarding=0");
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.exec("sysctl -w net.ipv4.ip_forward=0");
|
|
272
|
+
}
|
|
273
|
+
this.logger.info("IP forwarding disabled");
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
this.logger.warn(`Failed to disable IP forwarding: ${error}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Setup NAT/masquerade — Linux iptables only (gateway mode).
|
|
281
|
+
* Not implemented on macOS in MVP.
|
|
282
|
+
*/
|
|
283
|
+
async setupMasquerade(subnet, outInterface, tunInterface) {
|
|
284
|
+
if (this.platform === "darwin") {
|
|
285
|
+
this.logger.warn("Masquerade not implemented on macOS in MVP");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.exec(`iptables -t nat -A POSTROUTING -o ${outInterface} -j MASQUERADE`);
|
|
289
|
+
this.exec(`iptables -A FORWARD -i ${tunInterface} -o ${outInterface} -j ACCEPT`);
|
|
290
|
+
this.exec(`iptables -A FORWARD -i ${outInterface} -o ${tunInterface} -m state --state RELATED,ESTABLISHED -j ACCEPT`);
|
|
291
|
+
this.logger.info(`Masquerade setup for ${subnet} (${tunInterface} -> ${outInterface})`);
|
|
292
|
+
}
|
|
293
|
+
async cleanupMasquerade(subnet, outInterface, tunInterface) {
|
|
294
|
+
if (this.platform === "darwin")
|
|
295
|
+
return;
|
|
296
|
+
for (const cmd of [
|
|
297
|
+
`iptables -t nat -D POSTROUTING -o ${outInterface} -j MASQUERADE`,
|
|
298
|
+
`iptables -D FORWARD -i ${tunInterface} -o ${outInterface} -j ACCEPT`,
|
|
299
|
+
`iptables -D FORWARD -i ${outInterface} -o ${tunInterface} -m state --state RELATED,ESTABLISHED -j ACCEPT`,
|
|
300
|
+
]) {
|
|
301
|
+
try {
|
|
302
|
+
this.exec(cmd);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// best effort
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
this.logger.info(`Masquerade cleanup for ${subnet}`);
|
|
309
|
+
}
|
|
310
|
+
exec(command) {
|
|
311
|
+
this.logger.debug(`Executing: ${command}`);
|
|
312
|
+
try {
|
|
313
|
+
return execSync(command, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
317
|
+
throw new Error(`Command failed: ${command}\n${message}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Convert "10.86.0.0/16" -> 16
|
|
322
|
+
*/
|
|
323
|
+
subnetToCidr(subnet) {
|
|
324
|
+
const parts = subnet.split("/");
|
|
325
|
+
if (parts.length === 2) {
|
|
326
|
+
return parseInt(parts[1], 10);
|
|
327
|
+
}
|
|
328
|
+
return 24;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Convert "10.86.0.0/16" -> "255.255.0.0"
|
|
332
|
+
*/
|
|
333
|
+
subnetToNetmask(subnet) {
|
|
334
|
+
const cidr = this.subnetToCidr(subnet);
|
|
335
|
+
const mask = (0xffffffff << (32 - cidr)) >>> 0;
|
|
336
|
+
return [
|
|
337
|
+
(mask >>> 24) & 0xff,
|
|
338
|
+
(mask >>> 16) & 0xff,
|
|
339
|
+
(mask >>> 8) & 0xff,
|
|
340
|
+
mask & 0xff,
|
|
341
|
+
].join(".");
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Convert "10.86.0.0/16" -> "10.86.0.0" (network address)
|
|
345
|
+
*/
|
|
346
|
+
subnetBase(subnet) {
|
|
347
|
+
const parts = subnet.split("/");
|
|
348
|
+
return parts[0];
|
|
349
|
+
}
|
|
350
|
+
getCreatedInterface() {
|
|
351
|
+
return this.createdInterface;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUN device abstraction for reading/writing IP packets
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* - mockMode (default): in-memory packet queue, used for tests
|
|
6
|
+
* - real mode: spawns a Go helper subprocess (bin/tun-helper-{platform})
|
|
7
|
+
* that owns the TUN device and pipes packets via stdin/stdout with
|
|
8
|
+
* 4-byte big-endian length-prefix framing.
|
|
9
|
+
*/
|
|
10
|
+
import { EventEmitter } from "events";
|
|
11
|
+
import type { TunDeviceConfig } from "./types.js";
|
|
12
|
+
export interface TunDeviceOptions {
|
|
13
|
+
config: TunDeviceConfig;
|
|
14
|
+
mockMode?: boolean;
|
|
15
|
+
helperPath?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class TunDevice extends EventEmitter {
|
|
18
|
+
private config;
|
|
19
|
+
private logger;
|
|
20
|
+
private mockMode;
|
|
21
|
+
private helperPath?;
|
|
22
|
+
private isOpen;
|
|
23
|
+
private packetQueue;
|
|
24
|
+
private readLoopActive;
|
|
25
|
+
private helper?;
|
|
26
|
+
private stdinBuffer;
|
|
27
|
+
private actualDeviceName?;
|
|
28
|
+
constructor(opts: TunDeviceOptions);
|
|
29
|
+
open(): Promise<void>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
write(packet: Uint8Array): Promise<void>;
|
|
32
|
+
read(timeoutMs?: number): Promise<Uint8Array>;
|
|
33
|
+
queuePacket(packet: Uint8Array): void;
|
|
34
|
+
startReadLoop(): Promise<void>;
|
|
35
|
+
stopReadLoop(): Promise<void>;
|
|
36
|
+
getConfig(): TunDeviceConfig;
|
|
37
|
+
isActive(): boolean;
|
|
38
|
+
getInterface(): string;
|
|
39
|
+
getVirtualIp(): string;
|
|
40
|
+
getSubnet(): string;
|
|
41
|
+
private openReal;
|
|
42
|
+
private waitForReady;
|
|
43
|
+
private handleStdoutData;
|
|
44
|
+
private findHelperBinary;
|
|
45
|
+
}
|