/
trace.go
129 lines (111 loc) · 2.66 KB
/
trace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package ping
import (
"log"
"sync"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
type PayloadIcmpIpLayer struct {
ip layers.IPv4
icmp layers.ICMPv4
payload gopacket.Payload
}
type IcmpEchoReply struct {
srcIp string
dstIp string
id int
seq int
}
type Server struct {
pid int
device string
wg sync.WaitGroup
stopReceiveChan chan bool
receiveParseChan chan []byte
receiveIcmpChan chan PayloadIcmpIpLayer
}
func NewServer(device string, pid int) *Server {
s := Server{
pid: pid,
device: device,
stopReceiveChan: make(chan bool),
receiveParseChan: make(chan []byte),
receiveIcmpChan: make(chan PayloadIcmpIpLayer, 1024),
}
return &s
}
func (s *Server) Start() {
log.Print("ping trace Start\n")
snaplen := 65536 // XXX make this a user specified option!
filter := "icmp"
handle, err := pcap.OpenLive(s.device, int32(snaplen), true, pcap.BlockForever)
if err != nil {
log.Fatal("error opening pcap handle: ", err)
}
if err := handle.SetBPFFilter(filter); err != nil {
log.Fatal("error setting BPF filter: ", err)
}
s.wg.Add(2)
s.parsingReplies()
go func() {
s.wg.Done()
Loop:
for {
select {
case <-s.stopReceiveChan:
break Loop
default:
data, _, err := handle.ReadPacketData()
if err != nil {
continue
}
s.receiveParseChan <- data
}
}
close(s.receiveParseChan)
close(s.stopReceiveChan)
}()
s.wg.Wait()
}
func (s *Server) Stop() {
log.Print("ping trace Stop\n")
s.stopReceiveChan <- true
}
func (s *Server) parsingReplies() {
var eth layers.Ethernet
var ip layers.IPv4
var icmp layers.ICMPv4
var payload gopacket.Payload
icmpParser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip, &icmp, &payload)
decoded := make([]gopacket.LayerType, 0, 4)
go func() {
s.wg.Done()
defer close(s.receiveIcmpChan)
for packet := range s.receiveParseChan {
err := icmpParser.DecodeLayers(packet, &decoded)
if err == nil {
s.receiveIcmpChan <- PayloadIcmpIpLayer{
ip: ip,
icmp: icmp,
payload: payload,
}
}
}
}()
}
func (s *Server) ReceivingIcmp(c chan<- IcmpEchoReply) {
go func() {
for bundle := range s.receiveIcmpChan {
//log.Println("icmp packet received:", bundle.ip.SrcIP.String(), ">", bundle.ip.DstIP.String(), bundle.icmp.Seq, bundle.icmp.TypeCode.String())
if bundle.icmp.TypeCode == 0 && s.pid == int(bundle.icmp.Id) { //ICMPv4TypeEchoReply
c <- IcmpEchoReply{
srcIp: bundle.ip.SrcIP.String(),
dstIp: bundle.ip.DstIP.String(),
id: int(bundle.icmp.Id),
seq: int(bundle.icmp.Seq),
}
}
}
}()
}