Skip to content

Commit 92cc740

Browse files
authored
fix: require confirmation of global unicast addresses (#2876)
Some routers will allocate global unicast addresses which peers will treat as publicly routable. The routers may also firewall any incoming traffic to these addresses so require confirmation that the node is reachable before publishing such addresses.
1 parent d19974d commit 92cc740

File tree

9 files changed

+313
-33
lines changed

9 files changed

+313
-33
lines changed

packages/interface-internal/src/address-manager/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export interface NodeAddress {
2929
*/
3030
verified: boolean
3131

32+
/**
33+
* The timestamp at which the address was last verified
34+
*/
35+
lastVerified?: number
36+
3237
/**
3338
* A millisecond timestamp after which this address should be reverified
3439
*/
@@ -40,6 +45,13 @@ export interface NodeAddress {
4045
type: AddressType
4146
}
4247

48+
export interface ConfirmAddressOptions {
49+
/**
50+
* Override the TTL of the observed address verification
51+
*/
52+
ttl?: number
53+
}
54+
4355
export interface AddressManager {
4456
/**
4557
* Get peer listen multiaddrs
@@ -61,7 +73,7 @@ export interface AddressManager {
6173
* Signal that we have confidence an observed multiaddr is publicly dialable -
6274
* this will make it appear in the output of getAddresses()
6375
*/
64-
confirmObservedAddr(addr: Multiaddr): void
76+
confirmObservedAddr(addr: Multiaddr, options?: ConfirmAddressOptions): void
6577

6678
/**
6779
* Signal that we do not have confidence an observed multiaddr is publicly dialable -

packages/libp2p/src/address-manager/dns-mappings.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import type { Logger } from '@libp2p/interface'
55
import type { NodeAddress } from '@libp2p/interface-internal'
66
import type { Multiaddr, StringTuple } from '@multiformats/multiaddr'
77

8+
const MAX_DATE = 8_640_000_000_000_000
9+
810
export const defaultValues = {
911
maxObservedAddresses: 10
1012
}
@@ -13,6 +15,7 @@ interface DNSMapping {
1315
domain: string
1416
verified: boolean
1517
expires: number
18+
lastVerified?: number
1619
}
1720

1821
const CODEC_TLS = 0x01c0
@@ -46,13 +49,15 @@ export class DNSMappings {
4649
add (domain: string, addresses: string[]): void {
4750
addresses.forEach(ip => {
4851
this.log('add DNS mapping %s to %s', ip, domain)
52+
// we are only confident if this is an local domain mapping, otherwise
53+
// we will require external validation
54+
const verified = isPrivateIp(ip) === true
4955

5056
this.mappings.set(ip, {
5157
domain,
52-
// we are only confident if this is an local domain mapping, otherwise
53-
// we will require external validation
54-
verified: isPrivateIp(ip) === true,
55-
expires: 0
58+
verified,
59+
expires: verified ? MAX_DATE - Date.now() : 0,
60+
lastVerified: verified ? MAX_DATE - Date.now() : undefined
5661
})
5762
})
5863
}
@@ -109,7 +114,8 @@ export class DNSMappings {
109114
}`),
110115
verified: mapping.verified,
111116
type: 'dns-mapping',
112-
expires: mapping.expires
117+
expires: mapping.expires,
118+
lastVerified: mapping.lastVerified
113119
})
114120
}
115121
}
@@ -139,12 +145,29 @@ export class DNSMappings {
139145
startingConfidence = mapping.verified
140146
mapping.verified = true
141147
mapping.expires = Date.now() + ttl
148+
mapping.lastVerified = Date.now()
142149
}
143150
}
144151

145152
return startingConfidence
146153
}
147154

155+
unconfirm (ma: Multiaddr, ttl: number): boolean {
156+
const host = this.findHost(ma)
157+
let wasConfident = false
158+
159+
for (const [ip, mapping] of this.mappings.entries()) {
160+
if (mapping.domain === host) {
161+
this.log('removing verification of %s to %s DNS mapping', ip, mapping.domain)
162+
wasConfident = wasConfident || mapping.verified
163+
mapping.verified = false
164+
mapping.expires = Date.now() + ttl
165+
}
166+
}
167+
168+
return wasConfident
169+
}
170+
148171
private findHost (ma: Multiaddr): string | undefined {
149172
for (const tuple of ma.stringTuples()) {
150173
if (tuple[0] === CODEC_SNI) {

packages/libp2p/src/address-manager/index.ts

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import { multiaddr } from '@multiformats/multiaddr'
77
import { DNSMappings } from './dns-mappings.js'
88
import { IPMappings } from './ip-mappings.js'
99
import { ObservedAddresses } from './observed-addresses.js'
10+
import { TransportAddresses } from './transport-addresses.js'
1011
import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget, PeerId, PeerStore } from '@libp2p/interface'
11-
import type { AddressManager as AddressManagerInterface, TransportManager, NodeAddress } from '@libp2p/interface-internal'
12+
import type { AddressManager as AddressManagerInterface, TransportManager, NodeAddress, ConfirmAddressOptions } from '@libp2p/interface-internal'
1213
import type { Filter } from '@libp2p/utils/filters'
1314
import type { Multiaddr } from '@multiformats/multiaddr'
1415

16+
const ONE_MINUTE = 60_000
17+
1518
export const defaultValues = {
1619
maxObservedAddresses: 10,
17-
observedAddressTTL: 60_000 * 10
20+
addressVerificationTTL: ONE_MINUTE * 10,
21+
addressVerificationRetry: ONE_MINUTE * 5
1822
}
1923

2024
export interface AddressManagerInit {
@@ -50,9 +54,25 @@ export interface AddressManagerInit {
5054
maxObservedAddresses?: number
5155

5256
/**
53-
* How long before each observed address should be reverified
57+
* How long before each public address should be reverified in ms.
58+
*
59+
* Requires `@libp2p/autonat` or some other verification method to be
60+
* configured.
61+
*
62+
* @default 600_000
63+
*/
64+
addressVerificationTTL?: number
65+
66+
/**
67+
* After a transport or mapped address has failed to verify, how long to wait
68+
* before retrying it in ms
69+
*
70+
* Requires `@libp2p/autonat` or some other verification method to be
71+
* configured.
72+
*
73+
* @default 300_000
5474
*/
55-
observedAddressTTL?: number
75+
addressVerificationRetry?: number
5676
}
5777

5878
export interface AddressManagerComponents {
@@ -103,8 +123,10 @@ export class AddressManager implements AddressManagerInterface {
103123
private readonly observed: ObservedAddresses
104124
private readonly dnsMappings: DNSMappings
105125
private readonly ipMappings: IPMappings
126+
private readonly transportAddresses: TransportAddresses
106127
private readonly observedAddressFilter: Filter
107-
private readonly observedAddressTTL: number
128+
private readonly addressVerificationTTL: number
129+
private readonly addressVerificationRetry: number
108130

109131
/**
110132
* Responsible for managing the peer addresses.
@@ -123,9 +145,11 @@ export class AddressManager implements AddressManagerInterface {
123145
this.observed = new ObservedAddresses(components, init)
124146
this.dnsMappings = new DNSMappings(components, init)
125147
this.ipMappings = new IPMappings(components, init)
148+
this.transportAddresses = new TransportAddresses(components, init)
126149
this.announceFilter = init.announceFilter ?? defaultAddressFilter
127150
this.observedAddressFilter = createScalableCuckooFilter(1024)
128-
this.observedAddressTTL = init.observedAddressTTL ?? defaultValues.observedAddressTTL
151+
this.addressVerificationTTL = init.addressVerificationTTL ?? defaultValues.addressVerificationTTL
152+
this.addressVerificationRetry = init.addressVerificationRetry ?? defaultValues.addressVerificationRetry
129153

130154
// this method gets called repeatedly on startup when transports start listening so
131155
// debounce it so we don't cause multiple self:peer:update events to be emitted
@@ -221,20 +245,24 @@ export class AddressManager implements AddressManagerInterface {
221245
this.observed.add(addr)
222246
}
223247

224-
confirmObservedAddr (addr: Multiaddr): void {
248+
confirmObservedAddr (addr: Multiaddr, options?: ConfirmAddressOptions): void {
225249
addr = stripPeerId(addr, this.components.peerId)
226250
let startingConfidence = true
227251

228252
if (this.observed.has(addr)) {
229-
startingConfidence = this.observed.confirm(addr, this.observedAddressTTL)
253+
startingConfidence = this.observed.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
254+
}
255+
256+
if (this.transportAddresses.has(addr)) {
257+
startingConfidence = this.transportAddresses.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
230258
}
231259

232260
if (this.dnsMappings.has(addr)) {
233-
startingConfidence = this.dnsMappings.confirm(addr, this.observedAddressTTL)
261+
startingConfidence = this.dnsMappings.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
234262
}
235263

236264
if (this.ipMappings.has(addr)) {
237-
startingConfidence = this.ipMappings.confirm(addr, this.observedAddressTTL)
265+
startingConfidence = this.ipMappings.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
238266
}
239267

240268
// only trigger the 'self:peer:update' event if our confidence in an address has changed
@@ -243,12 +271,31 @@ export class AddressManager implements AddressManagerInterface {
243271
}
244272
}
245273

246-
removeObservedAddr (addr: Multiaddr): void {
274+
removeObservedAddr (addr: Multiaddr, options?: ConfirmAddressOptions): void {
247275
addr = stripPeerId(addr, this.components.peerId)
248276

249-
this.observed.remove(addr)
250-
this.dnsMappings.remove(addr)
251-
this.ipMappings.remove(addr)
277+
let startingConfidence = false
278+
279+
if (this.observed.has(addr)) {
280+
startingConfidence = this.observed.remove(addr)
281+
}
282+
283+
if (this.transportAddresses.has(addr)) {
284+
startingConfidence = this.transportAddresses.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
285+
}
286+
287+
if (this.dnsMappings.has(addr)) {
288+
startingConfidence = this.dnsMappings.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
289+
}
290+
291+
if (this.ipMappings.has(addr)) {
292+
startingConfidence = this.ipMappings.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
293+
}
294+
295+
// only trigger the 'self:peer:update' event if our confidence in an address has changed
296+
if (startingConfidence) {
297+
this._updatePeerStoreAddresses()
298+
}
252299
}
253300

254301
getAddresses (): Multiaddr[] {
@@ -299,20 +346,17 @@ export class AddressManager implements AddressManagerInterface {
299346
multiaddr,
300347
verified: true,
301348
type: 'announce',
302-
expires: Date.now() + this.observedAddressTTL
349+
expires: Date.now() + this.addressVerificationTTL,
350+
lastVerified: Date.now()
303351
}))
304352
}
305353

306354
let addresses: NodeAddress[] = []
307355

308356
// add transport addresses
309357
addresses = addresses.concat(
310-
this.components.transportManager.getAddrs().map(multiaddr => ({
311-
multiaddr,
312-
verified: true,
313-
type: 'transport',
314-
expires: Date.now() + this.observedAddressTTL
315-
}))
358+
this.components.transportManager.getAddrs()
359+
.map(multiaddr => this.transportAddresses.get(multiaddr, this.addressVerificationTTL))
316360
)
317361

318362
// add append announce addresses
@@ -321,7 +365,8 @@ export class AddressManager implements AddressManagerInterface {
321365
multiaddr,
322366
verified: true,
323367
type: 'announce',
324-
expires: Date.now() + this.observedAddressTTL
368+
expires: Date.now() + this.addressVerificationTTL,
369+
lastVerified: Date.now()
325370
}))
326371
)
327372

packages/libp2p/src/address-manager/ip-mappings.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface PublicAddressMapping {
1818
protocol: 'tcp' | 'udp'
1919
verified: boolean
2020
expires: number
21+
lastVerified?: number
2122
}
2223

2324
const CODEC_IP4 = 0x04
@@ -134,7 +135,8 @@ export class IPMappings {
134135
}`),
135136
verified: mapping.verified,
136137
type: 'ip-mapping',
137-
expires: mapping.expires
138+
expires: mapping.expires,
139+
lastVerified: mapping.lastVerified
138140
})
139141
}
140142
}
@@ -155,10 +157,35 @@ export class IPMappings {
155157
startingConfidence = mapping.verified
156158
mapping.verified = true
157159
mapping.expires = Date.now() + ttl
160+
mapping.lastVerified = Date.now()
158161
}
159162
}
160163
}
161164

162165
return startingConfidence
163166
}
167+
168+
unconfirm (ma: Multiaddr, ttl: number): boolean {
169+
const tuples = ma.stringTuples()
170+
const host = tuples[0][1] ?? ''
171+
const protocol = tuples[1][0] === CODEC_TCP ? 'tcp' : 'udp'
172+
const port = parseInt(tuples[1][1] ?? '0')
173+
let wasConfident = false
174+
175+
for (const mappings of this.mappings.values()) {
176+
for (let i = 0; i < mappings.length; i++) {
177+
const mapping = mappings[i]
178+
179+
if (mapping.externalIp === host && mapping.externalPort === port && mapping.protocol === protocol) {
180+
this.log('removing verification of %s:%s to %s:%s %s IP mapping', mapping.externalIp, mapping.externalPort, host, port, protocol)
181+
182+
wasConfident = wasConfident || mapping.verified
183+
mapping.verified = false
184+
mapping.expires = Date.now() + ttl
185+
}
186+
}
187+
}
188+
189+
return wasConfident
190+
}
164191
}

packages/libp2p/src/address-manager/observed-addresses.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const defaultValues = {
1313
interface ObservedAddressMetadata {
1414
verified: boolean
1515
expires: number
16+
lastVerified?: number
1617
}
1718

1819
export class ObservedAddresses {
@@ -60,23 +61,30 @@ export class ObservedAddresses {
6061
multiaddr: multiaddr(ma),
6162
verified: metadata.verified,
6263
type: 'observed',
63-
expires: metadata.expires
64+
expires: metadata.expires,
65+
lastVerified: metadata.lastVerified
6466
}))
6567
}
6668

67-
remove (ma: Multiaddr): void {
69+
remove (ma: Multiaddr): boolean {
70+
const startingConfidence = this.addresses.get(ma.toString())?.verified ?? false
71+
6872
this.log('removing observed address %a', ma)
6973
this.addresses.delete(ma.toString())
74+
75+
return startingConfidence
7076
}
7177

7278
confirm (ma: Multiaddr, ttl: number): boolean {
7379
const addrString = ma.toString()
7480
const metadata = this.addresses.get(addrString) ?? {
7581
verified: false,
76-
expires: Date.now() + ttl
82+
expires: Date.now() + ttl,
83+
lastVerified: Date.now()
7784
}
7885
const startingConfidence = metadata.verified
7986
metadata.verified = true
87+
metadata.lastVerified = Date.now()
8088

8189
this.log('marking observed address %a as verified', addrString)
8290
this.addresses.set(addrString, metadata)

0 commit comments

Comments
 (0)