123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- package icmp
- import (
- "encoding/binary"
- "fmt"
- "net"
- "sync"
- "syscall"
- "time"
- "github.com/google/uuid"
- "golang.org/x/net/icmp"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- )
- const (
- protocolICMP = 1
- protocolIPv6ICMP = 58
- )
- var ENOLISTENER = fmt.Errorf("no listener")
- type Type icmp.Type
- type PacketConn interface {
- Close() error
- ICMPRequestType() Type
- ReadFrom(b []byte) (n int, ttl int, src net.Addr, err error)
- SetFlagTTL() error
- SetReadDeadline(t time.Time) error
- WriteTo(b []byte, dst net.Addr) (int, error)
- SetTTL(ttl int)
- }
- var (
- ipv4Proto = map[string]string{"icmp": "ip4:icmp", "udp": "udp4"}
- ipv6Proto = map[string]string{"icmp": "ip6:ipv6-icmp", "udp": "udp6"}
- )
- // Packet represents a received and processed ICMP echo packet.
- type Packet struct {
- // IPAddr is the address of the host being pinged.
- IPAddr *net.IPAddr
- // ID is the ICMP identifier.
- ID int
- // Seq is the ICMP sequence number.
- Seq int
- // UUID
- UUID uuid.UUID
- // TTL is the Time To Live on the packet.
- TTL int
- // NBytes is the number of bytes in the message.
- Nbytes int
- // SendTime
- SendTime time.Time
- // Rtt is the round-trip time it took to ping.
- Rtt time.Duration
- //
- TimeoutTimer *time.Timer
- }
- type recvPkt struct {
- recvtime time.Time
- addr net.Addr
- bytes []byte
- nbytes int
- ttl int
- }
- type MPacketConn struct {
- IPV4 bool
- Protocol string
- Source string
- Backlog int
- TTL int
- OnRecvPacket func(pkt *Packet)
- OnError func(error)
- mutex sync.Mutex
- conn PacketConn
- done chan interface{}
- recvbuf chan *recvPkt
- }
- func (mp *MPacketConn) Listen() error {
- mp.mutex.Lock()
- defer mp.mutex.Unlock()
- if mp.conn != nil {
- return nil
- }
- conn, err := mp.listen()
- if err != nil {
- return err
- }
- mp.done = make(chan interface{})
- mp.recvbuf = make(chan *recvPkt, mp.Backlog)
- mp.conn = conn
- go mp.recvICMP()
- go mp.processRecvPacket()
- return nil
- }
- func (mp *MPacketConn) listen() (conn PacketConn, err error) {
- if mp.IPV4 {
- var c icmpv4Conn
- c.c, err = icmp.ListenPacket(ipv4Proto[mp.Protocol], mp.Source)
- conn = &c
- } else {
- var c icmpV6Conn
- c.c, err = icmp.ListenPacket(ipv6Proto[mp.Protocol], mp.Source)
- conn = &c
- }
- if err != nil {
- return nil, err
- }
- conn.SetTTL(mp.TTL)
- if err := conn.SetFlagTTL(); err != nil {
- conn.Close()
- return nil, err
- }
- return conn, nil
- }
- func (mp *MPacketConn) Close() error {
- mp.mutex.Lock()
- defer mp.mutex.Unlock()
- open := true
- select {
- case _, open = <-mp.done:
- default:
- }
- if open {
- close(mp.done)
- }
- if mp.conn != nil {
- mp.conn.Close()
- mp.conn = nil
- }
- return nil
- }
- func (mp *MPacketConn) recvICMP() {
- bytes := make([]byte, 65536)
- for {
- select {
- case <-mp.done:
- return
- default:
- conn := mp.conn
- if conn == nil {
- return
- }
- var n, ttl int
- var addr net.Addr
- var err error
- n, ttl, addr, err = conn.ReadFrom(bytes)
- if err != nil {
- if neterr, ok := err.(*net.OpError); ok {
- if neterr.Timeout() {
- // Read timeout
- continue
- }
- }
- if mp.OnError != nil {
- mp.OnError(err)
- } else {
- fmt.Println("ReadFrom Error:", err)
- }
- }
- bs := make([]byte, n)
- copy(bs, bytes[:n])
- select {
- case <-mp.done:
- return
- case mp.recvbuf <- &recvPkt{recvtime: time.Now(), addr: addr, bytes: bs, nbytes: n, ttl: ttl}:
- }
- }
- }
- }
- func (mp *MPacketConn) SendPacket(pkt *Packet) error {
- conn := mp.conn
- if conn == nil {
- return ENOLISTENER
- }
- var dst net.Addr = pkt.IPAddr
- if mp.Protocol == "udp" {
- dst = &net.UDPAddr{IP: pkt.IPAddr.IP, Zone: pkt.IPAddr.Zone}
- }
- for {
- select {
- case <-mp.done:
- return nil
- default:
- }
- msgBytes, err := pkt.BuildEchoRequestMessage(conn.ICMPRequestType())
- if err != nil {
- return err
- }
- if _, err := conn.WriteTo(msgBytes, dst); err != nil {
- if neterr, ok := err.(*net.OpError); ok {
- if neterr.Err == syscall.ENOBUFS {
- if mp.OnError != nil {
- mp.OnError(neterr.Err)
- } else {
- // 运行时默认忽略缓存不够错误
- // fmt.Println("缓存不够,发送失败,重发")
- }
- continue
- }
- }
- return err
- } else {
- return nil
- }
- }
- }
- var max_receive_buffer_used = 0
- func MaxReceiveBufferUsed() int {
- return max_receive_buffer_used
- }
- func (mp *MPacketConn) processRecvPacket() {
- for pkt := range mp.recvbuf {
- if len(mp.recvbuf) > max_receive_buffer_used {
- max_receive_buffer_used = len(mp.recvbuf)
- }
- err := mp.processPacket(pkt)
- if err != nil {
- if mp.OnError != nil {
- mp.OnError(err)
- } else {
- // 运行时默认忽略接收数据格式不符错误
- // fmt.Println(err)
- }
- }
- }
- }
- var count = 0
- func (mp *MPacketConn) processPacket(recv *recvPkt) error {
- var proto int
- if mp.IPV4 {
- proto = protocolICMP
- } else {
- proto = protocolIPv6ICMP
- }
- // fmt.Println(count, "from", recv.addr.String(), "bytes", recv.bytes)
- var m *icmp.Message
- var err error
- if m, err = icmp.ParseMessage(proto, recv.bytes); err != nil {
- return fmt.Errorf("error parsing icmp message: %w", err)
- }
- if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply {
- // Not an echo reply, ignore it
- return nil
- }
- switch pkt := m.Body.(type) {
- case *icmp.Echo:
- return mp.processEchoReply(pkt, recv)
- default:
- // Very bad, not sure how this can happen
- return fmt.Errorf("invalid ICMP echo reply; type: '%T', '%v'", pkt, pkt)
- }
- }
- func (mp *MPacketConn) processEchoReply(pkt *icmp.Echo, recv *recvPkt) error {
- if len(pkt.Data) < 40 {
- return nil
- }
- sendtime := int64(binary.BigEndian.Uint64(pkt.Data[:8]))
- fullseq := int(binary.BigEndian.Uint64(pkt.Data[8:16]))
- fullid := int(binary.BigEndian.Uint64(pkt.Data[16:24]))
- pktuuid := uuid.Must(uuid.FromBytes(pkt.Data[24:40]))
- // Linux 下 UDP 方式,接收的 EchoReply.ID 与发送的 Echo.ID 是不一致的
- // if fullid%65536 != pkt.ID || fullseq%65536 != pkt.Seq {
- // return nil
- // }
- // fmt.Printf("%s %d bytes from %s: icmp_seq=%d time=%v\n",
- // time.Now().Format("15:04:05.000"), recv.nbytes, recv.addr, fullseq, recv.recvtime.Sub(time.Unix(0, sendtime)))
- if mp.OnRecvPacket != nil {
- mp.OnRecvPacket(&Packet{
- IPAddr: netAddrToIPAddr(recv.addr),
- ID: fullid,
- Seq: fullseq,
- UUID: pktuuid,
- Nbytes: recv.nbytes,
- TTL: recv.ttl,
- SendTime: time.Unix(0, sendtime),
- Rtt: recv.recvtime.Sub(time.Unix(0, sendtime)),
- })
- }
- return nil
- }
|