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  }