Skip to content

Commit

Permalink
Add probe_icmp_reply_ttl_total
Browse files Browse the repository at this point in the history
IPv4 TTL (IPv6 hoplimit) identifies the number of routers used by
an arriving packet. It can be used to detect routing problems.

Signed-off-by: Luiz Angelo Daros de Luca <[email protected]>
  • Loading branch information
luizluca committed Sep 25, 2020
1 parent 10b04fc commit 6bdb1c5
Showing 1 changed file with 42 additions and 12 deletions.
54 changes: 42 additions & 12 deletions prober/icmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
}

registry.MustRegister(durationGaugeVec)
ttlGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "probe_icmp_reply_ttl_total",
Help: "Number of router hops returning from the target",
})
registry.MustRegister(ttlGauge)

ip, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger)
if err != nil {
Expand All @@ -104,6 +109,8 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
// Unprivileged sockets are supported on Darwin and Linux only.
tryUnprivileged := runtime.GOOS == "darwin" || runtime.GOOS == "linux"

var icmpConn *icmp.PacketConn
var v4conn *v4Conn
if ip.IP.To4() == nil {
requestType = ipv6.ICMPTypeEchoRequest
replyType = ipv6.ICMPTypeEchoReply
Expand All @@ -112,7 +119,6 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
srcIP = net.ParseIP("::")
}

var icmpConn *icmp.PacketConn
if tryUnprivileged {
// "udp" here means unprivileged -- not the protocol "udp".
icmpConn, err = icmp.ListenPacket("udp6", srcIP.String())
Expand All @@ -131,6 +137,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
}
}

icmpConn.IPv6PacketConn().SetControlMessage(ipv6.FlagHopLimit, true)
socket = icmpConn
} else {
requestType = ipv4.ICMPTypeEcho
Expand All @@ -143,21 +150,21 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
if module.ICMP.DontFragment {
// If the user has set the don't fragment option we cannot use unprivileged
// sockets as it is not possible to set IP header level options.
icmpConn, err := net.ListenPacket("ip4:icmp", srcIP.String())
netConn, err := net.ListenPacket("ip4:icmp", srcIP.String())
if err != nil {
level.Error(logger).Log("msg", "Error listening to socket", "err", err)
return
}

rc, err := ipv4.NewRawConn(icmpConn)
rc, err := ipv4.NewRawConn(netConn)
if err != nil {
level.Error(logger).Log("msg", "Error creating raw connection", "err", err)
return
}
socket = &v4Conn{c: rc, df: true}
v4conn = &v4Conn{c: rc, df: true}
socket = v4conn
rc.SetControlMessage(ipv4.FlagTTL, true)
} else {
var icmpConn *icmp.PacketConn

if tryUnprivileged {
icmpConn, err = icmp.ListenPacket("udp4", srcIP.String())
if err != nil {
Expand All @@ -174,8 +181,8 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
return
}
}

socket = icmpConn
icmpConn.IPv4PacketConn().SetControlMessage(ipv4.FlagTTL, true)
}
}

Expand Down Expand Up @@ -249,7 +256,24 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
}
level.Info(logger).Log("msg", "Waiting for reply packets")
for {
n, peer, err := socket.ReadFrom(rb)
var n int
var peer net.Addr
var err error
var ttl int

if ip.IP.To4() == nil {
var cm *ipv6.ControlMessage
n, cm, peer, err = icmpConn.IPv6PacketConn().ReadFrom(rb)
ttl = cm.HopLimit
} else {
var cm *ipv4.ControlMessage
if (icmpConn != nil) {
n, cm, peer, err = icmpConn.IPv4PacketConn().ReadFrom(rb)
} else {
n, cm, peer, err = v4conn.ReadFromCM(rb)
}
ttl = cm.TTL
}
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
level.Warn(logger).Log("msg", "Timeout reading from socket", "err", err)
Expand All @@ -274,6 +298,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
}
if bytes.Equal(rb[:n], wb) {
durationGaugeVec.WithLabelValues("rtt").Add(time.Since(rttStart).Seconds())
ttlGauge.Set(float64(ttl))
level.Info(logger).Log("msg", "Found matching reply packet")
return true
}
Expand All @@ -287,18 +312,23 @@ type v4Conn struct {
src net.IP
}

func (c *v4Conn) ReadFrom(b []byte) (int, net.Addr, error) {
h, p, _, err := c.c.ReadFrom(b)
func (c *v4Conn) ReadFromCM(b []byte) (int, *ipv4.ControlMessage, net.Addr, error) {
h, p, cm, err := c.c.ReadFrom(b)
if err != nil {
return 0, nil, err
return 0, nil, nil, err
}

copy(b, p)
n := len(b)
if len(p) < len(b) {
n = len(p)
}
return n, &net.IPAddr{IP: h.Src}, nil
return n, cm, &net.IPAddr{IP: h.Src}, nil
}

func (c *v4Conn) ReadFrom(b []byte) (int, net.Addr, error) {
n, _, peer, err := c.ReadFromCM(b)
return n, peer, err
}

func (d *v4Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
Expand Down

0 comments on commit 6bdb1c5

Please sign in to comment.