gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/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  //     -a: Audible rings a bell when a packet is received
    18  //     -h: help
    19  package main
    20  
    21  import (
    22  	"encoding/binary"
    23  	"flag"
    24  	"fmt"
    25  	"log"
    26  	"net"
    27  	"os"
    28  	"time"
    29  )
    30  
    31  var (
    32  	net6       = flag.Bool("6", false, "use ipv4 (means ip4:icmp) or 6 (ip6:ipv6-icmp)")
    33  	packetSize = flag.Int("s", 64, "Data size")
    34  	iter       = flag.Uint64("c", 0, "# iterations")
    35  	intv       = flag.Int("i", 1000, "interval in milliseconds")
    36  	version    = flag.Bool("V", false, "version")
    37  	wtf        = flag.Int("w", 100, "wait time in milliseconds")
    38  	audible    = flag.Bool("a", false, "Audible rings a bell when a packet is received")
    39  )
    40  
    41  const (
    42  	ICMP_TYPE_ECHO_REQUEST             = 8
    43  	ICMP_TYPE_ECHO_REPLY               = 0
    44  	ICMP_ECHO_REPLY_HEADER_IPV4_OFFSET = 20
    45  	ICMP_ECHO_REPLY_HEADER_IPV6_OFFSET = 40
    46  )
    47  
    48  func usage() {
    49  	fmt.Fprintf(os.Stdout, "ping [-V] [-c count] [-i interval] [-s packetsize] [-w deadline] destination\n")
    50  	os.Exit(0)
    51  }
    52  
    53  func showversion() {
    54  	fmt.Fprintf(os.Stdout, "ping utility, Uroot version\n")
    55  	os.Exit(0)
    56  }
    57  
    58  func optwithoutparam() {
    59  	if *version {
    60  		showversion()
    61  	}
    62  	// if we reach this point, invalid or help (-h) gets the same result
    63  	usage()
    64  }
    65  
    66  func cksum(bs []byte) uint16 {
    67  	sum := uint32(0)
    68  
    69  	for k := 0; k < len(bs)/2; k++ {
    70  		sum += uint32(bs[k*2]) << 8
    71  		sum += uint32(bs[k*2+1])
    72  	}
    73  	if len(bs)%2 != 0 {
    74  		sum += uint32(bs[len(bs)-1]) << 8
    75  	}
    76  	sum = (sum >> 16) + (sum & 0xffff)
    77  	sum = (sum >> 16) + (sum & 0xffff)
    78  	if sum == 0xffff {
    79  		sum = 0
    80  	}
    81  
    82  	return ^uint16(sum)
    83  }
    84  
    85  func ping1(netname string, host string, i uint64) (string, error) {
    86  	c, derr := net.Dial(netname, host)
    87  	if derr != nil {
    88  		return "", fmt.Errorf("net.Dial(%v %v) failed: %v", netname, host, derr)
    89  	}
    90  	defer c.Close()
    91  
    92  	// Send ICMP Echo Request
    93  	waitFor := time.Duration(*wtf) * time.Millisecond
    94  	c.SetDeadline(time.Now().Add(waitFor))
    95  	msg := make([]byte, *packetSize)
    96  	msg[0] = ICMP_TYPE_ECHO_REQUEST
    97  	msg[1] = 0
    98  	binary.BigEndian.PutUint16(msg[6:], uint16(i))
    99  	binary.BigEndian.PutUint16(msg[4:], uint16(i>>16))
   100  	binary.BigEndian.PutUint16(msg[2:], cksum(msg))
   101  	if _, err := c.Write(msg[:]); err != nil {
   102  		return "", fmt.Errorf("write failed: %v", err)
   103  	}
   104  
   105  	// Get ICMP Echo Reply
   106  	c.SetDeadline(time.Now().Add(waitFor))
   107  	rmsg := make([]byte, *packetSize+256)
   108  	before := time.Now()
   109  	amt, rerr := c.Read(rmsg[:])
   110  	if rerr != nil {
   111  		return "", fmt.Errorf("read failed: %v", rerr)
   112  	}
   113  	latency := time.Since(before)
   114  	if (rmsg[0] & 0x0F) == 6 {
   115  		rmsg = rmsg[ICMP_ECHO_REPLY_HEADER_IPV6_OFFSET:]
   116  	} else {
   117  		rmsg = rmsg[ICMP_ECHO_REPLY_HEADER_IPV4_OFFSET:]
   118  	}
   119  	if rmsg[0] != ICMP_TYPE_ECHO_REPLY {
   120  		return "", fmt.Errorf("bad ICMP echo reply type: %v", msg[0])
   121  	}
   122  	cks := binary.BigEndian.Uint16(rmsg[2:])
   123  	binary.BigEndian.PutUint16(rmsg[2:], 0)
   124  	if cks != cksum(rmsg) {
   125  		return "", fmt.Errorf("bad ICMP checksum: %v (expected %v)", cks, cksum(rmsg))
   126  	}
   127  	id := binary.BigEndian.Uint16(rmsg[4:])
   128  	seq := binary.BigEndian.Uint16(rmsg[6:])
   129  	rseq := uint64(id)<<16 + uint64(seq)
   130  	if rseq != i {
   131  		return "", fmt.Errorf("wrong sequence number %v (expected %v)", rseq, i)
   132  	}
   133  
   134  	return fmt.Sprintf("%d bytes from %v: icmp_seq=%v, time=%v", amt, host, i, latency), nil
   135  }
   136  
   137  func main() {
   138  	flag.Parse()
   139  
   140  	// options without parameters (right now just: -hV)
   141  	if flag.NArg() < 1 {
   142  		optwithoutparam()
   143  	}
   144  	if *packetSize < 8 {
   145  		log.Fatalf("packet size too small (must be >= 8): %v", *packetSize)
   146  	}
   147  
   148  	netname := "ip4:icmp"
   149  	interval := time.Duration(*intv)
   150  	host := flag.Args()[0]
   151  	// todo: just figure out if it's an ip6 address and go from there.
   152  	if *net6 {
   153  		netname = "ip6:ipv6-icmp"
   154  	}
   155  
   156  	// ping needs to run forever, except if '*iter' is not zero
   157  	var i uint64
   158  	for i = 1; *iter == 0 || i <= *iter; i++ {
   159  		msg, err := ping1(netname, host, i)
   160  		if err != nil {
   161  			log.Fatalf("ping failed: %v", err)
   162  		}
   163  		if *audible {
   164  			msg = "\a" + msg
   165  		}
   166  		log.Print(msg)
   167  		time.Sleep(time.Millisecond * interval)
   168  	}
   169  }