Skip to content

Commit c0b26f8

Browse files
fjlstwiname
authored andcommitted
p2p: use netip.Addr where possible (ethereum#29891)
enode.Node was recently changed to store a cache of endpoint information. The IP address in the cache is a netip.Addr. I chose that type over net.IP because it is just better. netip.Addr is meant to be used as a value type. Copying it does not allocate, it can be compared with ==, and can be used as a map key. This PR changes most uses of Node.IP() into Node.IPAddr(), which returns the cached value directly without allocating. While there are still some public APIs left where net.IP is used, I have converted all code used internally by p2p/discover to the new types. So this does change some public Go API, but hopefully not APIs any external code actually uses. There weren't supposed to be any semantic differences resulting from this refactoring, however it does introduce one: In package p2p/netutil we treated the 0.0.0.0/8 network (addresses 0.x.y.z) as LAN, but netip.Addr.IsPrivate() doesn't. The treatment of this particular IP address range is controversial, with some software supporting it and others not. IANA lists it as special-purpose and invalid as a destination for a long time, so I don't know why I put it into the LAN list. It has now been marked as special in p2p/netutil as well.
1 parent 9d6ed30 commit c0b26f8

23 files changed

+392
-313
lines changed

cmd/devp2p/internal/ethtest/conn.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ func (s *Suite) dial() (*Conn, error) {
5353
// dialAs attempts to dial a given node and perform a handshake using the given
5454
// private key.
5555
func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
56-
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
56+
tcpEndpoint, _ := s.Dest.TCPEndpoint()
57+
fd, err := net.Dial("tcp", tcpEndpoint.String())
5758
if err != nil {
5859
return nil, err
5960
}

cmd/devp2p/internal/v4test/framework.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ func newTestEnv(remote string, listen1, listen2 string) *testenv {
5353
if err != nil {
5454
panic(err)
5555
}
56-
if node.IP() == nil || node.UDP() == 0 {
56+
if !node.IPAddr().IsValid() || node.UDP() == 0 {
5757
var ip net.IP
5858
var tcpPort, udpPort int
59-
if ip = node.IP(); ip == nil {
59+
if node.IPAddr().IsValid() {
60+
ip = node.IPAddr().AsSlice()
61+
} else {
6062
ip = net.ParseIP("127.0.0.1")
6163
}
6264
if tcpPort = node.TCP(); tcpPort == 0 {

cmd/devp2p/nodesetcmd.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package main
1919
import (
2020
"errors"
2121
"fmt"
22-
"net"
22+
"net/netip"
2323
"sort"
2424
"strconv"
2525
"strings"
@@ -205,11 +205,11 @@ func trueFilter(args []string) (nodeFilter, error) {
205205
}
206206

207207
func ipFilter(args []string) (nodeFilter, error) {
208-
_, cidr, err := net.ParseCIDR(args[0])
208+
prefix, err := netip.ParsePrefix(args[0])
209209
if err != nil {
210210
return nil, err
211211
}
212-
f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) }
212+
f := func(n nodeJSON) bool { return prefix.Contains(n.N.IPAddr()) }
213213
return f, nil
214214
}
215215

cmd/devp2p/rlpxcmd.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ var (
7777

7878
func rlpxPing(ctx *cli.Context) error {
7979
n := getNodeArg(ctx)
80-
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
80+
tcpEndpoint, ok := n.TCPEndpoint()
81+
if !ok {
82+
return fmt.Errorf("node has no TCP endpoint")
83+
}
84+
fd, err := net.Dial("tcp", tcpEndpoint.String())
8185
if err != nil {
8286
return err
8387
}

p2p/dial.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,8 @@ type tcpDialer struct {
6565
}
6666

6767
func (t tcpDialer) Dial(ctx context.Context, dest *enode.Node) (net.Conn, error) {
68-
return t.d.DialContext(ctx, "tcp", nodeAddr(dest).String())
69-
}
70-
71-
func nodeAddr(n *enode.Node) net.Addr {
72-
return &net.TCPAddr{IP: n.IP(), Port: n.TCP()}
68+
addr, _ := dest.TCPEndpoint()
69+
return t.d.DialContext(ctx, "tcp", addr.String())
7370
}
7471

7572
// checkDial errors:
@@ -243,7 +240,7 @@ loop:
243240
select {
244241
case node := <-nodesCh:
245242
if err := d.checkDial(node); err != nil {
246-
d.log.Trace("Discarding dial candidate", "id", node.ID(), "ip", node.IP(), "reason", err)
243+
d.log.Trace("Discarding dial candidate", "id", node.ID(), "ip", node.IPAddr(), "reason", err)
247244
} else {
248245
d.startDial(newDialTask(node, dynDialedConn))
249246
}
@@ -277,7 +274,7 @@ loop:
277274
case node := <-d.addStaticCh:
278275
id := node.ID()
279276
_, exists := d.static[id]
280-
d.log.Trace("Adding static node", "id", id, "ip", node.IP(), "added", !exists)
277+
d.log.Trace("Adding static node", "id", id, "ip", node.IPAddr(), "added", !exists)
281278
if exists {
282279
continue loop
283280
}
@@ -376,7 +373,7 @@ func (d *dialScheduler) checkDial(n *enode.Node) error {
376373
if n.ID() == d.self {
377374
return errSelf
378375
}
379-
if n.IP() != nil && n.TCP() == 0 {
376+
if n.IPAddr().IsValid() && n.TCP() == 0 {
380377
// This check can trigger if a non-TCP node is found
381378
// by discovery. If there is no IP, the node is a static
382379
// node and the actual endpoint will be resolved later in dialTask.
@@ -388,7 +385,7 @@ func (d *dialScheduler) checkDial(n *enode.Node) error {
388385
if _, ok := d.peers[n.ID()]; ok {
389386
return errAlreadyConnected
390387
}
391-
if d.netRestrict != nil && !d.netRestrict.Contains(n.IP()) {
388+
if d.netRestrict != nil && !d.netRestrict.ContainsAddr(n.IPAddr()) {
392389
return errNetRestrict
393390
}
394391
if d.history.contains(string(n.ID().Bytes())) {
@@ -439,7 +436,7 @@ func (d *dialScheduler) removeFromStaticPool(idx int) {
439436
// startDial runs the given dial task in a separate goroutine.
440437
func (d *dialScheduler) startDial(task *dialTask) {
441438
node := task.dest()
442-
d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IP(), "flag", task.flags)
439+
d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IPAddr(), "flag", task.flags)
443440
hkey := string(node.ID().Bytes())
444441
d.history.add(hkey, d.clock.Now().Add(dialHistoryExpiration))
445442
d.dialing[node.ID()] = task
@@ -492,7 +489,7 @@ func (t *dialTask) run(d *dialScheduler) {
492489
}
493490

494491
func (t *dialTask) needResolve() bool {
495-
return t.flags&staticDialedConn != 0 && t.dest().IP() == nil
492+
return t.flags&staticDialedConn != 0 && !t.dest().IPAddr().IsValid()
496493
}
497494

498495
// resolve attempts to find the current endpoint for the destination
@@ -526,7 +523,8 @@ func (t *dialTask) resolve(d *dialScheduler) bool {
526523
// The node was found.
527524
t.resolveDelay = initialResolveDelay
528525
t.destPtr.Store(resolved)
529-
d.log.Debug("Resolved node", "id", resolved.ID(), "addr", &net.TCPAddr{IP: resolved.IP(), Port: resolved.TCP()})
526+
resAddr, _ := resolved.TCPEndpoint()
527+
d.log.Debug("Resolved node", "id", resolved.ID(), "addr", resAddr)
530528
return true
531529
}
532530

@@ -535,7 +533,8 @@ func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error {
535533
dialMeter.Mark(1)
536534
fd, err := d.dialer.Dial(d.ctx, dest)
537535
if err != nil {
538-
d.log.Trace("Dial error", "id", dest.ID(), "addr", nodeAddr(dest), "conn", t.flags, "err", cleanupDialErr(err))
536+
addr, _ := dest.TCPEndpoint()
537+
d.log.Trace("Dial error", "id", dest.ID(), "addr", addr, "conn", t.flags, "err", cleanupDialErr(err))
539538
dialConnectionError.Mark(1)
540539
return &dialError{err}
541540
}
@@ -545,7 +544,7 @@ func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error {
545544
func (t *dialTask) String() string {
546545
node := t.dest()
547546
id := node.ID()
548-
return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], node.IP(), node.TCP())
547+
return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], node.IPAddr(), node.TCP())
549548
}
550549

551550
func cleanupDialErr(err error) error {

p2p/discover/table.go

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ package discover
2525
import (
2626
"context"
2727
"fmt"
28-
"net"
28+
"net/netip"
2929
"slices"
3030
"sync"
3131
"time"
@@ -207,8 +207,8 @@ func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
207207
if err := n.ValidateComplete(); err != nil {
208208
return fmt.Errorf("bad bootstrap node %q: %v", n, err)
209209
}
210-
if tab.cfg.NetRestrict != nil && !tab.cfg.NetRestrict.Contains(n.IP()) {
211-
tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IP())
210+
if tab.cfg.NetRestrict != nil && !tab.cfg.NetRestrict.ContainsAddr(n.IPAddr()) {
211+
tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IPAddr())
212212
continue
213213
}
214214
nursery = append(nursery, n)
@@ -448,7 +448,7 @@ func (tab *Table) loadSeedNodes() {
448448
for i := range seeds {
449449
seed := seeds[i]
450450
if tab.log.Enabled(context.Background(), log.LevelTrace) {
451-
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
451+
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IPAddr()))
452452
addr, _ := seed.UDPEndpoint()
453453
tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age)
454454
}
@@ -474,31 +474,31 @@ func (tab *Table) bucketAtDistance(d int) *bucket {
474474
return tab.buckets[d-bucketMinDistance-1]
475475
}
476476

477-
func (tab *Table) addIP(b *bucket, ip net.IP) bool {
478-
if len(ip) == 0 {
477+
func (tab *Table) addIP(b *bucket, ip netip.Addr) bool {
478+
if !ip.IsValid() || ip.IsUnspecified() {
479479
return false // Nodes without IP cannot be added.
480480
}
481-
if netutil.IsLAN(ip) {
481+
if netutil.AddrIsLAN(ip) {
482482
return true
483483
}
484-
if !tab.ips.Add(ip) {
484+
if !tab.ips.AddAddr(ip) {
485485
tab.log.Debug("IP exceeds table limit", "ip", ip)
486486
return false
487487
}
488-
if !b.ips.Add(ip) {
488+
if !b.ips.AddAddr(ip) {
489489
tab.log.Debug("IP exceeds bucket limit", "ip", ip)
490-
tab.ips.Remove(ip)
490+
tab.ips.RemoveAddr(ip)
491491
return false
492492
}
493493
return true
494494
}
495495

496-
func (tab *Table) removeIP(b *bucket, ip net.IP) {
497-
if netutil.IsLAN(ip) {
496+
func (tab *Table) removeIP(b *bucket, ip netip.Addr) {
497+
if netutil.AddrIsLAN(ip) {
498498
return
499499
}
500-
tab.ips.Remove(ip)
501-
b.ips.Remove(ip)
500+
tab.ips.RemoveAddr(ip)
501+
b.ips.RemoveAddr(ip)
502502
}
503503

504504
// handleAddNode adds the node in the request to the table, if there is space.
@@ -524,7 +524,7 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
524524
tab.addReplacement(b, req.node)
525525
return false
526526
}
527-
if !tab.addIP(b, req.node.IP()) {
527+
if !tab.addIP(b, req.node.IPAddr()) {
528528
// Can't add: IP limit reached.
529529
return false
530530
}
@@ -547,15 +547,15 @@ func (tab *Table) addReplacement(b *bucket, n *enode.Node) {
547547
// TODO: update ENR
548548
return
549549
}
550-
if !tab.addIP(b, n.IP()) {
550+
if !tab.addIP(b, n.IPAddr()) {
551551
return
552552
}
553553

554554
wn := &tableNode{Node: n, addedToTable: time.Now()}
555555
var removed *tableNode
556556
b.replacements, removed = pushNode(b.replacements, wn, maxReplacements)
557557
if removed != nil {
558-
tab.removeIP(b, removed.IP())
558+
tab.removeIP(b, removed.IPAddr())
559559
}
560560
}
561561

@@ -595,20 +595,20 @@ func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *tableNode {
595595
// Remove the node.
596596
n := b.entries[index]
597597
b.entries = slices.Delete(b.entries, index, index+1)
598-
tab.removeIP(b, n.IP())
598+
tab.removeIP(b, n.IPAddr())
599599
tab.nodeRemoved(b, n)
600600

601601
// Add replacement.
602602
if len(b.replacements) == 0 {
603-
tab.log.Debug("Removed dead node", "b", b.index, "id", n.ID(), "ip", n.IP())
603+
tab.log.Debug("Removed dead node", "b", b.index, "id", n.ID(), "ip", n.IPAddr())
604604
return nil
605605
}
606606
rindex := tab.rand.Intn(len(b.replacements))
607607
rep := b.replacements[rindex]
608608
b.replacements = slices.Delete(b.replacements, rindex, rindex+1)
609609
b.entries = append(b.entries, rep)
610610
tab.nodeAdded(b, rep)
611-
tab.log.Debug("Replaced dead node", "b", b.index, "id", n.ID(), "ip", n.IP(), "r", rep.ID(), "rip", rep.IP())
611+
tab.log.Debug("Replaced dead node", "b", b.index, "id", n.ID(), "ip", n.IPAddr(), "r", rep.ID(), "rip", rep.IPAddr())
612612
return rep
613613
}
614614

@@ -635,10 +635,10 @@ func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node, isInbound bool)
635635
ipchanged := newRecord.IPAddr() != n.IPAddr()
636636
portchanged := newRecord.UDP() != n.UDP()
637637
if ipchanged {
638-
tab.removeIP(b, n.IP())
639-
if !tab.addIP(b, newRecord.IP()) {
638+
tab.removeIP(b, n.IPAddr())
639+
if !tab.addIP(b, newRecord.IPAddr()) {
640640
// It doesn't fit with the limit, put the previous record back.
641-
tab.addIP(b, n.IP())
641+
tab.addIP(b, n.IPAddr())
642642
return n, false
643643
}
644644
}
@@ -657,11 +657,11 @@ func (tab *Table) handleTrackRequest(op trackRequestOp) {
657657
var fails int
658658
if op.success {
659659
// Reset failure counter because it counts _consecutive_ failures.
660-
tab.db.UpdateFindFails(op.node.ID(), op.node.IP(), 0)
660+
tab.db.UpdateFindFails(op.node.ID(), op.node.IPAddr(), 0)
661661
} else {
662-
fails = tab.db.FindFails(op.node.ID(), op.node.IP())
662+
fails = tab.db.FindFails(op.node.ID(), op.node.IPAddr())
663663
fails++
664-
tab.db.UpdateFindFails(op.node.ID(), op.node.IP(), fails)
664+
tab.db.UpdateFindFails(op.node.ID(), op.node.IPAddr(), fails)
665665
}
666666

667667
tab.mutex.Lock()

p2p/discover/table_test.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func checkIPLimitInvariant(t *testing.T, tab *Table) {
188188
tabset := netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}
189189
for _, b := range tab.buckets {
190190
for _, n := range b.entries {
191-
tabset.Add(n.IP())
191+
tabset.AddAddr(n.IPAddr())
192192
}
193193
}
194194
if tabset.String() != tab.ips.String() {
@@ -268,7 +268,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
268268
}
269269
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
270270
r := new(enr.Record)
271-
r.Set(enr.IP(genIP(rand)))
271+
r.Set(enr.IPv4Addr(netutil.RandomAddr(rand, true)))
272272
n := enode.SignNull(r, id)
273273
t.All = append(t.All, n)
274274
}
@@ -385,11 +385,11 @@ func checkBucketContent(t *testing.T, tab *Table, nodes []*enode.Node) {
385385
}
386386
t.Log("wrong bucket content. have nodes:")
387387
for _, n := range b.entries {
388-
t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IP())
388+
t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IPAddr())
389389
}
390390
t.Log("want nodes:")
391391
for _, n := range nodes {
392-
t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IP())
392+
t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IPAddr())
393393
}
394394
t.FailNow()
395395

@@ -483,12 +483,6 @@ func gen(typ interface{}, rand *rand.Rand) interface{} {
483483
return v.Interface()
484484
}
485485

486-
func genIP(rand *rand.Rand) net.IP {
487-
ip := make(net.IP, 4)
488-
rand.Read(ip)
489-
return ip
490-
}
491-
492486
func quickcfg() *quick.Config {
493487
return &quick.Config{
494488
MaxCount: 5000,

p2p/discover/table_util_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ func idAtDistance(a enode.ID, n int) (b enode.ID) {
100100
return b
101101
}
102102

103+
// intIP returns a LAN IP address based on i.
103104
func intIP(i int) net.IP {
104-
return net.IP{byte(i), 0, 2, byte(i)}
105+
return net.IP{10, 0, byte(i >> 8), byte(i & 0xFF)}
105106
}
106107

107108
// fillBucket inserts nodes into the given bucket until it is full.
@@ -254,7 +255,7 @@ NotEqual:
254255
}
255256

256257
func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool {
257-
return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP())
258+
return n1.ID() == n2.ID() && n1.IPAddr() == n2.IPAddr()
258259
}
259260

260261
func sortByID[N nodeType](nodes []N) {

0 commit comments

Comments
 (0)