github.com/jlowellwofford/u-root@v1.0.0/cmds/ping/ping.go (about)

     1  // Copyright 2009 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Send icmp packets to a server to test network connectivity.
     6  //
     7  // Synopsis:
     8  //     ping [-hV] [-c COUNT] [-i INTERVAL] [-s PACKETSIZE] [-w DEADLINE] DESTINATION
     9  //
    10  // Options:
    11  //     -6: use ipv6 (ip6:ipv6-icmp)
    12  //     -s: data size (default: 64)
    13  //     -c: # iterations, 0 to run forever (default)
    14  //     -i: interval in milliseconds (default: 1000)
    15  //     -V: version
    16  //     -w: wait time in milliseconds (default: 100)
    17  //     -h: help
    18  package main
    19  
    20  import (
    21  	"encoding/binary"
    22  	"flag"
    23  	"fmt"
    24  	"log"
    25  	"net"
    26  	"os"
    27  	"time"
    28  )
    29  
    30  var (
    31  	net6       = flag.Bool("6", false, "use ipv4 (means ip4:icmp) or 6 (ip6:ipv6-icmp)")
    32  	packetSize = flag.Int("s", 64, "Data size")
    33  	iter       = flag.Uint64("c", 0, "# iterations")
    34  	intv       = flag.Int("i", 1000, "interval in milliseconds")
    35  	version    = flag.Bool("V", false, "version")
    36  	wtf        = flag.Int("w", 100, "wait time in milliseconds")
    37  	help       = flag.Bool("h", false, "help")
    38  )
    39  
    40  const (
    41  	ICMP_TYPE_ECHO_REQUEST             = 8
    42  	ICMP_TYPE_ECHO_REPLY               = 0
    43  	ICMP_ECHO_REPLY_HEADER_IPV4_OFFSET = 20
    44  	ICMP_ECHO_REPLY_HEADER_IPV6_OFFSET = 40
    45  )
    46  
    47  func usage() {
    48  	fmt.Fprintf(os.Stdout, "ping [-hV] [-c count] [-i interval] [-s packetsize] [-w deadline] destination\n")
    49  	os.Exit(0)
    50  }
    51  
    52  func showversion() {
    53  	fmt.Fprintf(os.Stdout, "ping utility, Uroot version\n")
    54  	os.Exit(0)
    55  }
    56  
    57  func optwithoutparam() {
    58  	if *version {
    59  		showversion()
    60  	}
    61  	// if we reach this point, invalid or help (-h) gets the same result
    62  	usage()
    63  }
    64  
    65  func beatifulLatency(before time.Time) (latency string) {
    66  	now := float64(time.Since(before).Nanoseconds())
    67  
    68  	switch {
    69  	case now > 1e6:
    70  		latency = fmt.Sprintf("%.2f ms", now/1e6)
    71  	case now > 1e3:
    72  		latency = fmt.Sprintf("%.2f ยต", now/1e3)
    73  	default:
    74  		latency = fmt.Sprintf("%v ns", now)
    75  	}
    76  
    77  	return latency
    78  }
    79  
    80  func cksum(bs []byte) uint16 {
    81  	sum := uint32(0)
    82  
    83  	for k := 0; k < len(bs)/2; k++ {
    84  		sum += uint32(bs[k*2]) << 8
    85  		sum += uint32(bs[k*2+1])
    86  	}
    87  	if len(bs)%2 != 0 {
    88  		sum += uint32(bs[len(bs)-1]) << 8
    89  	}
    90  	sum = (sum >> 16) + (sum & 0xffff)
    91  	sum = (sum >> 16) + (sum & 0xffff)
    92  	if sum == 0xffff {
    93  		sum = 0
    94  	}
    95  
    96  	return ^uint16(sum)
    97  }
    98  
    99  func ping1(netname string, host string, i uint64) (string, error) {
   100  	c, derr := net.Dial(netname, host)
   101  	if derr != nil {
   102  		return "", fmt.Errorf("net.Dial(%v %v) failed: %v", netname, host, derr)
   103  	}
   104  	defer c.Close()
   105  
   106  	// Send ICMP Echo Request
   107  	waitFor := time.Duration(*wtf) * time.Millisecond
   108  	c.SetDeadline(time.Now().Add(waitFor))
   109  	msg := make([]byte, *packetSize)
   110  	msg[0] = ICMP_TYPE_ECHO_REQUEST
   111  	msg[1] = 0
   112  	binary.BigEndian.PutUint16(msg[6:], uint16(i))
   113  	binary.BigEndian.PutUint16(msg[4:], uint16(i>>16))
   114  	binary.BigEndian.PutUint16(msg[2:], cksum(msg))
   115  	if _, err := c.Write(msg[:]); err != nil {
   116  		return "", fmt.Errorf("Write failed: %v", err)
   117  	}
   118  
   119  	// Get ICMP Echo Reply
   120  	before := time.Now()
   121  	c.SetDeadline(time.Now().Add(waitFor))
   122  	rmsg := make([]byte, *packetSize+256)
   123  	amt, rerr := c.Read(rmsg[:])
   124  	if rerr != nil {
   125  		return "", fmt.Errorf("Read failed: %v", rerr)
   126  	}
   127  	if (rmsg[0] & 0x0F) == 6 {
   128  		rmsg = rmsg[ICMP_ECHO_REPLY_HEADER_IPV6_OFFSET:]
   129  	} else {
   130  		rmsg = rmsg[ICMP_ECHO_REPLY_HEADER_IPV4_OFFSET:]
   131  	}
   132  	if rmsg[0] != ICMP_TYPE_ECHO_REPLY {
   133  		return "", fmt.Errorf("Bad ICMP echo reply type: %v", msg[0])
   134  	}
   135  	cks := binary.BigEndian.Uint16(rmsg[2:])
   136  	binary.BigEndian.PutUint16(rmsg[2:], 0)
   137  	if cks != cksum(rmsg) {
   138  		return "", fmt.Errorf("Bad ICMP checksum: %v (expected %v)", cks, cksum(rmsg))
   139  	}
   140  	latency := beatifulLatency(before)
   141  	id := binary.BigEndian.Uint16(rmsg[4:])
   142  	seq := binary.BigEndian.Uint16(rmsg[6:])
   143  	rseq := uint64(id)<<16 + uint64(seq)
   144  	if rseq != i {
   145  		return "", fmt.Errorf("wrong sequence number %v (expected %v)", rseq, i)
   146  	}
   147  
   148  	return fmt.Sprintf("%d bytes from %v: icmp_seq=%v, time=%v", amt, host, i, latency), nil
   149  }
   150  
   151  func main() {
   152  	flag.Parse()
   153  
   154  	// options without parameters (right now just: -hV)
   155  	if flag.NArg() < 1 {
   156  		optwithoutparam()
   157  	}
   158  	if *packetSize < 8 {
   159  		log.Fatalf("packet size too small (must be >= 8): %v", *packetSize)
   160  	}
   161  
   162  	netname := "ip4:icmp"
   163  	interval := time.Duration(*intv)
   164  	host := flag.Args()[0]
   165  	// todo: just figure out if it's an ip6 address and go from there.
   166  	if *net6 {
   167  		netname = "ip6:ipv6-icmp"
   168  	}
   169  
   170  	// ping needs to run forever, except if '*iter' is not zero
   171  	var i uint64
   172  	for i = 1; *iter == 0 || i < *iter; i++ {
   173  		msg, err := ping1(netname, host, i)
   174  		if err != nil {
   175  			log.Fatalf("ping failed: %v", err)
   176  		}
   177  		log.Print(msg)
   178  		time.Sleep(time.Millisecond * interval)
   179  	}
   180  }