ipv6-bridge 1.0.0 → 2.1.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/README.md +206 -31
- package/docs/API.md +365 -0
- package/docs/ARCHITECTURE.md +218 -0
- package/docs/CHANGELOG.md +159 -0
- package/docs/CONTRIBUTING.md +88 -0
- package/docs/GUIDE.md +675 -0
- package/docs/ROADMAP.md +69 -0
- package/examples/basic-usage.js +31 -23
- package/examples/embedded-usage.js +100 -0
- package/examples/production-usage.js +106 -0
- package/package.json +6 -6
- package/src/agent.js +271 -0
- package/src/cache.js +108 -0
- package/src/cli.js +265 -48
- package/src/config.js +159 -24
- package/src/connect.js +142 -0
- package/src/detect.js +82 -43
- package/src/discovery.js +122 -0
- package/src/dns64.js +226 -44
- package/src/doctor.js +222 -0
- package/src/index.js +157 -62
- package/src/ipv6.js +232 -0
- package/src/logger.js +47 -0
- package/src/netmatch.js +122 -0
- package/src/proxy.js +419 -101
- package/src/socks5.js +283 -0
- package/src/stats.js +140 -0
package/docs/GUIDE.md
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
# IPv6 Bridge — User Guide
|
|
2
|
+
|
|
3
|
+
A practical guide: what this tool is for, when you need it, when you don't, and
|
|
4
|
+
how to use it in real situations.
|
|
5
|
+
|
|
6
|
+
If you only read one thing: run `npx ipv6-bridge doctor`. It tells you whether
|
|
7
|
+
you need this tool and what is wrong with your network if you do.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
1. [The problem this solves](#1-the-problem-this-solves)
|
|
14
|
+
2. [Do you need this?](#2-do-you-need-this)
|
|
15
|
+
3. [Install and first run](#3-install-and-first-run)
|
|
16
|
+
4. [Connecting your applications](#4-connecting-your-applications)
|
|
17
|
+
5. [Real-world scenarios](#5-real-world-scenarios)
|
|
18
|
+
6. [Checking that it actually works](#6-checking-that-it-actually-works)
|
|
19
|
+
7. [Using it from Node.js](#7-using-it-from-nodejs)
|
|
20
|
+
8. [Configuration recipes](#8-configuration-recipes)
|
|
21
|
+
9. [Troubleshooting](#9-troubleshooting)
|
|
22
|
+
10. [When *not* to use this](#10-when-not-to-use-this)
|
|
23
|
+
11. [Glossary](#11-glossary)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. The problem this solves
|
|
28
|
+
|
|
29
|
+
The internet ran out of IPv4 addresses. The replacement, IPv6, has been rolling
|
|
30
|
+
out for years — mobile carriers, cloud providers and some ISPs now hand out
|
|
31
|
+
IPv6-only connections.
|
|
32
|
+
|
|
33
|
+
The catch: a large share of the internet still has no IPv6 address at all. If
|
|
34
|
+
your network gives you only IPv6, those sites are simply unreachable. Your
|
|
35
|
+
browser resolves `example.com`, gets back an IPv4 address like `93.184.216.34`,
|
|
36
|
+
and has no way to send a packet to it.
|
|
37
|
+
|
|
38
|
+
The standard fix is a pair of technologies:
|
|
39
|
+
|
|
40
|
+
- **DNS64** invents an IPv6 address that contains the IPv4 address inside it.
|
|
41
|
+
`93.184.216.34` becomes `64:ff9b::5db8:d822`.
|
|
42
|
+
- **NAT64** is a gateway on your network that recognises those addresses,
|
|
43
|
+
unpacks the IPv4 address, and forwards the traffic.
|
|
44
|
+
|
|
45
|
+
**IPv6 Bridge does the DNS64 half, on your machine, in user space.** It runs a
|
|
46
|
+
local proxy that synthesizes those addresses and routes your traffic to them, so
|
|
47
|
+
your NAT64 gateway can do the rest.
|
|
48
|
+
|
|
49
|
+
### What it is not
|
|
50
|
+
|
|
51
|
+
It does **not** replace a NAT64 gateway. If your network operator does not run
|
|
52
|
+
one, no user-space tool can invent IPv4 connectivity out of nothing. `doctor`
|
|
53
|
+
tells you whether a gateway exists.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. Do you need this?
|
|
58
|
+
|
|
59
|
+
Run this first:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx ipv6-bridge doctor
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use this table to interpret the result:
|
|
66
|
+
|
|
67
|
+
| Your situation | Do you need IPv6 Bridge? |
|
|
68
|
+
|---|---|
|
|
69
|
+
| Normal home/office internet (IPv4 works) | **No.** Everything already works. |
|
|
70
|
+
| IPv6-only network, and your DNS already does DNS64 | **Probably not.** Your OS is already being handed synthesized addresses. |
|
|
71
|
+
| IPv6-only network, DNS does *not* do DNS64, but a NAT64 gateway exists | **Yes.** This is exactly the gap it fills. |
|
|
72
|
+
| IPv6-only network with no NAT64 gateway at all | **No** — nothing can help. Ask your operator. |
|
|
73
|
+
| You want to test how your app behaves on IPv6-only | **Yes**, with `FORCE_BRIDGE=1`. |
|
|
74
|
+
|
|
75
|
+
`ipv6-bridge start` applies this logic itself and exits quietly if it isn't
|
|
76
|
+
needed, so it is safe to run anywhere.
|
|
77
|
+
|
|
78
|
+
### How to tell you're on an IPv6-only network
|
|
79
|
+
|
|
80
|
+
Common signs:
|
|
81
|
+
|
|
82
|
+
- Some websites load and others time out, with no obvious pattern
|
|
83
|
+
- `ping6 google.com` works but `ping 93.184.216.34` does not
|
|
84
|
+
- Your IP settings show an address starting with `2` or `3` (e.g. `2406:...`)
|
|
85
|
+
and no IPv4 address other than a private `192.168.x.x` one
|
|
86
|
+
- Mobile tethering from certain carriers (T-Mobile US, Jio, and others operate
|
|
87
|
+
IPv6-only mobile cores)
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Install and first run
|
|
92
|
+
|
|
93
|
+
You need Node.js 18 or newer.
|
|
94
|
+
|
|
95
|
+
### Run it without installing
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx ipv6-bridge start
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Install globally
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g ipv6-bridge
|
|
105
|
+
ipv6-bridge start
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Add it to a project
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm install ipv6-bridge
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### What you'll see
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
$ ipv6-bridge start
|
|
118
|
+
|
|
119
|
+
IPv6 Bridge running on http://127.0.0.1:8080
|
|
120
|
+
Configure your browser/system proxy to 127.0.0.1:8080
|
|
121
|
+
NAT64 prefix: 64:ff9b::/96
|
|
122
|
+
Status: http://127.0.0.1:8080/status
|
|
123
|
+
PAC: http://127.0.0.1:8080/proxy.pac
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If instead it prints *"IPv6 bridge not needed"* and exits, that is the tool
|
|
127
|
+
working correctly — your network doesn't need it. To run it anyway (to test, or
|
|
128
|
+
to demo it):
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
FORCE_BRIDGE=1 ipv6-bridge start
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Stop it with `Ctrl+C`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 4. Connecting your applications
|
|
139
|
+
|
|
140
|
+
The bridge is a proxy. **Nothing routes through it automatically** — you have to
|
|
141
|
+
point applications at it. This is the single most common source of confusion.
|
|
142
|
+
|
|
143
|
+
### curl
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
curl -x http://127.0.0.1:8080 https://example.com
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Browsers — automatic (recommended)
|
|
150
|
+
|
|
151
|
+
Point your browser's "Automatic proxy configuration URL" at:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
http://127.0.0.1:8080/proxy.pac
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This routes external traffic through the bridge while leaving `localhost` and
|
|
158
|
+
your bypass list alone.
|
|
159
|
+
|
|
160
|
+
- **Firefox** — Settings → Network Settings → Automatic proxy configuration URL
|
|
161
|
+
- **Chrome/Edge** — uses the system proxy settings (below)
|
|
162
|
+
- **macOS** — System Settings → Network → Details → Proxies → Automatic Proxy Configuration
|
|
163
|
+
- **Windows** — Settings → Network & Internet → Proxy → Use setup script
|
|
164
|
+
|
|
165
|
+
### Browsers — manual
|
|
166
|
+
|
|
167
|
+
Set the HTTP and HTTPS proxy to `127.0.0.1` port `8080`.
|
|
168
|
+
|
|
169
|
+
### Node.js applications
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
export HTTP_PROXY=http://127.0.0.1:8080
|
|
173
|
+
export HTTPS_PROXY=http://127.0.0.1:8080
|
|
174
|
+
export NO_PROXY=localhost,127.0.0.1
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Note that Node's built-in `fetch`/`http` do **not** read these variables
|
|
178
|
+
automatically; most HTTP client libraries (axios, got, node-fetch with an agent)
|
|
179
|
+
do. `npm` reads them.
|
|
180
|
+
|
|
181
|
+
### npm, pip, apt and other package managers
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npm config set proxy http://127.0.0.1:8080
|
|
185
|
+
npm config set https-proxy http://127.0.0.1:8080
|
|
186
|
+
|
|
187
|
+
pip install --proxy http://127.0.0.1:8080 requests
|
|
188
|
+
|
|
189
|
+
# apt: /etc/apt/apt.conf.d/95proxy
|
|
190
|
+
Acquire::http::Proxy "http://127.0.0.1:8080";
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Docker
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
docker run \
|
|
197
|
+
-e HTTP_PROXY=http://host.docker.internal:8080 \
|
|
198
|
+
-e HTTPS_PROXY=http://host.docker.internal:8080 \
|
|
199
|
+
myimage
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Start the bridge with `IPV6_BRIDGE_HOST=0.0.0.0` so containers can reach it, and
|
|
203
|
+
add `IPV6_BRIDGE_ALLOW` to limit who can.
|
|
204
|
+
|
|
205
|
+
### ssh, git, databases — use SOCKS5
|
|
206
|
+
|
|
207
|
+
An HTTP proxy can only carry HTTP. For anything else, enable SOCKS5:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
IPV6_BRIDGE_SOCKS_PORT=1080 ipv6-bridge start
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Then:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# git over HTTPS
|
|
217
|
+
git config --global http.proxy socks5h://127.0.0.1:1080
|
|
218
|
+
|
|
219
|
+
# ssh
|
|
220
|
+
ssh -o ProxyCommand='nc -X 5 -x 127.0.0.1:1080 %h %p' user@host
|
|
221
|
+
|
|
222
|
+
# psql, redis-cli and others via a SOCKS-aware wrapper
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The `h` in `socks5h` matters: it makes the client send the *hostname* to the
|
|
226
|
+
proxy rather than resolving it locally, so the bridge performs DNS64 resolution.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 5. Real-world scenarios
|
|
231
|
+
|
|
232
|
+
### Scenario A — Developer on an IPv6-only mobile hotspot
|
|
233
|
+
|
|
234
|
+
*You are tethered to a carrier that assigns IPv6-only. `npm install` hangs and
|
|
235
|
+
half of GitHub is unreachable.*
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
npx ipv6-bridge doctor # confirm: IPv4 unreachable, IPv6 works, NAT64 present
|
|
239
|
+
npx ipv6-bridge start
|
|
240
|
+
|
|
241
|
+
npm config set proxy http://127.0.0.1:8080
|
|
242
|
+
npm config set https-proxy http://127.0.0.1:8080
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Undo when you're back on a normal network:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
npm config delete proxy && npm config delete https-proxy
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Scenario B — CI pipeline that must test IPv6-only behaviour
|
|
252
|
+
|
|
253
|
+
*You want to catch IPv6 bugs before your users do.*
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
// jest.globalSetup.js
|
|
257
|
+
const { start } = require('ipv6-bridge');
|
|
258
|
+
|
|
259
|
+
module.exports = async () => {
|
|
260
|
+
global.__BRIDGE__ = await start(8080, { force: true });
|
|
261
|
+
process.env.HTTP_PROXY = 'http://127.0.0.1:8080';
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
// jest.globalTeardown.js
|
|
267
|
+
const { stop } = require('ipv6-bridge');
|
|
268
|
+
module.exports = async () => { await stop(); };
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`stop()` tears down open tunnels, so the suite always exits cleanly.
|
|
272
|
+
|
|
273
|
+
### Scenario C — Small team behind one IPv6-only uplink
|
|
274
|
+
|
|
275
|
+
*Several machines on a LAN need IPv4 access through a single bridge host.*
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
IPV6_BRIDGE_HOST=0.0.0.0 \
|
|
279
|
+
IPV6_BRIDGE_AUTH=team:choose-a-real-secret \
|
|
280
|
+
IPV6_BRIDGE_ALLOW=192.168.1.0/24 \
|
|
281
|
+
IPV6_BRIDGE_SOCKS_PORT=1080 \
|
|
282
|
+
ipv6-bridge start
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Give colleagues the PAC URL `http://<bridge-host>:8080/proxy.pac`.
|
|
286
|
+
|
|
287
|
+
**Never** expose it without `IPV6_BRIDGE_AUTH` or `IPV6_BRIDGE_ALLOW` — an open
|
|
288
|
+
proxy will be found and abused, and the traffic will be attributed to you.
|
|
289
|
+
|
|
290
|
+
### Scenario D — Corporate network with internal services
|
|
291
|
+
|
|
292
|
+
*Internal hosts must be reached directly; everything else goes through NAT64.*
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
IPV6_BRIDGE_BYPASS='*.internal.company.com,10.0.0.0/8,192.168.0.0/16' \
|
|
296
|
+
ipv6-bridge start
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Bypassed hosts are connected to directly and counted separately in `/status`.
|
|
300
|
+
|
|
301
|
+
### Scenario E — Operator-assigned NAT64 prefix
|
|
302
|
+
|
|
303
|
+
*Your ISP uses its own prefix instead of the well-known `64:ff9b::/96`.*
|
|
304
|
+
|
|
305
|
+
Usually you don't need to do anything: the bridge discovers the prefix via
|
|
306
|
+
RFC 7050 at startup. `doctor` shows what it found. To pin it explicitly:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
NAT64_PREFIX=2001:db8:122:344::/64 ipv6-bridge start
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
All RFC 6052 prefix lengths are supported: `/32`, `/40`, `/48`, `/56`, `/64`, `/96`.
|
|
313
|
+
|
|
314
|
+
### Scenario F — Monitoring it in production
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
curl http://127.0.0.1:8080/healthz # liveness probe
|
|
318
|
+
curl http://127.0.0.1:8080/metrics # Prometheus scrape target
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Alert on `ipv6_bridge_translation_rate` dropping to `0` while
|
|
322
|
+
`ipv6_bridge_route_total{mode="direct_ipv4_fallback"}` climbs — that means
|
|
323
|
+
translation is failing and traffic is going out untranslated.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 6. Checking that it actually works
|
|
328
|
+
|
|
329
|
+
This is the question most proxies can't answer. Run:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
ipv6-bridge status
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
IPv6 Bridge on 127.0.0.1:8080
|
|
337
|
+
Uptime 143s
|
|
338
|
+
NAT64 prefix 64:ff9b::/96
|
|
339
|
+
|
|
340
|
+
HTTP requests 27
|
|
341
|
+
CONNECT tunnels 4
|
|
342
|
+
SOCKS5 sessions 0
|
|
343
|
+
Errors 0 (0 timeouts)
|
|
344
|
+
|
|
345
|
+
Routing
|
|
346
|
+
via NAT64 24
|
|
347
|
+
native IPv6 7
|
|
348
|
+
direct IPv4 0
|
|
349
|
+
untranslated fallback 0
|
|
350
|
+
|
|
351
|
+
DNS cache 12 entries, hit rate 0.71
|
|
352
|
+
|
|
353
|
+
Translating 100% of routed connections.
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
What the routing modes mean:
|
|
357
|
+
|
|
358
|
+
| Mode | Meaning |
|
|
359
|
+
|---|---|
|
|
360
|
+
| `via NAT64` | The bridge synthesized an IPv6 address and used it. **This is the bridge doing its job.** |
|
|
361
|
+
| `native IPv6` | The site already had an IPv6 address; no translation needed. |
|
|
362
|
+
| `direct IPv4` | A private/loopback target, connected to directly. Expected and correct. |
|
|
363
|
+
| `untranslated fallback` | DNS64 failed and traffic went out untranslated. **If this is climbing, something is wrong.** |
|
|
364
|
+
|
|
365
|
+
If you see the warning *"nothing has been translated through NAT64"*, the bridge
|
|
366
|
+
is passing your traffic through without doing anything. Run `doctor`.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 7. Using it from Node.js
|
|
371
|
+
|
|
372
|
+
There are two ways to use this from an application. Pick based on what you're
|
|
373
|
+
trying to do.
|
|
374
|
+
|
|
375
|
+
| | Embedded | Proxy |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| **Use when** | Your own app needs to reach IPv4-only hosts | Other programs on the machine need it |
|
|
378
|
+
| **Covers** | Only your app's outbound connections | Anything configured to use the proxy |
|
|
379
|
+
| **Setup** | Pass an agent; no system config | Run a server, point clients at it |
|
|
380
|
+
| **Production** | Recommended for services | Recommended for developer machines |
|
|
381
|
+
|
|
382
|
+
### Embedded: use the bridge inside your app
|
|
383
|
+
|
|
384
|
+
No proxy, no ports, no system configuration. Your app's outbound connections
|
|
385
|
+
gain DNS64 translation, address-family failover and connection pooling.
|
|
386
|
+
|
|
387
|
+
```javascript
|
|
388
|
+
const https = require('https');
|
|
389
|
+
const { createHttpsAgent } = require('ipv6-bridge');
|
|
390
|
+
|
|
391
|
+
const agent = createHttpsAgent();
|
|
392
|
+
|
|
393
|
+
https.get('https://some-ipv4-only-api.example', { agent }, (res) => {
|
|
394
|
+
console.log(res.statusCode);
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Each connection tries native IPv6 first, then a NAT64-synthesized address, then
|
|
399
|
+
direct IPv4 — so it works on dual-stack, IPv6-only and IPv4-only hosts alike.
|
|
400
|
+
|
|
401
|
+
**TLS is validated against the hostname you requested**, not the synthesized
|
|
402
|
+
address the connection travelled over. You never need to disable certificate
|
|
403
|
+
checking to make this work; if you find yourself wanting to, something is wrong.
|
|
404
|
+
|
|
405
|
+
#### With popular HTTP clients
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
const { createAgents } = require('ipv6-bridge');
|
|
409
|
+
const agents = createAgents();
|
|
410
|
+
|
|
411
|
+
// axios
|
|
412
|
+
const axios = require('axios');
|
|
413
|
+
const client = axios.create({
|
|
414
|
+
httpAgent: agents.http,
|
|
415
|
+
httpsAgent: agents.https,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// got
|
|
419
|
+
const got = require('got');
|
|
420
|
+
await got('https://example.com', { agent: { http: agents.http, https: agents.https } });
|
|
421
|
+
|
|
422
|
+
// node-fetch
|
|
423
|
+
const fetch = require('node-fetch');
|
|
424
|
+
await fetch('https://example.com', { agent: agents.https });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
#### With Node's global `fetch` (undici)
|
|
428
|
+
|
|
429
|
+
Node's `fetch` is powered by undici, which isn't a dependency of this package.
|
|
430
|
+
If your project already has it:
|
|
431
|
+
|
|
432
|
+
```javascript
|
|
433
|
+
const { Agent, setGlobalDispatcher } = require('undici');
|
|
434
|
+
const { createConnector } = require('ipv6-bridge');
|
|
435
|
+
|
|
436
|
+
setGlobalDispatcher(new Agent({ connect: createConnector() }));
|
|
437
|
+
|
|
438
|
+
// Every fetch() in the process now goes through the bridge.
|
|
439
|
+
await fetch('https://some-ipv4-only-api.example');
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
#### As a drop-in `lookup` function
|
|
443
|
+
|
|
444
|
+
Anything that accepts a `lookup` option — `net.connect`, `http.request`, many
|
|
445
|
+
database drivers — can use the bridge without an agent:
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
const net = require('net');
|
|
449
|
+
const { createLookup } = require('ipv6-bridge');
|
|
450
|
+
|
|
451
|
+
const socket = net.connect({
|
|
452
|
+
host: 'some-ipv4-only-host.example',
|
|
453
|
+
port: 5432,
|
|
454
|
+
lookup: createLookup(),
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
This is the lightest-touch integration, but it only changes address resolution —
|
|
459
|
+
you don't get the failover or pooling the agents provide.
|
|
460
|
+
|
|
461
|
+
#### Confirming it is doing something
|
|
462
|
+
|
|
463
|
+
```javascript
|
|
464
|
+
const { resolve, getStats } = require('ipv6-bridge');
|
|
465
|
+
|
|
466
|
+
// What route would be used, without connecting
|
|
467
|
+
await resolve('8.8.8.8');
|
|
468
|
+
// → [ { host: '64:ff9b::808:808', family: 6, mode: 'nat64' },
|
|
469
|
+
// { host: '8.8.8.8', family: 4, mode: 'direct-ipv4' } ]
|
|
470
|
+
|
|
471
|
+
// What actually happened
|
|
472
|
+
const stats = getStats();
|
|
473
|
+
stats.routes.nat64; // connections that were translated
|
|
474
|
+
stats.routes.directIpv4Fallback; // connections that silently could not be
|
|
475
|
+
stats.translationRate; // 0 means nothing is being translated
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Expose `getStats()` on an internal health endpoint and alert if
|
|
479
|
+
`translationRate` sits at `0` while `directIpv4Fallback` climbs.
|
|
480
|
+
|
|
481
|
+
#### What this does not do
|
|
482
|
+
|
|
483
|
+
It cannot create connectivity the host lacks. On a host with no IPv6 address
|
|
484
|
+
and no route, nothing here produces IPv6 reachability; on an IPv6-only host with
|
|
485
|
+
no upstream NAT64 gateway, IPv4 destinations stay unreachable. It makes your app
|
|
486
|
+
reach everything the host *can* reach, regardless of address family.
|
|
487
|
+
|
|
488
|
+
### Proxy: run a server for other programs
|
|
489
|
+
|
|
490
|
+
```javascript
|
|
491
|
+
const { start, stop } = require('ipv6-bridge');
|
|
492
|
+
|
|
493
|
+
// Returns the server, or null if the bridge isn't needed on this network
|
|
494
|
+
const server = await start(8080);
|
|
495
|
+
|
|
496
|
+
if (server) {
|
|
497
|
+
console.log(`Listening on port ${server.address().port}`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
await stop();
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
With options:
|
|
504
|
+
|
|
505
|
+
```javascript
|
|
506
|
+
const server = await start(8080, {
|
|
507
|
+
host: '127.0.0.1', // interface to bind (default: loopback)
|
|
508
|
+
force: true, // start even if detection says it isn't needed
|
|
509
|
+
discoverPrefix: true, // RFC 7050 prefix discovery (default: true)
|
|
510
|
+
socksPort: 1080, // also serve SOCKS5
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Notes:
|
|
515
|
+
|
|
516
|
+
- `start()` returns `null` rather than throwing when the bridge isn't needed —
|
|
517
|
+
check for it.
|
|
518
|
+
- Calling `start()` twice rejects with "already running".
|
|
519
|
+
- `stop()` always resolves, even with open CONNECT tunnels, so it is safe in
|
|
520
|
+
test teardown.
|
|
521
|
+
- Environment variables are read when the module first loads, so set them before
|
|
522
|
+
`require('ipv6-bridge')`.
|
|
523
|
+
|
|
524
|
+
See [API.md](API.md) for the complete reference.
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## 8. Configuration recipes
|
|
529
|
+
|
|
530
|
+
Every setting is an environment variable. The full list is in
|
|
531
|
+
`ipv6-bridge --help` and [API.md](API.md).
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
# Run on a different port
|
|
535
|
+
IPV6_BRIDGE_PORT=9090 ipv6-bridge start
|
|
536
|
+
|
|
537
|
+
# Debug what it is deciding, request by request
|
|
538
|
+
LOG_LEVEL=debug ipv6-bridge start
|
|
539
|
+
|
|
540
|
+
# Silence it entirely (library use)
|
|
541
|
+
LOG_LEVEL=silent ipv6-bridge start
|
|
542
|
+
|
|
543
|
+
# Slow/lossy network: allow longer per-address attempts
|
|
544
|
+
IPV6_CONNECT_ATTEMPT_TIMEOUT=8000 IPV6_CONN_TIMEOUT=30000 ipv6-bridge start
|
|
545
|
+
|
|
546
|
+
# Busy proxy: cache DNS longer and pool more sockets
|
|
547
|
+
IPV6_DNS_CACHE_TTL=120000 IPV6_MAX_SOCKETS_PER_HOST=256 ipv6-bridge start
|
|
548
|
+
|
|
549
|
+
# Restricted network where the default probes are blocked
|
|
550
|
+
IPV4_TEST_URL=http://example.com IPV6_TEST_URL=http://ipv6.example.com ipv6-bridge start
|
|
551
|
+
|
|
552
|
+
# Turn off the control endpoints
|
|
553
|
+
IPV6_BRIDGE_CONTROL=off ipv6-bridge start
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Invalid values are rejected at startup with an explanation rather than failing
|
|
557
|
+
mysteriously later:
|
|
558
|
+
|
|
559
|
+
```
|
|
560
|
+
$ NAT64_PREFIX=garbage ipv6-bridge start
|
|
561
|
+
Configuration error: Invalid NAT64_PREFIX: Invalid NAT64 prefix "garbage": not a valid IPv6 address
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## 9. Troubleshooting
|
|
567
|
+
|
|
568
|
+
**Start here:** `ipv6-bridge doctor`.
|
|
569
|
+
|
|
570
|
+
### "IPv6 bridge not needed" and it exits
|
|
571
|
+
|
|
572
|
+
Working as intended — IPv4 already works on this network. Use `FORCE_BRIDGE=1`
|
|
573
|
+
to run it anyway.
|
|
574
|
+
|
|
575
|
+
### Everything returns 502 Bad Gateway
|
|
576
|
+
|
|
577
|
+
The bridge reached your target and failed. Usually one of:
|
|
578
|
+
|
|
579
|
+
- No NAT64 gateway on this network (`doctor` says so)
|
|
580
|
+
- Wrong NAT64 prefix (`doctor` reports what the network advertises)
|
|
581
|
+
- The destination is genuinely down
|
|
582
|
+
|
|
583
|
+
### Requests work but `status` shows nothing translated
|
|
584
|
+
|
|
585
|
+
Your traffic is bypassing the bridge's purpose — it is connecting directly. On a
|
|
586
|
+
dual-stack network with `FORCE_BRIDGE=1`, this is expected. On an IPv6-only
|
|
587
|
+
network it means DNS64 is failing; run `doctor`.
|
|
588
|
+
|
|
589
|
+
### It's slow, a few seconds per request
|
|
590
|
+
|
|
591
|
+
Each candidate address gets `IPV6_CONNECT_ATTEMPT_TIMEOUT` (3s) before the next
|
|
592
|
+
is tried. If the NAT64 route is unreachable, every new connection waits for that
|
|
593
|
+
timeout before falling back. Either fix the NAT64 route or stop forcing the
|
|
594
|
+
bridge on a network that doesn't need it.
|
|
595
|
+
|
|
596
|
+
### `dns.resolve` errors in doctor, but everything works
|
|
597
|
+
|
|
598
|
+
Normal on machines using DNS-over-HTTPS. The bridge uses the system resolver,
|
|
599
|
+
which works. `doctor` labels this as informational.
|
|
600
|
+
|
|
601
|
+
### My browser isn't using the proxy
|
|
602
|
+
|
|
603
|
+
Check that you configured it — nothing routes automatically. Verify with:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
curl -x http://127.0.0.1:8080 http://example.com
|
|
607
|
+
ipv6-bridge status # the counter should have gone up
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Port already in use
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
IPV6_BRIDGE_PORT=9090 ipv6-bridge start
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
To find the culprit: `lsof -i :8080` (macOS/Linux) or
|
|
617
|
+
`netstat -ano | findstr 8080` (Windows).
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## 10. When *not* to use this
|
|
622
|
+
|
|
623
|
+
Be honest about the boundaries:
|
|
624
|
+
|
|
625
|
+
- **You have working IPv4.** You gain nothing and add a hop. The tool refuses to
|
|
626
|
+
start for exactly this reason.
|
|
627
|
+
- **Your network has no NAT64 gateway.** Nothing in user space can fix that.
|
|
628
|
+
- **You need transparent, system-wide interception.** This is a proxy;
|
|
629
|
+
applications must be pointed at it. For whole-system translation you want
|
|
630
|
+
something kernel-level such as Jool or Tayga, or `clatd` for 464XLAT.
|
|
631
|
+
- **You need UDP.** SOCKS5 `UDP ASSOCIATE` is not implemented, so QUIC and plain
|
|
632
|
+
DNS won't go through it.
|
|
633
|
+
- **You're deploying it as a public internet proxy.** It has no rate limiting or
|
|
634
|
+
abuse controls. An open proxy will be found and abused within hours.
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## 11. Glossary
|
|
639
|
+
|
|
640
|
+
**IPv4** — the original internet addressing scheme (`93.184.216.34`). Exhausted.
|
|
641
|
+
|
|
642
|
+
**IPv6** — its replacement (`2606:2800:220:1:248:1893:25c8:1946`). Vastly larger.
|
|
643
|
+
|
|
644
|
+
**Dual-stack** — a network with both. What most people have; nothing to fix.
|
|
645
|
+
|
|
646
|
+
**IPv6-only** — a network with no IPv4 at all, where IPv4-only sites are
|
|
647
|
+
unreachable without translation.
|
|
648
|
+
|
|
649
|
+
**NAT64** — a gateway that translates IPv6 packets to IPv4 and back. Run by your
|
|
650
|
+
network operator; this tool cannot replace it.
|
|
651
|
+
|
|
652
|
+
**DNS64** — inventing an IPv6 address that encodes an IPv4 address inside it, so
|
|
653
|
+
traffic can be routed to a NAT64 gateway. This is what IPv6 Bridge does.
|
|
654
|
+
|
|
655
|
+
**NAT64 prefix** — the IPv6 range used for those synthesized addresses. The
|
|
656
|
+
well-known one is `64:ff9b::/96`; operators often assign their own.
|
|
657
|
+
|
|
658
|
+
**Forward proxy** — a server your applications send requests *through*. That is
|
|
659
|
+
what IPv6 Bridge is, which is why applications must be configured to use it.
|
|
660
|
+
|
|
661
|
+
**PAC file** — a small script that tells a browser which proxy to use for which
|
|
662
|
+
destination. Served at `/proxy.pac`.
|
|
663
|
+
|
|
664
|
+
**SOCKS5** — a protocol-agnostic proxy standard. Carries any TCP connection,
|
|
665
|
+
which is why it works for ssh and git where an HTTP proxy cannot.
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
## Further reading
|
|
670
|
+
|
|
671
|
+
- [README.md](../README.md) — overview and quick start
|
|
672
|
+
- [API.md](API.md) — programmatic API and every configuration value
|
|
673
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md) — how it works internally, and why
|
|
674
|
+
- [ROADMAP.md](ROADMAP.md) — what's shipped and what's planned
|
|
675
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — how to work on it
|