func (this *Pinger) sender() { defer func() { this.wg.Done() // Tell the receiver the sender has quit this.quit() }() id := int(this.pid) m := &icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: id, Seq: this.seq(), Data: this.payload, }, } var hf ipv4.HeaderFlags if this.DF { hf |= ipv4.DontFragment } wh := &ipv4.Header{ Version: ipv4.Version, Len: ipv4.HeaderLen, TOS: this.TOS, TTL: this.TTL, Protocol: 1, Flags: hf, } err := this.sendMessage(m, wh) if err != nil { glog.Errorf("ping/sender: Error sending echo requests: %v", err) } }
func (this *Pinger) sendMessage(m *icmp.Message, wh *ipv4.Header) error { select { case <-this.done: return nil default: } var ( wb []byte tempDelay time.Duration // how long to sleep on accept failure ticker = time.NewTicker(this.Interval) ) for i, dst := range this.IPs() { if i != 0 { <-ticker.C } binary.BigEndian.PutUint64(this.payload, uint64(time.Now().UnixNano())) wb, err := this.marshalMessage(m, wb) if err != nil { glog.Errorf("Error creating ICMP payload: %v", err) continue } wh.TotalLen = ipv4.HeaderLen + len(wb) wh.Dst = dst glog.Debugf("Pinging %s", wh.Dst) this.mu.Lock() this.results[wh.Dst.String()] = nil this.mu.Unlock() if err := this.rconn.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { return err } if err := this.rconn.WriteTo(wh, wb, nil); err != nil { select { case <-this.done: return nil default: } // Borrowed from go1.3.3/src/pkg/net/http/server.go:1699 if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } glog.Errorf("write error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue // out of the IP list, wait for next tick } return err } } if err := this.rconn.SetReadDeadline(time.Now().Add(this.MaxRTT)); err != nil { return err } return nil }
func (this *Pinger) receiver() { defer func() { // Let's recover from panic if r := recover(); r != nil { glog.Errorf("Recovering from panic: %v", r) } this.wg.Done() close(this.reschan) this.Stop() }() rb := make([]byte, ipv4.HeaderLen+icmpHeaderSize+this.Size) var tempDelay time.Duration // how long to sleep on accept failure loop: for { rh, b, _, err := this.rconn.ReadFrom(rb) if err != nil { select { case <-this.done: break loop default: } // Borrowed from go1.3.3/src/pkg/net/http/server.go:1699 if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := this.MaxRTT; tempDelay > max { tempDelay = max } glog.Errorf("Accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } break loop } m, err := icmp.ParseMessage(ProtocolICMP, b) if err != nil { // Hm...bad message? continue } if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { // On Linux we must handle own sent packets. continue } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { glog.Errorf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) continue } // Echo Reply er, ok := m.Body.(*icmp.Echo) if !ok { glog.Errorf("Error type requireing m.Body to *icmp.Echo") continue } if er.ID != int(this.pid) || er.Seq > int(this.seqnum) { glog.Debugf("%d != %d, %d != %d", er.ID, this.pid, er.Seq, this.seqnum) continue } rtt := time.Since(time.Unix(0, int64(binary.BigEndian.Uint64(er.Data)))) src := make(net.IP, len(rh.Src)) copy(src, rh.Src) dst := make(net.IP, len(rh.Dst)) copy(dst, rh.Dst) pr := &PingResult{ TOS: rh.TOS, TTL: rh.TTL, Type: m.Type, Code: m.Code, ID: er.ID, Seq: er.Seq, RTT: rtt, Src: src, Dst: dst, Size: len(er.Data), } this.mu.Lock() this.results[src.String()] = pr this.mu.Unlock() this.reschan <- pr } for ip, pr := range this.results { if pr == nil { this.reschan <- &PingResult{ Src: net.ParseIP(ip), ID: int(this.pid), Seq: int(this.seqnum), Err: fmt.Errorf("%s: Request timed out for seq %d", ip, this.seqnum), } } } }