本文将通过Blackbox_Exporter的ICMP探测功能的源代码,逐步分析其各个部分的实现逻辑及其在实际应用中的作用。我们将从代码的基本结构入手,详细解释每一行的功能,并为你提供如何在实际环境中使用这一功能的思路。
目录
目标与背景
这个函数 ProbeICMP 旨在通过发送ICMP请求包(Echo Request)并等待ICMP响应包(Echo Reply),来检测目标主机的连通性。代码利用了Go语言的标准库以及 prometheus 用于性能监控。
函数定义
func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) (success bool)
ctx: 上下文对象,通常用于控制超时和取消操作。target: 目标主机地址。module: 配置模块,包含ICMP探测的相关参数。registry: Prometheus监控注册表,用于记录和导出性能指标。logger: 日志记录器,记录操作过程中的各种信息。
1. 定义局部变量
var (
requestType icmp.Type
replyType icmp.Type
icmpConn *icmp.PacketConn
v4RawConn *ipv4.RawConn
hopLimitFlagSet bool = true
durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "probe_icmp_duration_seconds",
Help: "Duration of icmp request by phase",
}, []string{"phase"})
hopLimitGauge = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "probe_icmp_reply_hop_limit",
Help: "Replied packet hop limit (TTL for ipv4)",
})
)
requestType和replyType:分别用于存储请求和响应的ICMP类型。icmpConn:存储用于发送和接收ICMP包的连接。v4RawConn:IPv4原始连接,用于低级别的ICMP操作。hopLimitFlagSet:标记是否成功设置了TTL(IPv4)或Hop Limit(IPv6)。durationGaugeVec:Prometheus的计量向量,用于记录ICMP请求各阶段的时长。hopLimitGauge:Prometheus的计量对象,用于记录目标主机回复包的Hop Limit。
2. 注册Prometheus监控指标
for _, lv := range []string{"resolve", "setup", "rtt"} {
durationGaugeVec.WithLabelValues(lv)
}
registry.MustRegister(durationGaugeVec)
durationGaugeVec记录三个阶段的时长:解析(resolve)、设置(setup)、往返时延(RTT)。MustRegister方法确保指标被成功注册到Prometheus注册表。
3. 解析目标地址并选择协议
dstIPAddr, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger)
if err != nil {
logger.Error("Error resolving address", "err", err)
return false
}
durationGaugeVec.WithLabelValues("resolve").Add(lookupTime)
chooseProtocol函数决定是使用IPv4还是IPv6,并解析目标地址。如果解析失败,返回false。- 解析时间(
lookupTime)记录到durationGaugeVec中。
4. 设置源IP地址
var srcIP net.IP
if len(module.ICMP.SourceIPAddress) > 0 {
if srcIP = net.ParseIP(module.ICMP.SourceIPAddress); srcIP == nil {
logger.Error("Error parsing source ip address", "srcIP", module.ICMP.SourceIPAddress)
return false
}
logger.Info("Using source address", "srcIP", srcIP)
}
- 如果用户在配置中指定了源IP地址,将其解析并使用;否则,使用默认的源地址。
5. 创建ICMP连接(IPv4/IPv6)
根据目标主机是IPv4还是IPv6,选择合适的方式创建ICMP连接。
对于IPv6:
icmpConn, err = icmp.ListenPacket("ip6:ipv6-icmp", srcIP.String())
对于IPv4:
icmpConn, err = icmp.ListenPacket("ip4:icmp", srcIP.String())
icmp.ListenPacket创建ICMP连接。对于Linux和MacOS,可以使用未经授权的套接字来创建UDP连接,减少权限要求。
6. 构建ICMP请求包
body := &icmp.Echo{
ID: icmpID,
Seq: int(getICMPSequence()),
Data: data,
}
wm := icmp.Message{
Type: requestType,
Code: 0,
Body: body,
}
- 构建ICMP回显请求(Echo Request)包,设置唯一的ID和序列号。
- 通过
icmp.Message类型构造请求消息。
7. 发送ICMP请求包
_, err = icmpConn.WriteTo(wb, dst)
- 将构建好的ICMP请求包写入到套接字中并发送到目标地址。
8. 等待并接收ICMP响应包
n, cm, peer, err = icmpConn.IPv6PacketConn().ReadFrom(rb)
- 等待接收ICMP响应包。
- 如果收到的包和发送的包匹配,且目标地址与期望地址一致,则返回成功。
9. 设置和读取TTL或Hop Limit
在IPv4和IPv6中,TTL和Hop Limit分别用于表示数据包的跳数限制,这些信息对于网络诊断非常重要。
if cm != nil && hopLimitFlagSet {
hopLimit = float64(cm.TTL) // 对于IPv4
}
- 通过设置控制消息来获取TTL或Hop Limit,并将其记录在Prometheus监控中。
10. 完整的ICMP探测逻辑
if bytes.Equal(rb[:n], wb) {
durationGaugeVec.WithLabelValues("rtt").Add(time.Since(rttStart).Seconds())
if hopLimit >= 0 {
hopLimitGauge.Set(hopLimit)
registry.MustRegister(hopLimitGauge)
}
logger.Info("Found matching reply packet")
return true
}
- 如果收到的响应包与发送的请求包相匹配,则计算往返时延(RTT)并记录TTL或Hop Limit。
总结
本文详细解析了一个Go语言实现的ICMP探测函数,通过使用Prometheus进行性能监控,能够监控请求的各个阶段(解析、设置、RTT)以及目标主机的TTL或Hop Limit。该函数在实际应用中非常有用,尤其是在网络故障排查、性能监控和自动化监控工具中。通过对代码的逐步分析,你可以更清晰地理解ICMP探测和性能监控的实现方式,以及如何根据自己的需求进行扩展和调整。
415

被折叠的 条评论
为什么被折叠?



