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  }