Skip to content

Commit 9700b5b

Browse files
cmd/devp2p: support dual-stack discovery listener (#35220)
`devp2p discv4 listen` / `discv5 listen` is the supported replacement for the removed bootnode tool, but it bound IPv4-only and `-extaddr` took a single address, so it couldn't run a dual-stack bootnode. This binds the listener dual-stack (falling back to IPv4-only where IPv6 is unavailable) and lets `-extaddr` take a comma-separated IPv4/IPv6 pair. A single node can then advertise both `ip` and `ip6` in its ENR over one UDP port: ``` devp2p discv4 listen --nodekey <key> --addr [::]:30301 \ --extaddr 203.0.113.10:30301,[2001:db8::1]:30301 ``` The fallback IP is only derived from the listener when no `-extaddr` is given, so a v4- or v6-only `-extaddr` no longer leaks a loopback entry. All addresses must share one UDP port (single socket).
1 parent a0568b1 commit 9700b5b

1 file changed

Lines changed: 42 additions & 18 deletions

File tree

cmd/devp2p/discv4cmd.go

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ var (
126126
}
127127
extAddrFlag = &cli.StringFlag{
128128
Name: "extaddr",
129-
Usage: "UDP endpoint announced in ENR. You can provide a bare IP address or IP:port as the value of this flag.",
129+
Usage: "UDP endpoint announced in ENR. You can provide a bare IP address or IP:port as the value of this flag. Provide a comma-separated pair to announce both an IPv4 and an IPv6 endpoint.",
130130
}
131131
crawlTimeoutFlag = &cli.DurationFlag{
132132
Name: "timeout",
@@ -344,36 +344,60 @@ func parseExtAddr(spec string) (ip net.IP, port int, ok bool) {
344344

345345
func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
346346
addr := ctx.String(listenAddrFlag.Name)
347+
extAddr := ctx.String(extAddrFlag.Name)
348+
var (
349+
socket net.PacketConn
350+
err error
351+
)
347352
if addr == "" {
348-
addr = "0.0.0.0:0"
353+
// Dual-stack socket, falling back to IPv4-only where IPv6 is unavailable.
354+
if socket, err = net.ListenPacket("udp", "[::]:0"); err != nil {
355+
socket, err = net.ListenPacket("udp", "0.0.0.0:0")
356+
}
357+
} else {
358+
socket, err = net.ListenPacket("udp", addr)
349359
}
350-
socket, err := net.ListenPacket("udp4", addr)
351360
if err != nil {
352361
exit(err)
353362
}
354363

355-
// Configure UDP endpoint in ENR from listener address.
364+
// Configure the ENR endpoint from the listener address, but only without an
365+
// explicit -extaddr: otherwise we'd announce a fallback IP for an address
366+
// family the user didn't specify (e.g. loopback IPv4 on an IPv6-only node).
356367
usocket := socket.(*net.UDPConn)
357368
uaddr := socket.LocalAddr().(*net.UDPAddr)
358-
if uaddr.IP.IsUnspecified() {
359-
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
360-
} else {
361-
ln.SetFallbackIP(uaddr.IP)
369+
if extAddr == "" {
370+
if uaddr.IP.IsUnspecified() {
371+
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
372+
} else {
373+
ln.SetFallbackIP(uaddr.IP)
374+
}
362375
}
363376
ln.SetFallbackUDP(uaddr.Port)
364377

365-
// If an ENR endpoint is set explicitly on the command-line, override
366-
// the information from the listening address. Note this is careful not
367-
// to set the UDP port if the external address doesn't have it.
368-
extAddr := ctx.String(extAddrFlag.Name)
378+
// Override with explicit -extaddr address(es). A static IP is set per family,
379+
// and all specs share one UDP port because the node has a single socket.
369380
if extAddr != "" {
370-
ip, port, ok := parseExtAddr(extAddr)
371-
if !ok {
372-
exit(fmt.Errorf("-%s: invalid external address %q", extAddrFlag.Name, extAddr))
381+
var extPort int
382+
for spec := range strings.SplitSeq(extAddr, ",") {
383+
spec = strings.TrimSpace(spec)
384+
if spec == "" {
385+
continue
386+
}
387+
ip, port, ok := parseExtAddr(spec)
388+
if !ok {
389+
exit(fmt.Errorf("-%s: invalid external address %q", extAddrFlag.Name, spec))
390+
}
391+
ln.SetStaticIP(ip)
392+
if port != 0 {
393+
if extPort != 0 && port != extPort {
394+
exit(fmt.Errorf("-%s: all addresses must announce the same UDP port, got %d and %d", extAddrFlag.Name, extPort, port))
395+
}
396+
extPort = port
397+
}
373398
}
374-
ln.SetStaticIP(ip)
375-
if port != 0 {
376-
ln.SetFallbackUDP(port)
399+
if extPort != 0 {
400+
ln.SetFallbackUDP(extPort)
377401
}
378402
}
379403

0 commit comments

Comments
 (0)