From 99adc3db9a43223568607f563cb7a449ff5da852 Mon Sep 17 00:00:00 2001 From: fhltang Date: Wed, 15 Jun 2022 18:27:05 +0100 Subject: [PATCH] Allow TTL to be explicitly set on ICMP packets from icmp prober (#919) This adds an option to the config file to specify the TTL value. If 0 or not specified, in some cases it defaults to 64 and in other cases it defaults to whatever is configured in the operating system. This matches previous behavior. Signed-off-by: Francis Tang --- CONFIGURATION.md | 5 +++++ blackbox.yml | 5 +++++ config/config.go | 10 ++++++++++ config/config_test.go | 8 ++++++++ config/testdata/invalid-icmp-ttl-overflow.yml | 5 +++++ config/testdata/invalid-icmp-ttl.yml | 5 +++++ prober/icmp.go | 18 +++++++++++++++++- 7 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 config/testdata/invalid-icmp-ttl-overflow.yml create mode 100644 config/testdata/invalid-icmp-ttl.yml diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 6de2238..35e8446 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -262,6 +262,11 @@ validate_additional_rrs: # The size of the payload. [ payload_size: ] +# TTL of outbound packets. Value must be in the range [0, 255]. Can be used +# to test reachability of a target within a given number of hops, for example, +# to determine when network routing has changed. +[ ttl: ] + ``` ### diff --git a/blackbox.yml b/blackbox.yml index 8418399..62884bc 100644 --- a/blackbox.yml +++ b/blackbox.yml @@ -42,3 +42,8 @@ modules: - expect: "^:[^ ]+ 001" icmp: prober: icmp + icmp_ttl5: + prober: icmp + timeout: 5s + icmp: + ttl: 5 \ No newline at end of file diff --git a/config/config.go b/config/config.go index 6737963..8094b29 100644 --- a/config/config.go +++ b/config/config.go @@ -77,8 +77,10 @@ var ( } // DefaultICMPProbe set default value for ICMPProbe + DefaultICMPTTL = 64 DefaultICMPProbe = ICMPProbe{ IPProtocolFallback: true, + TTL: DefaultICMPTTL, } // DefaultDNSProbe set default value for DNSProbe @@ -258,6 +260,7 @@ type ICMPProbe struct { SourceIPAddress string `yaml:"source_ip_address,omitempty"` PayloadSize int `yaml:"payload_size,omitempty"` DontFragment bool `yaml:"dont_fragment,omitempty"` + TTL int `yaml:"ttl,omitempty"` } type DNSProbe struct { @@ -405,6 +408,13 @@ func (s *ICMPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { if runtime.GOOS == "windows" && s.DontFragment { return errors.New("\"dont_fragment\" is not supported on windows platforms") } + + if s.TTL < 0 { + return errors.New("\"ttl\" cannot be negative") + } + if s.TTL > 255 { + return errors.New("\"ttl\" cannot exceed 255") + } return nil } diff --git a/config/config_test.go b/config/config_test.go index 432d0d9..e1b57a3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -87,6 +87,14 @@ func TestLoadBadConfigs(t *testing.T) { input: "testdata/invalid-http-request-compression-reject-all-encodings.yml", want: `error parsing config file: invalid configuration "Accept-Encoding: *;q=0.0", "compression: gzip"`, }, + { + input: "testdata/invalid-icmp-ttl.yml", + want: "error parsing config file: \"ttl\" cannot be negative", + }, + { + input: "testdata/invalid-icmp-ttl-overflow.yml", + want: "error parsing config file: \"ttl\" cannot exceed 255", + }, { input: "testdata/invalid-tcp-query-response-regexp.yml", want: `error parsing config file: "Could not compile regular expression" regexp=":["`, diff --git a/config/testdata/invalid-icmp-ttl-overflow.yml b/config/testdata/invalid-icmp-ttl-overflow.yml new file mode 100644 index 0000000..9b3afb1 --- /dev/null +++ b/config/testdata/invalid-icmp-ttl-overflow.yml @@ -0,0 +1,5 @@ +modules: + icmp_test: + prober: icmp + icmp: + ttl: 256 diff --git a/config/testdata/invalid-icmp-ttl.yml b/config/testdata/invalid-icmp-ttl.yml new file mode 100644 index 0000000..7eb4924 --- /dev/null +++ b/config/testdata/invalid-icmp-ttl.yml @@ -0,0 +1,5 @@ +modules: + icmp_test: + prober: icmp + icmp: + ttl: -1 diff --git a/prober/icmp.go b/prober/icmp.go index aa6cad3..1f8432a 100644 --- a/prober/icmp.go +++ b/prober/icmp.go @@ -234,15 +234,31 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr rttStart := time.Now() if icmpConn != nil { + ttl := module.ICMP.TTL + if ttl > 0 { + if c4 := icmpConn.IPv4PacketConn(); c4 != nil { + level.Debug(logger).Log("msg", "Setting TTL (IPv4 unprivileged)", "ttl", ttl) + c4.SetTTL(ttl) + } + if c6 := icmpConn.IPv6PacketConn(); c6 != nil { + level.Debug(logger).Log("msg", "Setting TTL (IPv6 unprivileged)", "ttl", ttl) + c6.SetHopLimit(ttl) + } + } _, err = icmpConn.WriteTo(wb, dst) } else { + ttl := config.DefaultICMPTTL + if module.ICMP.TTL > 0 { + level.Debug(logger).Log("msg", "Overriding TTL (raw IPv4)", "ttl", ttl) + ttl = module.ICMP.TTL + } // Only for IPv4 raw. Needed for setting DontFragment flag. header := &ipv4.Header{ Version: ipv4.Version, Len: ipv4.HeaderLen, Protocol: 1, TotalLen: ipv4.HeaderLen + len(wb), - TTL: 64, + TTL: ttl, Dst: dstIPAddr.IP, Src: srcIP, } -- 2.25.1