github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+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 ) 46 47 const ( 48 ICMP6_TYPE_ECHO_REQUEST = 128 49 ICMP6_TYPE_ECHO_REPLY = 129 50 ICMP6_ECHO_REPLY_HEADER_IPV6_OFFSET = 40 51 ) 52 53 func usage() { 54 fmt.Fprintf(os.Stdout, "ping [-V] [-c count] [-i interval] [-s packetsize] [-w deadline] destination\n") 55 os.Exit(0) 56 } 57 58 func showversion() { 59 fmt.Fprintf(os.Stdout, "ping utility, Uroot version\n") 60 os.Exit(0) 61 } 62 63 func optwithoutparam() { 64 if *version { 65 showversion() 66 } 67 // if we reach this point, invalid or help (-h) gets the same result 68 usage() 69 } 70 71 func cksum(bs []byte) uint16 { 72 sum := uint32(0) 73 74 for k := 0; k < len(bs)/2; k++ { 75 sum += uint32(bs[k*2]) << 8 76 sum += uint32(bs[k*2+1]) 77 } 78 if len(bs)%2 != 0 { 79 sum += uint32(bs[len(bs)-1]) << 8 80 } 81 sum = (sum >> 16) + (sum & 0xffff) 82 sum = (sum >> 16) + (sum & 0xffff) 83 if sum == 0xffff { 84 sum = 0 85 } 86 87 return ^uint16(sum) 88 } 89 90 func ping1(net6 bool, host string, i uint64, waitFor time.Duration) (string, error) { 91 netname := "ip4:icmp" 92 // todo: just figure out if it's an ip6 address and go from there. 93 if net6 { 94 netname = "ip6:ipv6-icmp" 95 } 96 c, derr := net.Dial(netname, host) 97 if derr != nil { 98 return "", fmt.Errorf("net.Dial(%v %v) failed: %v", netname, host, derr) 99 } 100 defer c.Close() 101 102 if net6 { 103 ipc := c.(*net.IPConn) 104 if err := setupICMPv6Socket(ipc); err != nil { 105 return "", fmt.Errorf("failed to set up the ICMPv6 connection: %w", err) 106 } 107 } 108 109 // Send ICMP Echo Request 110 c.SetDeadline(time.Now().Add(waitFor)) 111 msg := make([]byte, *packetSize) 112 if net6 { 113 msg[0] = ICMP6_TYPE_ECHO_REQUEST 114 } else { 115 msg[0] = ICMP_TYPE_ECHO_REQUEST 116 } 117 msg[1] = 0 118 binary.BigEndian.PutUint16(msg[6:], uint16(i)) 119 binary.BigEndian.PutUint16(msg[4:], uint16(i>>16)) 120 binary.BigEndian.PutUint16(msg[2:], cksum(msg)) 121 if _, err := c.Write(msg[:]); err != nil { 122 return "", fmt.Errorf("write failed: %v", err) 123 } 124 125 // Get ICMP Echo Reply 126 c.SetDeadline(time.Now().Add(waitFor)) 127 rmsg := make([]byte, *packetSize+256) 128 before := time.Now() 129 amt, rerr := c.Read(rmsg[:]) 130 if rerr != nil { 131 return "", fmt.Errorf("read failed: %v", rerr) 132 } 133 latency := time.Since(before) 134 if !net6 { 135 rmsg = rmsg[ICMP_ECHO_REPLY_HEADER_IPV4_OFFSET:] 136 } 137 if net6 { 138 if rmsg[0] != ICMP6_TYPE_ECHO_REPLY { 139 return "", fmt.Errorf("bad ICMPv6 echo reply type, got %d, want %d", rmsg[0], ICMP6_TYPE_ECHO_REPLY) 140 } 141 } else { 142 if rmsg[0] != ICMP_TYPE_ECHO_REPLY { 143 return "", fmt.Errorf("bad ICMP echo reply type, got %d, want %d", rmsg[0], ICMP_TYPE_ECHO_REPLY) 144 } 145 } 146 cks := binary.BigEndian.Uint16(rmsg[2:]) 147 binary.BigEndian.PutUint16(rmsg[2:], 0) 148 // only validate the checksum for IPv4. For IPv6 this *should* be done by the 149 // TCP stack (and do we need to validate the checksum anyway?) 150 if !net6 && cks != cksum(rmsg) { 151 return "", fmt.Errorf("bad ICMP checksum: %v (expected %v)", cks, cksum(rmsg)) 152 } 153 id := binary.BigEndian.Uint16(rmsg[4:]) 154 seq := binary.BigEndian.Uint16(rmsg[6:]) 155 rseq := uint64(id)<<16 + uint64(seq) 156 if rseq != i { 157 return "", fmt.Errorf("wrong sequence number %v (expected %v)", rseq, i) 158 } 159 160 return fmt.Sprintf("%d bytes from %v: icmp_seq=%v, time=%v", amt, host, i, latency), nil 161 } 162 163 func main() { 164 flag.Parse() 165 166 // options without parameters (right now just: -hV) 167 if flag.NArg() < 1 { 168 optwithoutparam() 169 } 170 if *packetSize < 8 { 171 log.Fatalf("packet size too small (must be >= 8): %v", *packetSize) 172 } 173 174 interval := time.Duration(*intv) 175 host := flag.Args()[0] 176 177 // ping needs to run forever, except if '*iter' is not zero 178 waitFor := time.Duration(*wtf) * time.Millisecond 179 var i uint64 180 for i = 1; *iter == 0 || i <= *iter; i++ { 181 msg, err := ping1(*net6, host, i, waitFor) 182 if err != nil { 183 log.Fatalf("ping failed: %v", err) 184 } 185 if *audible { 186 msg = "\a" + msg 187 } 188 log.Print(msg) 189 time.Sleep(time.Millisecond * interval) 190 } 191 }