packetconn.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package icmp
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "net"
  6. "sync"
  7. "syscall"
  8. "time"
  9. "github.com/google/uuid"
  10. "golang.org/x/net/icmp"
  11. "golang.org/x/net/ipv4"
  12. "golang.org/x/net/ipv6"
  13. )
  14. const (
  15. protocolICMP = 1
  16. protocolIPv6ICMP = 58
  17. )
  18. var ENOLISTENER = fmt.Errorf("no listener")
  19. type Type icmp.Type
  20. type PacketConn interface {
  21. Close() error
  22. ICMPRequestType() Type
  23. ReadFrom(b []byte) (n int, ttl int, src net.Addr, err error)
  24. SetFlagTTL() error
  25. SetReadDeadline(t time.Time) error
  26. WriteTo(b []byte, dst net.Addr) (int, error)
  27. SetTTL(ttl int)
  28. }
  29. var (
  30. ipv4Proto = map[string]string{"icmp": "ip4:icmp", "udp": "udp4"}
  31. ipv6Proto = map[string]string{"icmp": "ip6:ipv6-icmp", "udp": "udp6"}
  32. )
  33. func listen(ipv4 bool, protocol string, source string) (conn PacketConn, err error) {
  34. if ipv4 {
  35. var c icmpv4Conn
  36. c.c, err = icmp.ListenPacket(ipv4Proto[protocol], source)
  37. conn = &c
  38. } else {
  39. var c icmpV6Conn
  40. c.c, err = icmp.ListenPacket(ipv6Proto[protocol], source)
  41. conn = &c
  42. }
  43. return
  44. }
  45. // Packet represents a received and processed ICMP echo packet.
  46. type Packet struct {
  47. // IPAddr is the address of the host being pinged.
  48. IPAddr *net.IPAddr
  49. // ID is the ICMP identifier.
  50. ID int
  51. // Seq is the ICMP sequence number.
  52. Seq int
  53. // UUID
  54. UUID uuid.UUID
  55. // TTL is the Time To Live on the packet.
  56. TTL int
  57. // NBytes is the number of bytes in the message.
  58. Nbytes int
  59. // SendTime
  60. SendTime time.Time
  61. // Rtt is the round-trip time it took to ping.
  62. Rtt time.Duration
  63. }
  64. type recvPkt struct {
  65. recvtime time.Time
  66. addr net.Addr
  67. bytes []byte
  68. nbytes int
  69. ttl int
  70. }
  71. type MPacketConn struct {
  72. IPV4 bool
  73. Protocol string
  74. Source string
  75. Backlog int
  76. TTL int
  77. OnRecvPacket func(pkt *Packet)
  78. OnRecvError func(error)
  79. mutex sync.Mutex
  80. conn PacketConn
  81. done chan interface{}
  82. recvbuf chan *recvPkt
  83. }
  84. func (mp *MPacketConn) Listen() error {
  85. mp.mutex.Lock()
  86. defer mp.mutex.Unlock()
  87. if mp.conn != nil {
  88. return nil
  89. }
  90. conn, err := listen(mp.IPV4, mp.Protocol, mp.Source)
  91. if err != nil {
  92. return err
  93. }
  94. conn.SetTTL(mp.TTL)
  95. if err := conn.SetFlagTTL(); err != nil {
  96. return err
  97. }
  98. mp.done = make(chan interface{})
  99. mp.recvbuf = make(chan *recvPkt, mp.Backlog)
  100. mp.conn = conn
  101. go mp.recvICMP()
  102. go mp.processRecvPacket()
  103. return nil
  104. }
  105. func (mp *MPacketConn) Close() error {
  106. mp.mutex.Lock()
  107. defer mp.mutex.Unlock()
  108. open := true
  109. select {
  110. case _, open = <-mp.done:
  111. default:
  112. }
  113. if open {
  114. close(mp.done)
  115. }
  116. return mp.conn.Close()
  117. }
  118. func (mp *MPacketConn) recvICMP() {
  119. bytes := make([]byte, 65536)
  120. for {
  121. select {
  122. case <-mp.done:
  123. return
  124. default:
  125. var n, ttl int
  126. var addr net.Addr
  127. var err error
  128. n, ttl, addr, err = mp.conn.ReadFrom(bytes)
  129. if err != nil {
  130. if neterr, ok := err.(*net.OpError); ok {
  131. if neterr.Timeout() {
  132. // Read timeout
  133. continue
  134. }
  135. }
  136. if mp.OnRecvError != nil {
  137. mp.OnRecvError(err)
  138. } else {
  139. fmt.Println(err)
  140. }
  141. }
  142. bs := make([]byte, n)
  143. copy(bs, bytes[:n])
  144. select {
  145. case <-mp.done:
  146. return
  147. case mp.recvbuf <- &recvPkt{recvtime: time.Now(), addr: addr, bytes: bs, nbytes: n, ttl: ttl}:
  148. }
  149. }
  150. }
  151. }
  152. func (mp *MPacketConn) SendPacket(pkt *Packet) error {
  153. if mp.conn == nil {
  154. return ENOLISTENER
  155. }
  156. var dst net.Addr = pkt.IPAddr
  157. if mp.Protocol == "udp" {
  158. dst = &net.UDPAddr{IP: pkt.IPAddr.IP, Zone: pkt.IPAddr.Zone}
  159. }
  160. for {
  161. select {
  162. case <-mp.done:
  163. return nil
  164. default:
  165. }
  166. msgBytes, err := pkt.BuildEchoRequestMessage(mp.conn.ICMPRequestType())
  167. if err != nil {
  168. return err
  169. }
  170. if _, err := mp.conn.WriteTo(msgBytes, dst); err != nil {
  171. if neterr, ok := err.(*net.OpError); ok {
  172. if neterr.Err == syscall.ENOBUFS {
  173. if mp.OnRecvError != nil {
  174. mp.OnRecvError(neterr.Err)
  175. } else {
  176. fmt.Println("缓存不够,发送失败,重发")
  177. }
  178. continue
  179. }
  180. }
  181. return err
  182. } else {
  183. return nil
  184. }
  185. }
  186. }
  187. var max_receive_buffer_used = 0
  188. func MaxReceiveBufferUsed() int {
  189. return max_receive_buffer_used
  190. }
  191. func (mp *MPacketConn) processRecvPacket() {
  192. for pkt := range mp.recvbuf {
  193. if len(mp.recvbuf) > max_receive_buffer_used {
  194. max_receive_buffer_used = len(mp.recvbuf)
  195. }
  196. err := mp.processPacket(pkt)
  197. if err != nil {
  198. if mp.OnRecvError != nil {
  199. mp.OnRecvError(err)
  200. } else {
  201. fmt.Println(err)
  202. }
  203. }
  204. }
  205. }
  206. var count = 0
  207. func (mp *MPacketConn) processPacket(recv *recvPkt) error {
  208. var proto int
  209. if mp.IPV4 {
  210. proto = protocolICMP
  211. } else {
  212. proto = protocolIPv6ICMP
  213. }
  214. // fmt.Println(count, "from", recv.addr.String(), "bytes", recv.bytes)
  215. var m *icmp.Message
  216. var err error
  217. if m, err = icmp.ParseMessage(proto, recv.bytes); err != nil {
  218. return fmt.Errorf("error parsing icmp message: %w", err)
  219. }
  220. if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply {
  221. // Not an echo reply, ignore it
  222. return nil
  223. }
  224. switch pkt := m.Body.(type) {
  225. case *icmp.Echo:
  226. return mp.processEchoReply(pkt, recv)
  227. default:
  228. // Very bad, not sure how this can happen
  229. return fmt.Errorf("invalid ICMP echo reply; type: '%T', '%v'", pkt, pkt)
  230. }
  231. }
  232. func (mp *MPacketConn) processEchoReply(pkt *icmp.Echo, recv *recvPkt) error {
  233. if len(pkt.Data) < 40 {
  234. return nil
  235. }
  236. sendtime := int64(binary.BigEndian.Uint64(pkt.Data[:8]))
  237. fullseq := int(binary.BigEndian.Uint64(pkt.Data[8:16]))
  238. fullid := int(binary.BigEndian.Uint64(pkt.Data[16:24]))
  239. pktuuid := uuid.Must(uuid.FromBytes(pkt.Data[24:40]))
  240. // Linux 下 UDP 方式,接收的 EchoReply.ID 与发送的 Echo.ID 是不一致的
  241. // if fullid%65536 != pkt.ID || fullseq%65536 != pkt.Seq {
  242. // return nil
  243. // }
  244. // fmt.Printf("%s %d bytes from %s: icmp_seq=%d time=%v\n",
  245. // time.Now().Format("15:04:05.000"), recv.nbytes, recv.addr, fullseq, recv.recvtime.Sub(time.Unix(0, sendtime)))
  246. if mp.OnRecvPacket != nil {
  247. mp.OnRecvPacket(&Packet{
  248. IPAddr: netAddrToIPAddr(recv.addr),
  249. ID: fullid,
  250. Seq: fullseq,
  251. UUID: pktuuid,
  252. Nbytes: recv.nbytes,
  253. TTL: recv.ttl,
  254. SendTime: time.Unix(0, sendtime),
  255. Rtt: recv.recvtime.Sub(time.Unix(0, sendtime)),
  256. })
  257. }
  258. return nil
  259. }