func (p *Pinger) recvICMP(conn *icmp.PacketConn, ctx *context, wg *sync.WaitGroup) { for { select { case <-ctx.stop: wg.Done() return default: } bytes := make([]byte, 512) conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) _, ra, err := conn.ReadFrom(bytes) if err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { continue } else { // prevent 2x close in different threads p.mu.Lock() if ctx.err == nil { close(ctx.done) } ctx.err = err p.mu.Unlock() wg.Done() return } } } p.procRecv(bytes, ra, ctx) } }
func (p *Pinger) recvICMP( conn *icmp.PacketConn, recv chan<- *packet, wg *sync.WaitGroup, ) { defer wg.Done() for { select { case <-p.done: return default: bytes := make([]byte, 512) conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) n, _, err := conn.ReadFrom(bytes) if err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { // Read timeout continue } else { close(p.done) return } } } recv <- &packet{bytes: bytes, nbytes: n} } } }
func (p *Pinger) recvICMP(conn *icmp.PacketConn, recv chan<- *packet, ctx *context, wg *sync.WaitGroup) { p.debugln("recvICMP(): Start") for { select { case <-ctx.stop: p.debugln("recvICMP(): <-ctx.stop") wg.Done() p.debugln("recvICMP(): wg.Done()") return default: } bytes := make([]byte, 512) conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) p.debugln("recvICMP(): ReadFrom Start") _, ra, err := conn.ReadFrom(bytes) p.debugln("recvICMP(): ReadFrom End") if err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { p.debugln("recvICMP(): Read Timeout") continue } else { p.debugln("recvICMP(): OpError happen", err) p.mu.Lock() ctx.err = err p.mu.Unlock() p.debugln("recvICMP(): close(ctx.done)") close(ctx.done) p.debugln("recvICMP(): wg.Done()") wg.Done() return } } } p.debugln("recvICMP(): p.recv <- packet") select { case recv <- &packet{bytes: bytes, addr: ra}: case <-ctx.stop: p.debugln("recvICMP(): <-ctx.stop") wg.Done() p.debugln("recvICMP(): wg.Done()") return } } }
func Send_UDP_Ping(c *icmp.PacketConn, targetIP string, concurrent int, timeout int, ch_lock chan struct{}, rCache *ResultCache) { ch_lock <- struct{}{} wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: 1, Data: []byte("HELLO-R-U-THERE"), }, } wb, err := wm.Marshal(nil) if err != nil { return } timeForSend := time.Now() c.SetReadDeadline(timeForSend.Add(time.Duration(timeout) * time.Second)) rCache.New(targetIP) if _, err := c.WriteTo(wb, &net.UDPAddr{IP: net.ParseIP(targetIP)}); err != nil { return } time.Sleep(time.Second) <-ch_lock }
// Sends a single ICMP echo to an IP and returns success and latency information. // Borrowed from BrianBrazil's blackbox exporter func Ping(ip net.IP, maxRTT time.Duration) (success bool, latency time.Duration) { deadline := time.Now().Add(maxRTT) var socket *icmp.PacketConn var err error if isIPv4(ip) { socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") } else if isIPv6(ip) { socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::") } else { log.Errorln("IP did not match any known types?") return } if err != nil { log.Errorf("Error listening to socket: %s", err) return } defer socket.Close() seq := getICMPSequence() pid := os.Getpid() & 0xffff // Build the packet var wm icmp.Message if isIPv4(ip) { wm = icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("poller_exporter"), }, } } else if isIPv6(ip) { wm = icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("poller_exporter"), }, } } else { log.Errorln("IP did not match any known types?") return } wb, err := wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", ip.String(), err) return } sendTime := time.Now() var dst *net.IPAddr dst = &net.IPAddr{IP: ip} if _, err := socket.WriteTo(wb, dst); err != nil { log.Errorf("Error writing to socket for %s: %s", ip.String(), err) return } // Reply should be the same except for the message type. if isIPv4(ip) { wm.Type = ipv4.ICMPTypeEchoReply } else if isIPv6(ip) { wm.Type = ipv6.ICMPTypeEchoReply } else { log.Errorln("IP did not match any known types?") return } wb, err = wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", ip.String(), err) return } rb := make([]byte, 1500) if err := socket.SetReadDeadline(deadline); err != nil { log.Errorf("Error setting socket deadline for %s: %s", ip.String(), err) return } for { n, peer, err := socket.ReadFrom(rb) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Infof("Timeout reading from socket for %s: %s", ip.String(), err) return } log.Errorf("Error reading from socket for %s: %s", ip.String(), err) continue } if peer.String() != ip.String() { continue } if bytes.Compare(rb[:n], wb) == 0 { success = true latency = time.Now().Sub(sendTime) return } } return }
func (l *icmpLoop) runICMPRecv(conn *icmp.PacketConn, proto int) { for { bytes := make([]byte, 512) conn.SetReadDeadline(time.Now().Add(1 * time.Second)) _, addr, err := conn.ReadFrom(bytes) if err != nil { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { continue } else { // TODO: report error and quit loop return } } } ts := time.Now() m, err := icmp.ParseMessage(proto, bytes) if err != nil { continue } // process echo reply only if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { continue } echo, ok := m.Body.(*icmp.Echo) if !ok { continue } id := requestID{ addr: addr.String(), proto: proto, id: echo.ID, seq: echo.Seq, } l.mutex.Lock() ctx := l.requests[id] if ctx != nil { delete(l.requests, id) } l.mutex.Unlock() // no return context available for echo reply -> handle next message if ctx == nil { continue } ctx.result <- requestResult{ packet: packet{ ts: ts, addr: addr, Type: m.Type, Code: m.Code, Checksum: m.Checksum, Echo: *echo, }, } } }
func probeICMP(target string, w http.ResponseWriter, module Module) (success bool) { var ( socket *icmp.PacketConn requestType icmp.Type replyType icmp.Type fallbackProtocol string ) deadline := time.Now().Add(module.Timeout) // Defaults to IPv4 to be compatible with older versions if module.ICMP.Protocol == "" { module.ICMP.Protocol = "icmp" } // In case of ICMP prefer IPv6 by default if module.ICMP.Protocol == "icmp" && module.ICMP.PreferredIpProtocol == "" { module.ICMP.PreferredIpProtocol = "ip6" } if module.ICMP.Protocol == "icmp4" { module.ICMP.PreferredIpProtocol = "ip4" fallbackProtocol = "" } else if module.ICMP.Protocol == "icmp6" { module.ICMP.PreferredIpProtocol = "ip6" fallbackProtocol = "" } else if module.ICMP.PreferredIpProtocol == "ip6" { fallbackProtocol = "ip4" } else { fallbackProtocol = "ip6" } ip, err := net.ResolveIPAddr(module.ICMP.PreferredIpProtocol, target) if err != nil && fallbackProtocol != "" { ip, err = net.ResolveIPAddr(fallbackProtocol, target) } if err != nil { log.Errorf("Error resolving address %s: %s", target, err) } if ip.IP.To4() == nil { requestType = ipv6.ICMPTypeEchoRequest replyType = ipv6.ICMPTypeEchoReply socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::") fmt.Fprintf(w, "probe_ip_protocol 6\n") } else { requestType = ipv4.ICMPTypeEcho replyType = ipv4.ICMPTypeEchoReply socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") fmt.Fprintf(w, "probe_ip_protocol 4\n") } if err != nil { log.Errorf("Error listening to socket: %s", err) return } defer socket.Close() if err != nil { log.Errorf("Error resolving address %s: %s", target, err) return } seq := getICMPSequence() pid := os.Getpid() & 0xffff wm := icmp.Message{ Type: requestType, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("Prometheus Blackbox Exporter"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", target, err) return } if _, err := socket.WriteTo(wb, ip); err != nil { log.Errorf("Error writing to socker for %s: %s", target, err) return } // Reply should be the same except for the message type. wm.Type = replyType wb, err = wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", target, err) return } rb := make([]byte, 1500) if err := socket.SetReadDeadline(deadline); err != nil { log.Errorf("Error setting socket deadline for %s: %s", target, err) return } for { n, peer, err := socket.ReadFrom(rb) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Infof("Timeout reading from socket for %s: %s", target, err) return } log.Errorf("Error reading from socket for %s: %s", target, err) continue } if peer.String() != ip.String() { continue } if replyType == ipv6.ICMPTypeEchoReply { // Clear checksum to make comparison succeed. rb[2] = 0 rb[3] = 0 } if bytes.Compare(rb[:n], wb) == 0 { success = true return } } }