/
sendpacket_linux.go
117 lines (100 loc) · 2.17 KB
/
sendpacket_linux.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
//go:build linux
package tcping
import (
"context"
"encoding/hex"
mrand "math/rand"
"net"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func getOutboundIP(dstIP net.IP) (*net.IPAddr, error) {
conn, err := net.Dial("udp", dstIP.String()+":80")
if err != nil {
return nil, err
}
defer conn.Close()
udpAddr := conn.LocalAddr().(*net.UDPAddr)
ipAddr := &net.IPAddr{
IP: udpAddr.IP,
Zone: udpAddr.Zone,
}
return ipAddr, nil
}
func (p *Pinger) reportTimeout(event *PacketEvent, err error) {
event.IsTimeout = true
p.updateStatistic(event)
if p.OnEvent != nil {
p.OnEvent(event, err)
}
}
func (p *Pinger) sendPacket(seq int, ctx context.Context) {
var err error
if p.outbound == nil {
p.outbound, err = getOutboundIP(p.Host.IP)
}
srcIP := *p.outbound
if err != nil {
panic(err)
}
ipLayer := &layers.IPv4{
SrcIP: srcIP.IP,
DstIP: p.Host.IP,
}
tcpHeader := &layers.TCP{
SrcPort: layers.TCPPort(mrand.Int()),
DstPort: layers.TCPPort(0),
Seq: mrand.Uint32(),
Ack: mrand.Uint32(),
ACK: true,
RST: false,
Window: 1505,
}
tcpHeader.SetNetworkLayerForChecksum(ipLayer)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
if err := tcpHeader.SerializeTo(buf, opts); err != nil {
panic(err)
}
event := &PacketEvent{
Seq: seq,
From: p.Host.IP.String(),
}
dialer := &net.Dialer{}
conn, err := dialer.DialContext(ctx, "ip4:tcp", p.Host.IP.String())
if err != nil {
p.reportTimeout(event, err)
return
}
defer conn.Close()
deadline := time.Now().Add(p.Timeout)
conn.SetDeadline(deadline)
timeA := time.Now()
_, err = conn.Write(buf.Bytes())
if err != nil {
p.reportTimeout(event, err)
return
}
recvBuf := make([]byte, 4096)
n, err := conn.Read(recvBuf)
timeB := time.Now()
if err != nil {
p.reportTimeout(event, err)
return
}
ackNum := hex.EncodeToString(buf.Bytes())[16:20]
seqNum := hex.EncodeToString(recvBuf[:n])[48:52]
if ackNum == seqNum {
event.Latency = timeB.Sub(timeA)
p.updateStatistic(event)
if p.OnEvent != nil {
p.OnEvent(event, nil)
}
return
}
p.reportTimeout(event, err)
}