This repository has been archived by the owner on Jan 26, 2024. It is now read-only.
forked from google/cloudprober
/
pingutils.go
123 lines (108 loc) · 3.43 KB
/
pingutils.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
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ping
import (
"encoding/binary"
"net"
"strconv"
"strings"
"time"
"github.com/google/cloudprober/probes/probeutils"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
const timeBytesSize = 8
func bytesToTime(b []byte) int64 {
var unixNano int64
for i := uint8(0); i < timeBytesSize; i++ {
unixNano += int64(b[i]) << ((timeBytesSize - i - 1) * timeBytesSize)
}
return unixNano
}
func ipToKey(ip net.IP) (key [16]byte) {
copy(key[:], ip.To16())
return
}
func validEchoReply(ipVer int, typeByte byte) bool {
switch ipVer {
case 6:
return ipv6.ICMPType(typeByte) == ipv6.ICMPTypeEchoReply
default:
return ipv4.ICMPType(typeByte) == ipv4.ICMPTypeEchoReply
}
}
func prepareRequestPayload(payload []byte, unixNano int64) {
var timeBytes [timeBytesSize]byte
for i := uint8(0); i < timeBytesSize; i++ {
// To get timeBytes:
// 0th byte - shift bits by 56 (7*8) bits, AND with 0xff to get the last 8 bits
// 1st byte - shift bits by 48 (6*8) bits, AND with 0xff to get the last 8 bits
// ... ...
// 7th byte - shift bits by 0 (0*8) bits, AND with 0xff to get the last 8 bits
timeBytes[i] = byte((unixNano >> ((timeBytesSize - i - 1) * timeBytesSize)) & 0xff)
}
probeutils.PatternPayload(payload, timeBytes[:])
}
// This function is a direct copy of checksum from the following package:
// https://godoc.org/golang.org/x/net/icmp
// TODO(manugarg): Follow up to find out if checksum from icmp package can be
// made public.
func checksum(b []byte) uint16 {
csumcv := len(b) - 1 // checksum coverage
s := uint32(0)
for i := 0; i < csumcv; i += 2 {
s += uint32(b[i+1])<<8 | uint32(b[i])
}
if csumcv&1 == 0 {
s += uint32(b[csumcv])
}
s = s>>16 + s&0xffff
s = s + s>>16
return ^uint16(s)
}
// prepareRequestPacket prepares the Echo request packet in the given pktbuf
// bytes buffer.
func (p *Probe) prepareRequestPacket(pktbuf []byte, runID, seq uint16, unixNano int64) {
if p.ipVer == 6 {
pktbuf[0] = byte(ipv6.ICMPTypeEchoRequest)
} else {
pktbuf[0] = byte(ipv4.ICMPTypeEcho)
}
pktbuf[1] = byte(0)
// We fill these 2 bytes later with the checksum.
pktbuf[2] = 0
pktbuf[3] = 0
binary.BigEndian.PutUint16(pktbuf[4:6], uint16(runID))
binary.BigEndian.PutUint16(pktbuf[6:8], uint16(seq))
// Fill payload with the bytes corresponding to current time.
prepareRequestPayload(pktbuf[8:], unixNano)
// For IPv6 and datagram sockets ,checksum is computed by the kernel.
if p.ipVer != 6 && !p.useDatagramSocket {
csum := checksum(pktbuf)
pktbuf[2] ^= byte(csum)
pktbuf[3] ^= byte(csum >> 8)
}
}
func (pkt *rcvdPkt) String(rtt time.Duration) string {
var b strings.Builder
b.WriteString("peer=")
b.WriteString(pkt.target)
b.WriteString(" id=")
b.WriteString(strconv.FormatInt(int64(pkt.id), 10))
b.WriteString(" seq=")
b.WriteString(strconv.FormatInt(int64(pkt.seq), 10))
b.WriteString(" rtt=")
b.WriteString(rtt.String())
return b.String()
}