Allow TTL to be explicitly set on ICMP packets from icmp prober (#919)
authorfhltang <francis.tang@gmail.com>
Wed, 15 Jun 2022 17:27:05 +0000 (18:27 +0100)
committerGitHub <noreply@github.com>
Wed, 15 Jun 2022 17:27:05 +0000 (11:27 -0600)
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 <francis.tang@gmail.com>
CONFIGURATION.md
blackbox.yml
config/config.go
config/config_test.go
config/testdata/invalid-icmp-ttl-overflow.yml [new file with mode: 0644]
config/testdata/invalid-icmp-ttl.yml [new file with mode: 0644]
prober/icmp.go

index 6de22380b2009ed174afc49528ec5c40ed2b0853..35e8446fe68814a1a2d9439f986edcd3a24f2fe4 100644 (file)
@@ -262,6 +262,11 @@ validate_additional_rrs:
 # The size of the payload.
 [ payload_size: <int> ]
 
+# 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: <int> ]
+
 ```
 
 ### <grpc_probe>
index 8418399ea451c9ba189b437469be3a59ff8373b0..62884bcb63bd36dd4b3035522347aeba07f84fb3 100644 (file)
@@ -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
index 6737963bbdb694d6ae2340e83ce324e45e0f2fba..8094b29e16bb960cd9a4b9e96d61ec73218b61cc 100644 (file)
@@ -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
 }
 
index 432d0d9aa95d8341adf0cca9681ba9d4710504fe..e1b57a3e0314b202ec22567d8d1459af5b7ee70c 100644 (file)
@@ -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 (file)
index 0000000..9b3afb1
--- /dev/null
@@ -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 (file)
index 0000000..7eb4924
--- /dev/null
@@ -0,0 +1,5 @@
+modules:
+  icmp_test:
+    prober: icmp
+    icmp:
+      ttl: -1
index aa6cad345e8620ea30e6a90b7cdb3894945e8c03..1f8432a94ca2974c4f1c6aae807ff92d9d5c18ba 100644 (file)
@@ -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,
                }