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 }