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 }