github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discover/udp.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:43</date> 10 //</624342656343871488> 11 12 13 package discover 14 15 import ( 16 "bytes" 17 "container/list" 18 "crypto/ecdsa" 19 "errors" 20 "fmt" 21 "net" 22 "time" 23 24 "github.com/ethereum/go-ethereum/crypto" 25 "github.com/ethereum/go-ethereum/log" 26 "github.com/ethereum/go-ethereum/p2p/nat" 27 "github.com/ethereum/go-ethereum/p2p/netutil" 28 "github.com/ethereum/go-ethereum/rlp" 29 ) 30 31 //错误 32 var ( 33 errPacketTooSmall = errors.New("too small") 34 errBadHash = errors.New("bad hash") 35 errExpired = errors.New("expired") 36 errUnsolicitedReply = errors.New("unsolicited reply") 37 errUnknownNode = errors.New("unknown node") 38 errTimeout = errors.New("RPC timeout") 39 errClockWarp = errors.New("reply deadline too far in the future") 40 errClosed = errors.New("socket closed") 41 ) 42 43 //超时 44 const ( 45 respTimeout = 500 * time.Millisecond 46 expiration = 20 * time.Second 47 48 ntpFailureThreshold = 32 //连续超时,之后检查NTP 49 ntpWarningCooldown = 10 * time.Minute //重复NTP警告之前要经过的最短时间 50 driftThreshold = 10 * time.Second //警告用户前允许的时钟漂移 51 ) 52 53 //RPC数据包类型 54 const ( 55 pingPacket = iota + 1 //零为“保留” 56 pongPacket 57 findnodePacket 58 neighborsPacket 59 ) 60 61 //RPC请求结构 62 type ( 63 ping struct { 64 Version uint 65 From, To rpcEndpoint 66 Expiration uint64 67 //忽略其他字段(为了向前兼容)。 68 Rest []rlp.RawValue `rlp:"tail"` 69 } 70 71 //乒乓球是对乒乓球的回应。 72 pong struct { 73 //此字段应镜像UDP信封地址 74 //提供了一种发现 75 //外部地址(在NAT之后)。 76 To rpcEndpoint 77 78 ReplyTok []byte //这包含ping包的哈希。 79 Expiration uint64 //数据包失效的绝对时间戳。 80 //忽略其他字段(为了向前兼容)。 81 Rest []rlp.RawValue `rlp:"tail"` 82 } 83 84 //findnode是对接近给定目标的节点的查询。 85 findnode struct { 86 Target NodeID //不需要是实际的公钥 87 Expiration uint64 88 //忽略其他字段(为了向前兼容)。 89 Rest []rlp.RawValue `rlp:"tail"` 90 } 91 92 //回复findnode 93 neighbors struct { 94 Nodes []rpcNode 95 Expiration uint64 96 //忽略其他字段(为了向前兼容)。 97 Rest []rlp.RawValue `rlp:"tail"` 98 } 99 100 rpcNode struct { 101 IP net.IP //IPv4的len 4或IPv6的len 16 102 UDP uint16 //用于发现协议 103 TCP uint16 //对于RLPX协议 104 ID NodeID 105 } 106 107 rpcEndpoint struct { 108 IP net.IP //IPv4的len 4或IPv6的len 16 109 UDP uint16 //用于发现协议 110 TCP uint16 //对于RLPX协议 111 } 112 ) 113 114 func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { 115 ip := addr.IP.To4() 116 if ip == nil { 117 ip = addr.IP.To16() 118 } 119 return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} 120 } 121 122 func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { 123 if rn.UDP <= 1024 { 124 return nil, errors.New("low port") 125 } 126 if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { 127 return nil, err 128 } 129 if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { 130 return nil, errors.New("not contained in netrestrict whitelist") 131 } 132 n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) 133 err := n.validateComplete() 134 return n, err 135 } 136 137 func nodeToRPC(n *Node) rpcNode { 138 return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} 139 } 140 141 type packet interface { 142 handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error 143 name() string 144 } 145 146 type conn interface { 147 ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) 148 WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) 149 Close() error 150 LocalAddr() net.Addr 151 } 152 153 //UDP实现RPC协议。 154 type udp struct { 155 conn conn 156 netrestrict *netutil.Netlist 157 priv *ecdsa.PrivateKey 158 ourEndpoint rpcEndpoint 159 160 addpending chan *pending 161 gotreply chan reply 162 163 closing chan struct{} 164 nat nat.Interface 165 166 *Table 167 } 168 169 //挂起表示挂起的答复。 170 // 171 //协议的某些实现希望发送多个 172 //将数据包回复到findnode。一般来说,任何邻居包都不能 173 //与特定的findnode包匹配。 174 // 175 //我们的实现通过存储 176 //每个等待答复。来自节点的传入数据包被调度 177 //到该节点的所有回调函数。 178 type pending struct { 179 //这些字段必须在答复中匹配。 180 from NodeID 181 ptype byte 182 183 //请求必须完成的时间 184 deadline time.Time 185 186 //当匹配的答复到达时调用回调。如果它回来 187 //如果为true,则从挂起的答复队列中删除回调。 188 //如果返回错误,则认为答复不完整,并且 189 //将为下一个匹配的答复再次调用回调。 190 callback func(resp interface{}) (done bool) 191 192 //当回调指示完成或 193 //如果在超时时间内没有收到进一步的答复,则出错。 194 errc chan<- error 195 } 196 197 type reply struct { 198 from NodeID 199 ptype byte 200 data interface{} 201 //循环指示是否存在 202 //通过此频道发送的匹配请求。 203 matched chan<- bool 204 } 205 206 //无法处理readpacket时,会将其发送到未处理的通道。 207 type ReadPacket struct { 208 Data []byte 209 Addr *net.UDPAddr 210 } 211 212 //配置保存与表相关的设置。 213 type Config struct { 214 //需要这些设置并配置UDP侦听器: 215 PrivateKey *ecdsa.PrivateKey 216 217 //这些设置是可选的: 218 AnnounceAddr *net.UDPAddr //DHT中公布的本地地址 219 NodeDBPath string //如果设置,则节点数据库存储在此文件系统位置 220 NetRestrict *netutil.Netlist //网络白名单 221 Bootnodes []*Node //引导程序节点列表 222 Unhandled chan<- ReadPacket //在此通道上发送未处理的数据包 223 } 224 225 //listenudp返回一个新表,用于侦听laddr上的udp包。 226 func ListenUDP(c conn, cfg Config) (*Table, error) { 227 tab, _, err := newUDP(c, cfg) 228 if err != nil { 229 return nil, err 230 } 231 log.Info("UDP listener up", "self", tab.self) 232 return tab, nil 233 } 234 235 func newUDP(c conn, cfg Config) (*Table, *udp, error) { 236 udp := &udp{ 237 conn: c, 238 priv: cfg.PrivateKey, 239 netrestrict: cfg.NetRestrict, 240 closing: make(chan struct{}), 241 gotreply: make(chan reply), 242 addpending: make(chan *pending), 243 } 244 realaddr := c.LocalAddr().(*net.UDPAddr) 245 if cfg.AnnounceAddr != nil { 246 realaddr = cfg.AnnounceAddr 247 } 248 //TODO:单独的TCP端口 249 udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port)) 250 tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes) 251 if err != nil { 252 return nil, nil, err 253 } 254 udp.Table = tab 255 256 go udp.loop() 257 go udp.readLoop(cfg.Unhandled) 258 return udp.Table, udp, nil 259 } 260 261 func (t *udp) close() { 262 close(t.closing) 263 t.conn.Close() 264 //TODO:等待循环结束。 265 } 266 267 //ping向给定节点发送ping消息并等待答复。 268 func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error { 269 return <-t.sendPing(toid, toaddr, nil) 270 } 271 272 //发送ping向给定节点发送ping消息并调用回调 273 //当回复到达时。 274 func (t *udp) sendPing(toid NodeID, toaddr *net.UDPAddr, callback func()) <-chan error { 275 req := &ping{ 276 Version: 4, 277 From: t.ourEndpoint, 278 To: makeEndpoint(toaddr, 0), //TODO:可能使用数据库中已知的TCP端口 279 Expiration: uint64(time.Now().Add(expiration).Unix()), 280 } 281 packet, hash, err := encodePacket(t.priv, pingPacket, req) 282 if err != nil { 283 errc := make(chan error, 1) 284 errc <- err 285 return errc 286 } 287 errc := t.pending(toid, pongPacket, func(p interface{}) bool { 288 ok := bytes.Equal(p.(*pong).ReplyTok, hash) 289 if ok && callback != nil { 290 callback() 291 } 292 return ok 293 }) 294 t.write(toaddr, req.name(), packet) 295 return errc 296 } 297 298 func (t *udp) waitping(from NodeID) error { 299 return <-t.pending(from, pingPacket, func(interface{}) bool { return true }) 300 } 301 302 //findnode向给定节点发送findnode请求,并等待直到 303 //节点已发送到k个邻居。 304 func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { 305 //如果我们有一段时间没有看到目标节点的ping,它将不会记得 306 //我们的端点证明和拒绝findnode。先打个乒乓球。 307 if time.Since(t.db.lastPingReceived(toid)) > nodeDBNodeExpiration { 308 t.ping(toid, toaddr) 309 t.waitping(toid) 310 } 311 312 nodes := make([]*Node, 0, bucketSize) 313 nreceived := 0 314 errc := t.pending(toid, neighborsPacket, func(r interface{}) bool { 315 reply := r.(*neighbors) 316 for _, rn := range reply.Nodes { 317 nreceived++ 318 n, err := t.nodeFromRPC(toaddr, rn) 319 if err != nil { 320 log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toaddr, "err", err) 321 continue 322 } 323 nodes = append(nodes, n) 324 } 325 return nreceived >= bucketSize 326 }) 327 t.send(toaddr, findnodePacket, &findnode{ 328 Target: target, 329 Expiration: uint64(time.Now().Add(expiration).Unix()), 330 }) 331 return nodes, <-errc 332 } 333 334 //挂起向挂起的答复队列添加答复回调。 335 //有关详细说明,请参阅“挂起”类型的文档。 336 func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-chan error { 337 ch := make(chan error, 1) 338 p := &pending{from: id, ptype: ptype, callback: callback, errc: ch} 339 select { 340 case t.addpending <- p: 341 //循环将处理它 342 case <-t.closing: 343 ch <- errClosed 344 } 345 return ch 346 } 347 348 func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool { 349 matched := make(chan bool, 1) 350 select { 351 case t.gotreply <- reply{from, ptype, req, matched}: 352 //循环将处理它 353 return <-matched 354 case <-t.closing: 355 return false 356 } 357 } 358 359 //循环在自己的Goroutine中运行。它跟踪 360 //刷新计时器和挂起的答复队列。 361 func (t *udp) loop() { 362 var ( 363 plist = list.New() 364 timeout = time.NewTimer(0) 365 nextTimeout *pending //上次重置超时时的plist头 366 contTimeouts = 0 //要执行NTP检查的连续超时数 367 ntpWarnTime = time.Unix(0, 0) 368 ) 369 <-timeout.C //忽略第一次超时 370 defer timeout.Stop() 371 372 resetTimeout := func() { 373 if plist.Front() == nil || nextTimeout == plist.Front().Value { 374 return 375 } 376 //启动计时器,以便在下一个挂起的答复过期时触发。 377 now := time.Now() 378 for el := plist.Front(); el != nil; el = el.Next() { 379 nextTimeout = el.Value.(*pending) 380 if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { 381 timeout.Reset(dist) 382 return 383 } 384 //删除截止时间太长的挂起答复 385 //未来。如果系统时钟跳变,就会发生这种情况。 386 //在最后期限被分配后向后。 387 nextTimeout.errc <- errClockWarp 388 plist.Remove(el) 389 } 390 nextTimeout = nil 391 timeout.Stop() 392 } 393 394 for { 395 resetTimeout() 396 397 select { 398 case <-t.closing: 399 for el := plist.Front(); el != nil; el = el.Next() { 400 el.Value.(*pending).errc <- errClosed 401 } 402 return 403 404 case p := <-t.addpending: 405 p.deadline = time.Now().Add(respTimeout) 406 plist.PushBack(p) 407 408 case r := <-t.gotreply: 409 var matched bool 410 for el := plist.Front(); el != nil; el = el.Next() { 411 p := el.Value.(*pending) 412 if p.from == r.from && p.ptype == r.ptype { 413 matched = true 414 //如果Matcher的回调指示 415 //所有答复都已收到。这是 416 //需要多个数据包类型 417 //应答包。 418 if p.callback(r.data) { 419 p.errc <- nil 420 plist.Remove(el) 421 } 422 //重置连续超时计数器(时间漂移检测) 423 contTimeouts = 0 424 } 425 } 426 r.matched <- matched 427 428 case now := <-timeout.C: 429 nextTimeout = nil 430 431 //通知并删除期限已过的回调。 432 for el := plist.Front(); el != nil; el = el.Next() { 433 p := el.Value.(*pending) 434 if now.After(p.deadline) || now.Equal(p.deadline) { 435 p.errc <- errTimeout 436 plist.Remove(el) 437 contTimeouts++ 438 } 439 } 440 //如果我们累积了太多超时,请执行NTP时间同步检查 441 if contTimeouts > ntpFailureThreshold { 442 if time.Since(ntpWarnTime) >= ntpWarningCooldown { 443 ntpWarnTime = time.Now() 444 go checkClockDrift() 445 } 446 contTimeouts = 0 447 } 448 } 449 } 450 } 451 452 const ( 453 macSize = 256 / 8 454 sigSize = 520 / 8 455 headSize = macSize + sigSize //包帧数据空间 456 ) 457 458 var ( 459 headSpace = make([]byte, headSize) 460 461 //邻居答复通过多个数据包发送到 462 //低于1280字节的限制。我们计算最大数 463 //通过填充一个包直到它变得太大。 464 maxNeighbors int 465 ) 466 467 func init() { 468 p := neighbors{Expiration: ^uint64(0)} 469 maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} 470 for n := 0; ; n++ { 471 p.Nodes = append(p.Nodes, maxSizeNode) 472 size, _, err := rlp.EncodeToReader(p) 473 if err != nil { 474 //如果发生这种情况,它将被单元测试捕获。 475 panic("cannot encode: " + err.Error()) 476 } 477 if headSize+size+1 >= 1280 { 478 maxNeighbors = n 479 break 480 } 481 } 482 } 483 484 func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req packet) ([]byte, error) { 485 packet, hash, err := encodePacket(t.priv, ptype, req) 486 if err != nil { 487 return hash, err 488 } 489 return hash, t.write(toaddr, req.name(), packet) 490 } 491 492 func (t *udp) write(toaddr *net.UDPAddr, what string, packet []byte) error { 493 _, err := t.conn.WriteToUDP(packet, toaddr) 494 log.Trace(">> "+what, "addr", toaddr, "err", err) 495 return err 496 } 497 498 func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (packet, hash []byte, err error) { 499 b := new(bytes.Buffer) 500 b.Write(headSpace) 501 b.WriteByte(ptype) 502 if err := rlp.Encode(b, req); err != nil { 503 log.Error("Can't encode discv4 packet", "err", err) 504 return nil, nil, err 505 } 506 packet = b.Bytes() 507 sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) 508 if err != nil { 509 log.Error("Can't sign discv4 packet", "err", err) 510 return nil, nil, err 511 } 512 copy(packet[macSize:], sig) 513 //将哈希添加到前面。注意:这不保护 514 //以任何方式打包。我们的公钥将是这个哈希的一部分 515 //未来。 516 hash = crypto.Keccak256(packet[macSize:]) 517 copy(packet, hash) 518 return packet, hash, nil 519 } 520 521 //readloop在自己的goroutine中运行。它处理传入的UDP数据包。 522 func (t *udp) readLoop(unhandled chan<- ReadPacket) { 523 defer t.conn.Close() 524 if unhandled != nil { 525 defer close(unhandled) 526 } 527 //发现数据包被定义为不大于1280字节。 528 //大于此尺寸的包装将在末端切割并处理 529 //因为它们的哈希不匹配而无效。 530 buf := make([]byte, 1280) 531 for { 532 nbytes, from, err := t.conn.ReadFromUDP(buf) 533 if netutil.IsTemporaryError(err) { 534 //忽略临时读取错误。 535 log.Debug("Temporary UDP read error", "err", err) 536 continue 537 } else if err != nil { 538 //关闭永久错误循环。 539 log.Debug("UDP read error", "err", err) 540 return 541 } 542 if t.handlePacket(from, buf[:nbytes]) != nil && unhandled != nil { 543 select { 544 case unhandled <- ReadPacket{buf[:nbytes], from}: 545 default: 546 } 547 } 548 } 549 } 550 551 func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { 552 packet, fromID, hash, err := decodePacket(buf) 553 if err != nil { 554 log.Debug("Bad discv4 packet", "addr", from, "err", err) 555 return err 556 } 557 err = packet.handle(t, from, fromID, hash) 558 log.Trace("<< "+packet.name(), "addr", from, "err", err) 559 return err 560 } 561 562 func decodePacket(buf []byte) (packet, NodeID, []byte, error) { 563 if len(buf) < headSize+1 { 564 return nil, NodeID{}, nil, errPacketTooSmall 565 } 566 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] 567 shouldhash := crypto.Keccak256(buf[macSize:]) 568 if !bytes.Equal(hash, shouldhash) { 569 return nil, NodeID{}, nil, errBadHash 570 } 571 fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) 572 if err != nil { 573 return nil, NodeID{}, hash, err 574 } 575 var req packet 576 switch ptype := sigdata[0]; ptype { 577 case pingPacket: 578 req = new(ping) 579 case pongPacket: 580 req = new(pong) 581 case findnodePacket: 582 req = new(findnode) 583 case neighborsPacket: 584 req = new(neighbors) 585 default: 586 return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype) 587 } 588 s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) 589 err = s.Decode(req) 590 return req, fromID, hash, err 591 } 592 593 func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { 594 if expired(req.Expiration) { 595 return errExpired 596 } 597 t.send(from, pongPacket, &pong{ 598 To: makeEndpoint(from, req.From.TCP), 599 ReplyTok: mac, 600 Expiration: uint64(time.Now().Add(expiration).Unix()), 601 }) 602 t.handleReply(fromID, pingPacket, req) 603 604 //将节点添加到表中。在这样做之前,确保我们最近有足够的乒乓球 605 //记录在数据库中,以便稍后接受其findnode请求。 606 n := NewNode(fromID, from.IP, uint16(from.Port), req.From.TCP) 607 if time.Since(t.db.lastPongReceived(fromID)) > nodeDBNodeExpiration { 608 t.sendPing(fromID, from, func() { t.addThroughPing(n) }) 609 } else { 610 t.addThroughPing(n) 611 } 612 t.db.updateLastPingReceived(fromID, time.Now()) 613 return nil 614 } 615 616 func (req *ping) name() string { return "PING/v4" } 617 618 func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { 619 if expired(req.Expiration) { 620 return errExpired 621 } 622 if !t.handleReply(fromID, pongPacket, req) { 623 return errUnsolicitedReply 624 } 625 t.db.updateLastPongReceived(fromID, time.Now()) 626 return nil 627 } 628 629 func (req *pong) name() string { return "PONG/v4" } 630 631 func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { 632 if expired(req.Expiration) { 633 return errExpired 634 } 635 if !t.db.hasBond(fromID) { 636 //不存在端点验证pong,我们不处理数据包。这可以防止 637 //攻击向量,发现协议可用于放大 638 //DDoS攻击。恶意参与者将使用IP地址发送findnode请求 639 //目标的UDP端口作为源地址。findnode的接收者 640 //然后,包将发送一个邻居包(比 641 //找到受害者。 642 return errUnknownNode 643 } 644 target := crypto.Keccak256Hash(req.Target[:]) 645 t.mutex.Lock() 646 closest := t.closest(target, bucketSize).entries 647 t.mutex.Unlock() 648 649 p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} 650 var sent bool 651 //以块形式发送邻居,每个数据包最多有maxneighbors 652 //低于1280字节的限制。 653 for _, n := range closest { 654 if netutil.CheckRelayIP(from.IP, n.IP) == nil { 655 p.Nodes = append(p.Nodes, nodeToRPC(n)) 656 } 657 if len(p.Nodes) == maxNeighbors { 658 t.send(from, neighborsPacket, &p) 659 p.Nodes = p.Nodes[:0] 660 sent = true 661 } 662 } 663 if len(p.Nodes) > 0 || !sent { 664 t.send(from, neighborsPacket, &p) 665 } 666 return nil 667 } 668 669 func (req *findnode) name() string { return "FINDNODE/v4" } 670 671 func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { 672 if expired(req.Expiration) { 673 return errExpired 674 } 675 if !t.handleReply(fromID, neighborsPacket, req) { 676 return errUnsolicitedReply 677 } 678 return nil 679 } 680 681 func (req *neighbors) name() string { return "NEIGHBORS/v4" } 682 683 func expired(ts uint64) bool { 684 return time.Unix(int64(ts), 0).Before(time.Now()) 685 } 686