github.com/google/cloudprober@v0.11.3/probes/ping/pingutils.go (about) 1 // Copyright 2017 The Cloudprober Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ping 16 17 import ( 18 "encoding/binary" 19 "net" 20 "runtime" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/google/cloudprober/probes/probeutils" 26 "golang.org/x/net/ipv4" 27 "golang.org/x/net/ipv6" 28 ) 29 30 const timeBytesSize = 8 31 32 func bytesToTime(b []byte) int64 { 33 var unixNano int64 34 for i := uint8(0); i < timeBytesSize; i++ { 35 unixNano += int64(b[i]) << ((timeBytesSize - i - 1) * timeBytesSize) 36 } 37 return unixNano 38 } 39 40 func ipToKey(ip net.IP) (key [16]byte) { 41 copy(key[:], ip.To16()) 42 return 43 } 44 45 func validEchoReply(ipVer int, typeByte byte) bool { 46 switch ipVer { 47 case 6: 48 return ipv6.ICMPType(typeByte) == ipv6.ICMPTypeEchoReply 49 default: 50 return ipv4.ICMPType(typeByte) == ipv4.ICMPTypeEchoReply 51 } 52 } 53 54 func prepareRequestPayload(payload []byte, unixNano int64) { 55 var timeBytes [timeBytesSize]byte 56 for i := uint8(0); i < timeBytesSize; i++ { 57 // To get timeBytes: 58 // 0th byte - shift bits by 56 (7*8) bits, AND with 0xff to get the last 8 bits 59 // 1st byte - shift bits by 48 (6*8) bits, AND with 0xff to get the last 8 bits 60 // ... ... 61 // 7th byte - shift bits by 0 (0*8) bits, AND with 0xff to get the last 8 bits 62 timeBytes[i] = byte((unixNano >> ((timeBytesSize - i - 1) * timeBytesSize)) & 0xff) 63 } 64 probeutils.PatternPayload(payload, timeBytes[:]) 65 } 66 67 // This function is a direct copy of checksum from the following package: 68 // https://godoc.org/golang.org/x/net/icmp 69 // TODO(manugarg): Follow up to find out if checksum from icmp package can be 70 // made public. 71 func checksum(b []byte) uint16 { 72 csumcv := len(b) - 1 // checksum coverage 73 s := uint32(0) 74 for i := 0; i < csumcv; i += 2 { 75 s += uint32(b[i+1])<<8 | uint32(b[i]) 76 } 77 if csumcv&1 == 0 { 78 s += uint32(b[csumcv]) 79 } 80 s = s>>16 + s&0xffff 81 s = s + s>>16 82 return ^uint16(s) 83 } 84 85 // prepareRequestPacket prepares the Echo request packet in the given pktbuf 86 // bytes buffer. 87 func (p *Probe) prepareRequestPacket(pktbuf []byte, runID, seq uint16, unixNano int64) { 88 if p.ipVer == 6 { 89 pktbuf[0] = byte(ipv6.ICMPTypeEchoRequest) 90 } else { 91 pktbuf[0] = byte(ipv4.ICMPTypeEcho) 92 } 93 pktbuf[1] = byte(0) 94 95 // We fill these 2 bytes later with the checksum. 96 pktbuf[2] = 0 97 pktbuf[3] = 0 98 99 binary.BigEndian.PutUint16(pktbuf[4:6], uint16(runID)) 100 binary.BigEndian.PutUint16(pktbuf[6:8], uint16(seq)) 101 102 // Fill payload with the bytes corresponding to current time. 103 prepareRequestPayload(pktbuf[8:], unixNano) 104 105 // For IPv6 checksum is always computed by the kernel. 106 // For IPv4, we compute checksum only if using RAW socket or OS is darwin. 107 if p.ipVer == 4 { 108 if !p.useDatagramSocket || runtime.GOOS == "darwin" { 109 csum := checksum(pktbuf) 110 pktbuf[2] ^= byte(csum) 111 pktbuf[3] ^= byte(csum >> 8) 112 } 113 } 114 } 115 116 func (pkt *rcvdPkt) String(rtt time.Duration) string { 117 var b strings.Builder 118 b.WriteString("peer=") 119 b.WriteString(pkt.target) 120 b.WriteString(" id=") 121 b.WriteString(strconv.FormatInt(int64(pkt.id), 10)) 122 b.WriteString(" seq=") 123 b.WriteString(strconv.FormatInt(int64(pkt.seq), 10)) 124 b.WriteString(" rtt=") 125 b.WriteString(rtt.String()) 126 return b.String() 127 }