github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/prober/icmp/icmp_impl.go (about) 1 package icmp 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "time" 8 9 "github.com/go-kit/kit/log" //nolint:staticcheck // TODO(mem): replace in BBE 10 "github.com/go-kit/kit/log/level" //nolint:staticcheck // TODO(mem): replace in BBE 11 ping "github.com/prometheus-community/pro-bing" 12 "github.com/prometheus/client_golang/prometheus" 13 ) 14 15 func probeICMP(ctx context.Context, target string, module Module, registry *prometheus.Registry, logger log.Logger) (success bool, duration float64) { 16 var ( 17 durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ 18 Name: "probe_icmp_duration_seconds", 19 Help: "Duration of icmp request by phase", 20 }, []string{"phase"}) 21 22 durationMaxGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 23 Name: "probe_icmp_duration_rtt_max_seconds", 24 Help: "Maximum duration of round trip time phase", 25 }) 26 27 durationMinGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 28 Name: "probe_icmp_duration_rtt_min_seconds", 29 Help: "Minimum duration of round trip time phase", 30 }) 31 32 durationStddevGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 33 Name: "probe_icmp_duration_rtt_stddev_seconds", 34 Help: "Standard deviation of round trip time phase", 35 }) 36 37 packetsSentGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 38 Name: "probe_icmp_packets_sent_count", 39 Help: "Number of ICMP packets sent", 40 }) 41 42 packetsReceivedGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 43 Name: "probe_icmp_packets_received_count", 44 Help: "Number of ICMP packets received", 45 }) 46 47 hopLimitGauge = prometheus.NewGauge(prometheus.GaugeOpts{ 48 Name: "probe_icmp_reply_hop_limit", 49 Help: "Replied packet hop limit (TTL for ipv4)", 50 }) 51 ) 52 53 for _, lv := range []string{"resolve", "setup", "rtt"} { 54 durationGaugeVec.WithLabelValues(lv) 55 } 56 57 registry.MustRegister(durationGaugeVec) 58 registry.MustRegister(durationMaxGauge) 59 registry.MustRegister(durationMinGauge) 60 registry.MustRegister(durationStddevGauge) 61 registry.MustRegister(packetsSentGauge) 62 registry.MustRegister(packetsReceivedGauge) 63 64 dstIPAddr, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, int(module.MaxResolveRetries), registry, logger) 65 if err != nil { 66 _ = level.Error(logger).Log("msg", "Error resolving address", "err", err) 67 return false, 0 68 } 69 70 durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) 71 duration += lookupTime 72 73 pinger := ping.New(dstIPAddr.String()) 74 75 pinger.SetPrivileged(module.Privileged) 76 77 if err := pinger.Resolve(); err != nil { 78 // This should never happen, the address is already resolved. 79 _ = level.Error(logger).Log("msg", "Error resolving address", "err", err) 80 return false, 0 81 } 82 83 pinger.Timeout = module.Timeout 84 85 pinger.SetLogger(icmpLogger{logger}) 86 87 var ( 88 setupStart time.Time 89 setupDone bool 90 ) 91 92 pinger.OnSetup = func() { 93 if !setupDone { 94 setupDuration := time.Since(setupStart).Seconds() 95 durationGaugeVec.WithLabelValues("setup").Add(setupDuration) 96 duration += setupDuration 97 setupDone = true 98 } 99 100 _ = level.Info(logger).Log("msg", "Using source address", "srcIP", pinger.Source) 101 } 102 103 pinger.OnSend = func(pkt *ping.Packet) { 104 _ = level.Info(logger).Log("msg", "Creating ICMP packet", "seq", strconv.Itoa(pkt.Seq)) 105 _ = level.Info(logger).Log("msg", "Waiting for reply packets") 106 } 107 108 pinger.OnRecv = func(pkt *ping.Packet) { 109 if pkt.Seq == 0 && pkt.TTL >= 0 { 110 registry.MustRegister(hopLimitGauge) 111 hopLimitGauge.Set(float64(pkt.TTL)) 112 } 113 114 _ = level.Info(logger).Log("msg", "Found matching reply packet", "seq", strconv.Itoa(pkt.Seq)) 115 } 116 117 pinger.OnDuplicateRecv = func(pkt *ping.Packet) { 118 _ = level.Info(logger).Log("msg", "Duplicate packet received", "seq", strconv.Itoa(pkt.Seq)) 119 } 120 121 pinger.OnFinish = func(stats *ping.Statistics) { 122 durationGaugeVec.WithLabelValues("rtt").Set(stats.AvgRtt.Seconds()) 123 duration += stats.AvgRtt.Seconds() 124 durationMaxGauge.Set(stats.MaxRtt.Seconds()) 125 durationMinGauge.Set(stats.MinRtt.Seconds()) 126 durationStddevGauge.Set(stats.StdDevRtt.Seconds()) 127 packetsSentGauge.Set(float64(stats.PacketsSent)) 128 packetsReceivedGauge.Set(float64(stats.PacketsRecv)) 129 _ = level.Info(logger).Log("msg", "Probe finished", "packets_sent", stats.PacketsSent, "packets_received", stats.PacketsRecv) 130 } 131 132 pinger.SetDoNotFragment(module.ICMP.DontFragment) 133 134 if module.ICMP.PayloadSize != 0 { 135 pinger.Size = module.ICMP.PayloadSize 136 } 137 138 pinger.Count = int(module.PacketCount) 139 140 pinger.Interval = 50 * time.Millisecond 141 142 pinger.RecordRtts = false 143 144 pinger.Source = module.ICMP.SourceIPAddress 145 146 setupStart = time.Now() 147 148 _ = level.Info(logger).Log("msg", "Creating socket") 149 150 if err := pinger.RunWithContext(ctx); err != nil { 151 _ = level.Info(logger).Log("msg", "failed to run ping", "err", err.Error()) 152 return false, 0 153 } 154 155 return pinger.PacketsSent >= int(module.ReqSuccessCount) && pinger.PacketsRecv >= int(module.ReqSuccessCount), duration 156 } 157 158 type icmpLogger struct { 159 logger log.Logger 160 } 161 162 func (l icmpLogger) Fatalf(format string, v ...any) { 163 _ = level.Error(l.logger).Log("msg", fmt.Sprintf(format, v...)) 164 } 165 166 func (l icmpLogger) Errorf(format string, v ...any) { 167 _ = level.Error(l.logger).Log("msg", fmt.Sprintf(format, v...)) 168 } 169 170 func (l icmpLogger) Warnf(format string, v ...any) { 171 _ = level.Warn(l.logger).Log("msg", fmt.Sprintf(format, v...)) 172 } 173 174 func (l icmpLogger) Infof(format string, v ...any) { 175 _ = level.Info(l.logger).Log("msg", fmt.Sprintf(format, v...)) 176 } 177 178 func (l icmpLogger) Debugf(format string, v ...any) { 179 _ = level.Debug(l.logger).Log("msg", fmt.Sprintf(format, v...)) 180 }