github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/p2p/discover/table.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package discover implements the Node Discovery Protocol. 18 // 19 // The Node Discovery protocol provides a way to find RLPx nodes that 20 // can be connected to. It uses a Kademlia-like protocol to maintain a 21 // distributed database of the IDs and endpoints of all listening 22 // nodes. 23 package discover 24 25 import ( 26 "crypto/rand" 27 "encoding/binary" 28 "errors" 29 "fmt" 30 "net" 31 "sync" 32 "time" 33 34 "github.com/ethereumproject/go-ethereum/common" 35 "github.com/ethereumproject/go-ethereum/crypto" 36 "github.com/ethereumproject/go-ethereum/logger" 37 "github.com/ethereumproject/go-ethereum/logger/glog" 38 "github.com/ethereumproject/go-ethereum/p2p/distip" 39 ) 40 41 const ( 42 alpha = 3 // Kademlia concurrency factor 43 bucketSize = 16 // Kademlia bucket size 44 maxReplacements = 10 // Size of per-bucket replacement list 45 hashBits = len(common.Hash{}) * 8 46 nBuckets = hashBits + 1 // Number of buckets 47 bucketMinDistance = hashBits - nBuckets // Log distance of closest bucket 48 49 maxBondingPingPongs = 16 50 maxFindnodeFailures = 5 51 52 // IP address limits. 53 bucketIPLimit, bucketSubnet = 2, 24 // at most 2 addresses from the same /24 54 tableIPLimit, tableSubnet = 10, 24 55 56 autoRefreshInterval = 1 * time.Hour 57 seedCount = 30 58 seedMaxAge = 5 * 24 * time.Hour 59 ) 60 61 var ( 62 logdistS1 = new(common.Hash) 63 logdistS2 = new(common.Hash) 64 logdistS3 [64]byte 65 ) 66 67 func init() { 68 s1a := make([]byte, 32) 69 s2a := make([]byte, 32) 70 71 rand.Read(s1a) 72 rand.Read(s2a) 73 rand.Read(logdistS3[:]) 74 75 logdistS1.SetBytes(s1a) 76 logdistS2.SetBytes(s2a) 77 } 78 79 type Table struct { 80 mutex sync.Mutex // protects buckets, their content, and nursery 81 buckets [nBuckets]*bucket // index of known nodes by distance 82 nursery []*Node // bootstrap nodes 83 db *nodeDB // database of known nodes 84 ips distip.DistinctNetSet 85 86 refreshReq chan chan struct{} 87 closeReq chan struct{} 88 closed chan struct{} 89 initDone chan struct{} 90 91 bondmu sync.Mutex 92 bonding map[NodeID]*bondproc 93 bondslots chan struct{} // limits total number of active bonding processes 94 95 nodeAddedHook func(*Node) // for testing 96 97 net transport 98 self *Node // metadata of the local node 99 } 100 101 type bondproc struct { 102 err error 103 n *Node 104 done chan struct{} 105 } 106 107 // transport is implemented by the UDP transport. 108 // it is an interface so we can test without opening lots of UDP 109 // sockets and without generating a private key. 110 type transport interface { 111 ping(NodeID, *net.UDPAddr) error 112 waitping(NodeID) error 113 findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error) 114 close() 115 } 116 117 // bucket contains nodes, ordered by their last activity. the entry 118 // that was most recently active is the first element in entries. 119 type bucket struct { 120 entries []*Node // live entries, sorted by time of last contact 121 replacements []*Node // recently seen nodes to be used if revalidation fails 122 ips distip.DistinctNetSet 123 } 124 125 func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string) (*Table, error) { 126 // If no node database was given, use an in-memory one 127 db, err := newNodeDB(nodeDBPath, Version, ourID) 128 if err != nil { 129 return nil, err 130 } 131 tab := &Table{ 132 net: t, 133 db: db, 134 self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)), 135 bonding: make(map[NodeID]*bondproc), 136 bondslots: make(chan struct{}, maxBondingPingPongs), 137 refreshReq: make(chan chan struct{}), 138 closeReq: make(chan struct{}), 139 closed: make(chan struct{}), 140 initDone: make(chan struct{}), 141 ips: distip.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, 142 } 143 for i := 0; i < cap(tab.bondslots); i++ { 144 tab.bondslots <- struct{}{} 145 } 146 for i := range tab.buckets { 147 tab.buckets[i] = &bucket{ 148 ips: distip.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit}, 149 } 150 } 151 go tab.refreshLoop() 152 return tab, nil 153 } 154 155 // Self returns the local node. 156 // The returned node should not be modified by the caller. 157 func (tab *Table) Self() *Node { 158 return tab.self 159 } 160 161 // ReadRandomNodes fills the given slice with random nodes from the 162 // table. It will not write the same node more than once. The nodes in 163 // the slice are copies and can be modified by the caller. 164 func (tab *Table) ReadRandomNodes(buf []*Node) (n int) { 165 if !tab.isInitDone() { 166 return 0 167 } 168 169 tab.mutex.Lock() 170 defer tab.mutex.Unlock() 171 // TODO: tree-based buckets would help here 172 // Find all non-empty buckets and get a fresh slice of their entries. 173 var buckets [][]*Node 174 for _, b := range tab.buckets { 175 if len(b.entries) > 0 { 176 buckets = append(buckets, b.entries[:]) 177 } 178 } 179 if len(buckets) == 0 { 180 return 0 181 } 182 // Shuffle the buckets. 183 for i := uint32(len(buckets)) - 1; i > 0; i-- { 184 j := randUint(i) 185 buckets[i], buckets[j] = buckets[j], buckets[i] 186 } 187 // Move head of each bucket into buf, removing buckets that become empty. 188 var i, j int 189 for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { 190 b := buckets[j] 191 buf[i] = &(*b[0]) 192 buckets[j] = b[1:] 193 if len(b) == 1 { 194 buckets = append(buckets[:j], buckets[j+1:]...) 195 } 196 if len(buckets) == 0 { 197 break 198 } 199 } 200 return i + 1 201 } 202 203 func randUint(max uint32) uint32 { 204 if max == 0 { 205 return 0 206 } 207 var b [4]byte 208 rand.Read(b[:]) 209 return binary.BigEndian.Uint32(b[:]) % max 210 } 211 212 // Close terminates the network listener and flushes the node database. 213 func (tab *Table) Close() { 214 select { 215 case <-tab.closed: 216 // already closed. 217 case tab.closeReq <- struct{}{}: 218 <-tab.closed // wait for refreshLoop to end. 219 } 220 } 221 222 // SetFallbackNodes sets the initial points of contact. These nodes 223 // are used to connect to the network if the table is empty and there 224 // are no known nodes in the database. 225 func (tab *Table) SetFallbackNodes(nodes []*Node) error { 226 for _, n := range nodes { 227 if err := n.validateComplete(); err != nil { 228 return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) 229 } 230 } 231 tab.mutex.Lock() 232 tab.nursery = make([]*Node, 0, len(nodes)) 233 for _, n := range nodes { 234 cpy := *n 235 // Recompute cpy.sha because the node might not have been 236 // created by NewNode or ParseNode. 237 cpy.sha = crypto.Keccak256Hash(n.ID[:]) 238 tab.nursery = append(tab.nursery, &cpy) 239 } 240 tab.mutex.Unlock() 241 tab.refresh() 242 return nil 243 } 244 245 // isInitDone returns whether the table's initial seeding procedure has completed. 246 func (tab *Table) isInitDone() bool { 247 select { 248 case <-tab.initDone: 249 return true 250 default: 251 return false 252 } 253 } 254 255 // Resolve searches for a specific node with the given ID. 256 // It returns nil if the node could not be found. 257 func (tab *Table) Resolve(id NodeID) *Node { 258 // If the node is present in the local table, no 259 // network interaction is required. 260 tab.mutex.Lock() 261 for _, b := range tab.buckets { 262 for _, n := range b.entries { 263 if n.ID == id { 264 tab.mutex.Unlock() 265 return n 266 } 267 } 268 } 269 tab.mutex.Unlock() 270 271 // FIXME: do exact lookup 272 result := tab.Lookup(id) 273 for _, n := range result { 274 if n.ID == id { 275 return n 276 } 277 } 278 return nil 279 } 280 281 // Lookup performs a network search for nodes close 282 // to the given target. It approaches the target by querying 283 // nodes that are closer to it on each iteration. 284 // The given target does not need to be an actual node 285 // identifier. 286 func (tab *Table) Lookup(id NodeID) []*Node { 287 return tab.lookup(id, true) 288 } 289 290 func (tab *Table) lookup(id NodeID, refreshIfEmpty bool) []*Node { 291 var ( 292 asked = make(map[NodeID]bool) 293 reply = make(chan []*Node, alpha) 294 pendingQueries = 0 295 ) 296 297 var result *closest 298 if id == tab.self.ID { 299 result = tab.closest(logdistS3) 300 } else { 301 result = tab.closest(id) 302 } 303 304 // don't query further if we hit ourself. 305 // unlikely to happen often in practice. 306 asked[tab.self.ID] = true 307 308 if result.Nodes[0] == nil && refreshIfEmpty { 309 // The result set is empty, all nodes were dropped, refresh. 310 // We actually wait for the refresh to complete here. The very 311 // first query will hit this case and run the bootstrapping 312 // logic. 313 <-tab.refresh() 314 315 if id == tab.self.ID { 316 result = tab.closest(logdistS3) 317 } else { 318 result = tab.closest(id) 319 } 320 } 321 322 for { 323 // ask the alpha closest nodes that we haven't asked yet 324 for _, n := range result.Nodes { 325 if n == nil || pendingQueries >= alpha { 326 break 327 } 328 329 if !asked[n.ID] { 330 asked[n.ID] = true 331 pendingQueries++ 332 go func(n *Node) { 333 // Find potential neighbors to bond with 334 theirNeighbors, err := tab.net.findnode(n.ID, n.addr(), id) 335 if err != nil { 336 // Bump the failure counter to detect and evacuate non-bonded entries 337 fails := tab.db.findFails(n.ID) + 1 338 tab.db.updateFindFails(n.ID, fails) 339 glog.V(logger.Detail).Infof("Bumping failures for %x: %d", n.ID[:8], fails) 340 341 if fails >= maxFindnodeFailures { 342 glog.V(logger.Detail).Infof("Evacuating node %x: %d findnode failures", n.ID[:8], fails) 343 tab.delete(n) 344 } 345 } 346 reply <- tab.bondall(theirNeighbors) 347 }(n) 348 } 349 } 350 351 if pendingQueries == 0 { 352 // we have asked all closest nodes, stop the search 353 break 354 } 355 356 // wait for the next reply 357 for _, n := range <-reply { 358 if n != nil { 359 result.Add(n) 360 } 361 } 362 pendingQueries-- 363 } 364 return result.Slice() 365 } 366 367 func (tab *Table) refresh() <-chan struct{} { 368 done := make(chan struct{}) 369 select { 370 case tab.refreshReq <- done: 371 case <-tab.closed: 372 close(done) 373 } 374 return done 375 } 376 377 // refreshLoop schedules doRefresh runs and coordinates shutdown. 378 func (tab *Table) refreshLoop() { 379 var ( 380 timer = time.NewTicker(autoRefreshInterval) 381 waiting = []chan struct{}{tab.initDone} // accumulates waiting callers while doRefresh runs 382 done = make(chan struct{}) // where doRefresh reports completion 383 ) 384 385 // Initial refresh 386 go tab.doRefresh(done) 387 388 loop: 389 for { 390 select { 391 case <-timer.C: 392 if done == nil { 393 done = make(chan struct{}) 394 go tab.doRefresh(done) 395 } 396 case req := <-tab.refreshReq: 397 waiting = append(waiting, req) 398 if done == nil { 399 done = make(chan struct{}) 400 go tab.doRefresh(done) 401 } 402 case <-done: 403 for _, ch := range waiting { 404 close(ch) 405 } 406 waiting = nil 407 done = nil 408 case <-tab.closeReq: 409 break loop 410 } 411 } 412 413 if tab.net != nil { 414 tab.net.close() 415 } 416 if done != nil { 417 <-done 418 } 419 for _, ch := range waiting { 420 close(ch) 421 } 422 tab.db.close() 423 close(tab.closed) 424 } 425 426 // doRefresh performs a lookup for a random target to keep buckets 427 // full. seed nodes are inserted if the table is empty (initial 428 // bootstrap or discarded faulty peers). 429 func (tab *Table) doRefresh(done chan struct{}) { 430 defer close(done) 431 432 // The table is empty. Load nodes from the database and insert 433 // them. This should yield a few previously seen nodes that are 434 // (hopefully) still alive. 435 seeds := tab.db.querySeeds(seedCount, seedMaxAge) 436 seeds = tab.bondall(append(seeds, tab.nursery...)) 437 if glog.V(logger.Debug) { 438 if len(seeds) == 0 { 439 glog.Infof("no seed nodes found") 440 } 441 for _, n := range seeds { 442 age := time.Since(tab.db.lastPong(n.ID)) 443 glog.Infof("seed node (age %v): %v", age, n) 444 } 445 } 446 tab.mutex.Lock() 447 tab.stuff(seeds) 448 tab.mutex.Unlock() 449 450 // Finally, do a self lookup to fill up the buckets. 451 tab.lookup(tab.self.ID, false) 452 453 // The Kademlia paper specifies that the bucket refresh should 454 // perform a lookup in the least recently used bucket. We cannot 455 // adhere to this because the findnode target is a 512bit value 456 // (not hash-sized) and it is not easily possible to generate a 457 // sha3 preimage that falls into a chosen bucket. 458 // We perform a few lookups with a random target instead. 459 for i := 0; i < 3; i++ { 460 var target NodeID 461 rand.Read(target[:]) 462 tab.lookup(target, false) 463 } 464 } 465 466 func (tab *Table) closest(id NodeID) *closest { 467 c := &closest{Target: crypto.Keccak256Hash(id[:])} 468 469 tab.mutex.Lock() 470 defer tab.mutex.Unlock() 471 472 for _, b := range tab.buckets { 473 for _, n := range b.entries { 474 c.Add(n) 475 } 476 } 477 return c 478 } 479 480 func (tab *Table) len() (n int) { 481 for _, b := range tab.buckets { 482 n += len(b.entries) 483 } 484 return n 485 } 486 487 // bondall bonds with all given nodes concurrently and returns 488 // those nodes for which bonding has probably succeeded. 489 func (tab *Table) bondall(nodes []*Node) (result []*Node) { 490 rc := make(chan *Node, len(nodes)) 491 for i := range nodes { 492 go func(n *Node) { 493 nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCP)) 494 rc <- nn 495 }(nodes[i]) 496 } 497 for range nodes { 498 if n := <-rc; n != nil { 499 result = append(result, n) 500 } 501 } 502 return result 503 } 504 505 // bond ensures the local node has a bond with the given remote node. 506 // It also attempts to insert the node into the table if bonding succeeds. 507 // The caller must not hold tab.mutex. 508 // 509 // A bond is must be established before sending findnode requests. 510 // Both sides must have completed a ping/pong exchange for a bond to 511 // exist. The total number of active bonding processes is limited in 512 // order to restrain network use. 513 // 514 // bond is meant to operate idempotently in that bonding with a remote 515 // node which still remembers a previously established bond will work. 516 // The remote node will simply not send a ping back, causing waitping 517 // to time out. 518 // 519 // If pinged is true, the remote node has just pinged us and one half 520 // of the process can be skipped. 521 func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) { 522 if id == tab.self.ID { 523 return nil, errors.New("is self") 524 } 525 if pinged && !tab.isInitDone() { 526 return nil, errors.New("still initializing") 527 } 528 // Retrieve a previously known node and any recent findnode failures 529 node, fails := tab.db.node(id), 0 530 if node != nil { 531 fails = tab.db.findFails(id) 532 } 533 // If the node is unknown (non-bonded) or failed (remotely unknown), bond from scratch 534 var result error 535 age := time.Since(tab.db.lastPong(id)) 536 if node == nil || fails > 0 || age > nodeDBNodeExpiration { 537 glog.V(logger.Detail).Infof("Bonding %x: known=%t, fails=%d age=%v", id[:8], node != nil, fails, age) 538 539 tab.bondmu.Lock() 540 w := tab.bonding[id] 541 if w != nil { 542 // Wait for an existing bonding process to complete. 543 tab.bondmu.Unlock() 544 <-w.done 545 } else { 546 // Register a new bonding process. 547 w = &bondproc{done: make(chan struct{})} 548 tab.bonding[id] = w 549 tab.bondmu.Unlock() 550 // Do the ping/pong. The result goes into w. 551 tab.pingpong(w, pinged, id, addr, tcpPort) 552 // Unregister the process after it's done. 553 tab.bondmu.Lock() 554 delete(tab.bonding, id) 555 tab.bondmu.Unlock() 556 } 557 // Retrieve the bonding results 558 result = w.err 559 if result == nil { 560 node = w.n 561 } 562 } 563 if node != nil { 564 // Add the node to the table even if the bonding ping/pong 565 // fails. It will be replaced quickly if it continues to be 566 // unresponsive. 567 tab.add(node) 568 tab.db.updateFindFails(id, 0) 569 } 570 return node, result 571 } 572 573 func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) { 574 // Request a bonding slot to limit network usage 575 <-tab.bondslots 576 defer func() { tab.bondslots <- struct{}{} }() 577 578 // Ping the remote side and wait for a pong. 579 if w.err = tab.ping(id, addr); w.err != nil { 580 close(w.done) 581 return 582 } 583 if !pinged { 584 // Give the remote node a chance to ping us before we start 585 // sending findnode requests. If they still remember us, 586 // waitping will simply time out. 587 tab.net.waitping(id) 588 } 589 // Bonding succeeded, update the node database. 590 w.n = NewNode(id, addr.IP, uint16(addr.Port), tcpPort) 591 tab.db.updateNode(w.n) 592 close(w.done) 593 } 594 595 // ping a remote endpoint and wait for a reply, also updating the node 596 // database accordingly. 597 func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { 598 tab.db.updateLastPing(id, time.Now()) 599 if err := tab.net.ping(id, addr); err != nil { 600 return err 601 } 602 tab.db.updateLastPong(id, time.Now()) 603 604 // Start the background expiration goroutine after the first 605 // successful communication. Subsequent calls have no effect if it 606 // is already running. We do this here instead of somewhere else 607 // so that the search for seed nodes also considers older nodes 608 // that would otherwise be removed by the expiration. 609 tab.db.ensureExpirer() 610 return nil 611 } 612 613 // add attempts to add the given node its corresponding bucket. If the 614 // bucket has space available, adding the node succeeds immediately. 615 // Otherwise, the node is added if the least recently active node in 616 // the bucket does not respond to a ping packet. 617 // 618 // The caller must not hold tab.mutex. 619 func (tab *Table) add(new *Node) { 620 tab.mutex.Lock() 621 defer tab.mutex.Unlock() 622 623 b := tab.bucket(new.sha) 624 if !tab.bumpOrAdd(b, new) { 625 // Node is not in table. Add it to the replacement list. 626 tab.addReplacement(b, new) 627 } 628 } 629 630 // bucket returns the bucket for the given node ID hash. 631 func (tab *Table) bucket(sha common.Hash) *bucket { 632 d := logdist(tab.self.sha, sha) 633 if d <= bucketMinDistance { 634 return tab.buckets[0] 635 } 636 return tab.buckets[d-bucketMinDistance-1] 637 } 638 639 // stuff adds nodes the table to the end of their corresponding bucket 640 // if the bucket is not full. The caller must hold tab.mutex. 641 func (tab *Table) stuff(nodes []*Node) { 642 outer: 643 for _, n := range nodes { 644 if n.ID == tab.self.ID { 645 continue // don't add self 646 } 647 bucket := tab.buckets[logdist(*logdistS1, crypto.Keccak256Hash(n.sha.Bytes(), logdistS2.Bytes()))] 648 for i := range bucket.entries { 649 if bucket.entries[i].ID == n.ID { 650 continue outer // already in bucket 651 } 652 } 653 if len(bucket.entries) < bucketSize { 654 bucket.entries = append(bucket.entries, n) 655 if tab.nodeAddedHook != nil { 656 tab.nodeAddedHook(n) 657 } 658 } 659 } 660 } 661 662 // delete removes an entry from the node table (used to evacuate 663 // failed/non-bonded discovery peers). 664 func (tab *Table) delete(node *Node) { 665 tab.mutex.Lock() 666 defer tab.mutex.Unlock() 667 bucket := tab.buckets[logdist(*logdistS1, crypto.Keccak256Hash(node.sha.Bytes(), logdistS2.Bytes()))] 668 for i := range bucket.entries { 669 if bucket.entries[i].ID == node.ID { 670 bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...) 671 return 672 } 673 } 674 } 675 676 func (b *bucket) replace(n *Node, last *Node) bool { 677 // Don't add if b already contains n. 678 for i := range b.entries { 679 if b.entries[i].ID == n.ID { 680 return false 681 } 682 } 683 // Replace last if it is still the last entry or just add n if b 684 // isn't full. If is no longer the last entry, it has either been 685 // replaced with someone else or became active. 686 if len(b.entries) == bucketSize && (last == nil || b.entries[bucketSize-1].ID != last.ID) { 687 return false 688 } 689 if len(b.entries) < bucketSize { 690 b.entries = append(b.entries, nil) 691 } 692 copy(b.entries[1:], b.entries) 693 b.entries[0] = n 694 return true 695 } 696 697 func (b *bucket) bump(n *Node) bool { 698 for i := range b.entries { 699 if b.entries[i].ID == n.ID { 700 // move it to the front 701 copy(b.entries[1:], b.entries[:i]) 702 b.entries[0] = n 703 return true 704 } 705 } 706 return false 707 } 708 709 // bumpOrAdd moves n to the front of the bucket entry list or adds it if the list isn't 710 // full. The return value is true if n is in the bucket. 711 func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool { 712 if b.bump(n) { 713 return true 714 } 715 if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP) { 716 return false 717 } 718 b.entries, _ = pushNode(b.entries, n, bucketSize) 719 b.replacements = deleteNode(b.replacements, n) 720 n.addedAt = time.Now() 721 if tab.nodeAddedHook != nil { 722 tab.nodeAddedHook(n) 723 } 724 return true 725 } 726 727 func (tab *Table) addReplacement(b *bucket, n *Node) { 728 for _, e := range b.replacements { 729 if e.ID == n.ID { 730 return // already in list 731 } 732 } 733 if !tab.addIP(b, n.IP) { 734 return 735 } 736 var removed *Node 737 b.replacements, removed = pushNode(b.replacements, n, maxReplacements) 738 if removed != nil { 739 tab.removeIP(b, removed.IP) 740 } 741 } 742 743 func (tab *Table) addIP(b *bucket, ip net.IP) bool { 744 if distip.IsLAN(ip) { 745 return true 746 } 747 if !tab.ips.Add(ip) { 748 // log.Debug("IP exceeds table limit", "ip", ip) 749 return false 750 } 751 if !b.ips.Add(ip) { 752 // log.Debug("IP exceeds bucket limit", "ip", ip) 753 tab.ips.Remove(ip) 754 return false 755 } 756 return true 757 } 758 759 func (tab *Table) removeIP(b *bucket, ip net.IP) { 760 if distip.IsLAN(ip) { 761 return 762 } 763 tab.ips.Remove(ip) 764 b.ips.Remove(ip) 765 } 766 767 // pushNode adds n to the front of list, keeping at most max items. 768 func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) { 769 if len(list) < max { 770 list = append(list, nil) 771 } 772 removed := list[len(list)-1] 773 copy(list[1:], list) 774 list[0] = n 775 return list, removed 776 } 777 778 // deleteNode removes n from list. 779 func deleteNode(list []*Node, n *Node) []*Node { 780 for i := range list { 781 if list[i].ID == n.ID { 782 return append(list[:i], list[i+1:]...) 783 } 784 } 785 return list 786 }