Skip to content

Network Path


The NetPath collector actively probes network paths from the DataKit node. It supports TCP, UDP, and ICMP targets. Targets can be configured statically or discovered dynamically by local traffic sources such as datakit-ebpf. NetPath currently supports Linux and macOS; Windows is not supported.

Each execution produces one netpath record in the Network (N) category, including source and destination context, end-to-end latency, ICMP loss metrics, and per-hop results from multiple traceroute runs.

Configuration

Go to the conf.d/samples directory under the DataKit installation directory, copy netpath.conf.sample, and rename it to netpath.conf:

[[inputs.netpath]]
  ## Default protocol for static targets: tcp/udp/icmp/auto.
  protocol = "tcp"

  ## Default static target probe interval.
  interval = "60s"

  ## Per-probe timeout.
  timeout = "1s"

  ## Independent end-to-end probes. These do not inspect intermediate hops.
  e2e_queries = 10

  ## Maximum traceroute TTL and number of complete traceroute runs. The
  ## effective TTL limit is 60 for TCP/ICMP and 255 for Linux UDP.
  max_ttl = 30
  traceroute_queries = 3

  ## Static network path targets.
  # [[inputs.netpath.targets]]
  #   name = "api-gateway"
  #   target = "api.example.com"
  #   port = 443
  #   protocol = "tcp"
  #   interval = "60s"
  #   timeout = "1s"
  #   max_ttl = 30
  #   traceroute_queries = 3
  #   e2e_queries = 10
  #   [inputs.netpath.targets.tags]
  #     service = "api"

  ## Dynamic targets discovered from local traffic sources such as datakit-ebpf.
  [inputs.netpath.dynamic]
    enabled = true
    ## Optional only when both client and accepted server address are loopback.
    ## All other requests require a token.
    ## Configure the same token for datakit-ebpf, which sends it in this header:
    ## X-Datakit-Netpath-Token: <token>
    token = ""

    ## auto/tcp/udp/icmp. auto uses candidate protocol and falls back to tcp for
    ## candidates with a port, icmp for address-only candidates. Traceroute
    ## requires raw socket permission; UDP traceroute is supported on Linux.
    protocol = "auto"

    ## Dynamic candidates are deduplicated and kept for ttl. The scheduler runs
    ## each candidate at interval while it is alive.
    contexts_limit = 5000
    ## Total estimated bytes retained by stored and currently running contexts.
    contexts_bytes_limit = 67108864
    ttl = "50m"
    interval = "20m"
    flush_interval = "10s"
    max_per_minute = 150
    workers = 4
    timeout = "1s"
    ## The effective TTL limit is 60 for TCP/ICMP and 255 for Linux UDP.
    max_ttl = 30
    traceroute_queries = 3
    e2e_queries = 10
    ## Maximum concurrent candidate admissions waiting for the store lock.
    input_queue = 1000
    process_queue = 1000
    max_tests_per_request = 1000
    max_body_bytes = 1048576

    ## Set true to allow address-only candidates that do not carry a domain or
    ## hostname. Keeping this false reduces noisy high-cardinality path tests.
    monitor_ip_without_domain = false

    ## Exclude candidate rules. Conditions inside one rule are ANDed; values in
    ## the same condition are ORed. A candidate matching any rule is dropped
    ## before entering the scheduler. Hostname destinations are checked again
    ## against destination host/CIDR rules after every DNS lookup and before
    ## any probe packet is sent.
    # [[inputs.netpath.dynamic.filters]]
    #   name = "ignore-kube-system"
    #   namespaces = ["kube-system"]
    #
    # [[inputs.netpath.dynamic.filters]]
    #   name = "ignore-private-db"
    #   dest_cidrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
    #   ports = [5432, 6379]

  ## Optional reverse DNS enrichment for destination and hop IPs. Disabled by
  ## default to avoid adding DNS lookup latency to every traceroute result.
  [inputs.netpath.reverse_dns]
    enabled = false
    timeout = "500ms"
    cache_ttl = "10m"
    cache_size = 4096

  [inputs.netpath.tags]
    # some_tag = "some_value"

After configuration, restart DataKit.

Enable the collector with a ConfigMap, or add netpath to ENV_DEFAULT_ENABLED_INPUTS and configure it with environment variables:

  • ENV_INPUT_NETPATH_PROTOCOL

    input.conf: protocol

  • ENV_INPUT_NETPATH_INTERVAL

    Collect interval

    Type: Duration

    input.conf: interval

    Default: 10s

  • ENV_INPUT_NETPATH_TIMEOUT

    Timeout

    Type: Duration

    input.conf: timeout

    Default: 30s

  • ENV_INPUT_NETPATH_MAX_TTL

    input.conf: max_ttl

  • ENV_INPUT_NETPATH_TRACEROUTE_QUERIES

    input.conf: traceroute_queries

  • ENV_INPUT_NETPATH_E2E_QUERIES

    input.conf: e2e_queries

  • ENV_INPUT_NETPATH_TAGS

    Customize tags. If there is a tag with the same name in the configuration file, it will be overwritten

    Type: Map

    input.conf: tags

    Example: tag1=value1,tag2=value2

  • ENV_INPUT_NETPATH_DYNAMIC_ENABLED

    Type: Boolean

    input.conf: dynamic.enabled

  • ENV_INPUT_NETPATH_DYNAMIC_PROTOCOL

    input.conf: dynamic.protocol

  • ENV_INPUT_NETPATH_DYNAMIC_TOKEN

    Type: String

    input.conf: dynamic.token

  • ENV_INPUT_NETPATH_DYNAMIC_TTL

    Type: Duration

    input.conf: dynamic.ttl

  • ENV_INPUT_NETPATH_DYNAMIC_INTERVAL

    Type: Duration

    input.conf: dynamic.interval

  • ENV_INPUT_NETPATH_DYNAMIC_FLUSH_INTERVAL

    Type: Duration

    input.conf: dynamic.flush_interval

  • ENV_INPUT_NETPATH_DYNAMIC_MAX_PER_MINUTE

    Type: Int

    input.conf: dynamic.max_per_minute

  • ENV_INPUT_NETPATH_DYNAMIC_WORKERS

    Type: Int

    input.conf: dynamic.workers

  • ENV_INPUT_NETPATH_DYNAMIC_CONTEXTS_LIMIT

    Type: Int

    input.conf: dynamic.contexts_limit

  • ENV_INPUT_NETPATH_DYNAMIC_CONTEXTS_BYTES_LIMIT

    Type: Int

    input.conf: dynamic.contexts_bytes_limit

  • ENV_INPUT_NETPATH_DYNAMIC_E2E_QUERIES

    Type: Int

    input.conf: dynamic.e2e_queries

  • ENV_INPUT_NETPATH_DYNAMIC_MONITOR_IP_WITHOUT_DOMAIN

    Type: Boolean

    input.conf: dynamic.monitor_ip_without_domain

Static Targets

Configure static targets with [[inputs.netpath.targets]]:

[[inputs.netpath.targets]]
  name = "api-gateway"
  target = "api.example.com"
  port = 443
  protocol = "tcp"
  interval = "60s"
  timeout = "1s"
  max_ttl = 30
  traceroute_queries = 3
  e2e_queries = 10

  [inputs.netpath.targets.tags]
    service = "api"

With the auto protocol, targets with a port use TCP and targets without a port use ICMP. UDP is currently supported only on Linux and requires permission to receive raw ICMP responses. The effective max_ttl limit is 60 for TCP/ICMP traceroute and 255 for Linux UDP traceroute.

traceroute_queries is the number of complete traceroute executions. Each execution independently probes from TTL 1 until it reaches the destination or max_ttl, and produces one message.runs[] element; it is not a retry count for the same TTL. For a domain target, every run performs its own DNS lookup. The actual target is stored in runs[].destination.ip_address, and one test is not guaranteed to cover every IP of the domain.

e2e_queries is the number of independent end-to-end probes and defaults to 10. E2E does not inspect intermediate hops and runs concurrently with traceroute: TCP uses connection responses, ICMP uses Echo Reply, and Linux UDP uses destination ICMP responses. E2E packets use a normal end-to-end IP TTL rather than the traceroute max_ttl. A domain is resolved independently for E2E, and the selected address is stored in e2e_dest_ip; it may differ from message.runs[].destination.ip_address. UDP application silence is counted in e2e_unknown rather than being treated directly as loss.

Dynamic Targets

The dynamic target endpoint is POST /v1/netpath/candidates. dynamic.enabled is enabled by default. If the DataKit HTTP listener is reachable outside the host, configure a non-empty dynamic.token; clients must send the same token in the X-Datakit-Netpath-Token header.

Dynamic candidates with a hostname are deduplicated and probed by hostname; otherwise the IP is used. Different observed IPs for the same non-NAT hostname share one scheduled task, which runs at interval while it remains alive for ttl; translated destinations additionally retain the original destination tuple in their identity. Dynamic context, queue, rate, and worker limits do not consume or drop configured static targets. Keep monitor_ip_without_domain = false where possible, and configure dynamic.filters before increasing traffic volume to exclude unwanted namespaces, networks, or ports.

For hostname candidates, destination host and CIDR filters are re-evaluated against every DNS result before traceroute or E2E sends a probe packet. Candidate identity fields are limited to 1024 bytes each and 4096 bytes in total per test, including request-level defaults. Request-level and test-level tags are limited to 64 combined tags per candidate. Tag keys are limited to 128 bytes and values to 1024 bytes. Custom tags cannot override fixed contract fields such as traceroute_status, traceroute_fail_type, or endpoint tuple tags.

Dynamic discovery from datakit-ebpf also requires:

[inputs.ebpf]
  network_path_enabled = true
  network_path_api = "http://127.0.0.1:9529/v1/netpath/candidates"
  network_path_token = ""

Reverse DNS

Reverse DNS is disabled by default. When enabled, DataKit performs PTR lookups for the destination and responsive hop IPs and uses a TTL cache to limit repeated queries:

[inputs.netpath.reverse_dns]
  enabled = true
  timeout = "500ms"
  cache_ttl = "10m"
  cache_size = 4096

Data Model

Result tags identify the task, source, destination, and path. The main tags are:

  • task: path_key, task_name, task_source, origin, run_type, protocol;
  • flow tuple: src_ip, src_port, dst_ip, and dst_port, plus dst_nat_ip and dst_nat_port when DNAT occurs;
  • endpoint context: dst_domain, source_host, source_service, source_process, source_container_id, src_cloud_provider, and dst_cloud_provider;
  • actual probe route: probe_source_ip, probe_gateway_ip, probe_interface, probe_netns;
  • status: traceroute_protocol, traceroute_status, traceroute_fail_type, and e2e_status.

Source and destination tags do not depend on per-hop results. Their tuple names align with NetFlow: dst_* is the original destination and dst_nat_* is the translated probe destination. Unknown ports use "*"; a missing source IP falls back to probe_source_ip. The optional endpoint *_cloud_provider tags provide cloud-provider context for searching.

path_key is a stable logical-path identifier generated by DataKit in the form np-v1-<32 lowercase hexadecimal digits>. Use it to deduplicate list rows and associate history for the same path. Execution time, probe status, latency, and traceroute-hop changes do not change it, and custom tags cannot override it. NetPath does not upload branch_key; derive actual route branches and their changes from message.runs[].hops[].

Use traceroute_status (reached, partial, or failed) for path completion. It describes whether traceroute reached the destination, not end-to-end quality.

Use the independent e2e_* fields as the canonical end-to-end quality fields:

  • e2e_dest_ip: the independently resolved IPv4 address used by E2E probes;
  • e2e_packets_sent, e2e_packets_received, and e2e_unknown: sent, responded, and ambiguous probe counts;
  • e2e_probe_loss_percent: no-response percentage among determinate probes; unknown outcomes are excluded;
  • e2e_rtt_avg, e2e_rtt_min, and e2e_rtt_max: end-to-end RTT in microseconds;
  • e2e_rtt_variation_avg and e2e_rtt_variation_max: absolute RTT differences between consecutive successful probes in send order, in microseconds.

The frontend should use e2e_rtt_avg for whole-path latency and e2e_probe_loss_percent for the end-to-end probe no-response percentage. e2e_status is independent of traceroute_status. TCP connection refused/RST proves destination reachability and is counted as received. e2e_probe_loss_percent is not the actual packet-loss rate of an intermediate device.

Per-hop Path

The complete path is stored as standard JSON in the message field:

{
  "runs": [
    {
      "run_id": "1",
      "destination": {
        "ip_address": "8.8.8.8",
        "port": 443,
        "reverse_dns": ["dns.google"]
      },
      "hops": [
        {
          "ttl": 1,
          "ip_address": "10.0.0.1",
          "reverse_dns": ["gateway.local"],
          "rtt": 0.8315,
          "reachable": true
        },
        {
          "ttl": 2,
          "reachable": false
        },
        {
          "ttl": 3,
          "ip_address": "8.8.8.8",
          "rtt": 12.45,
          "reachable": true,
          "asn": 15169,
          "as_name": "GOOGLE",
          "as_prefix": "8.8.8.0/24",
          "cloud_provider": "gcp"
        }
      ]
    }
  ],
  "hop_count": {
    "avg": 3,
    "min": 3,
    "max": 3
  }
}
Field Type Description
runs[].run_id string Sequential traceroute run ID inside message.
runs[].destination.ip_address string Actual destination IPv4 used by this run.
runs[].destination.port uint16 TCP or UDP destination port.
runs[].destination.reverse_dns string[] Hostname used for a domain target.
runs[].hops[].ttl int Hop number.
runs[].hops[].reachable bool Whether this TTL probe received a response.
runs[].hops[].ip_address string Hop IP. Omitted for non-responsive hops; "*" is not used.
runs[].hops[].reverse_dns string[] Optional reverse DNS names.
runs[].hops[].rtt number Round-trip time of this TTL probe in milliseconds. It is not the latency between adjacent hops.
runs[].hops[].asn uint64 ASN added for public IPs by Kodo using a local offline database.
runs[].hops[].as_name string Optional autonomous-system organization name.
runs[].hops[].as_prefix string Optional autonomous-system network prefix.
runs[].hops[].cloud_provider string Optional cloud provider added by Kodo using a local IP ownership database.
hop_count.avg/min/max number Hop-count statistics across traceroute runs.

ASN and cloud-provider fields are optional server-side enrichment. They are absent for private, CGNAT, and non-responsive hops, when the database is not deployed, or when no record matches. Enrichment does not send hop IPs to a third-party service and does not infer a cloud provider from as_name.

If a probe fails before producing a path, message can contain error text instead of JSON. Handle it together with traceroute_status=failed, traceroute_fail_type, and traceroute_fail_reason.

Warning

reachable=false only means that a response was not received for this TTL probe. Device policy or ICMP rate limiting can cause this, so it must not be interpreted directly as actual packet loss at that hop. Per-hop probe_count, response_count, and timeout_count are not uploaded.

Logging

netpath

Tags & Fields Description
dst_cloud_provider
(tag)
Cloud provider associated with the destination endpoint when available.
dst_domain
(tag)
Configured or discovered destination domain.
dst_ip
(tag)
Original destination IP observed by the traffic source, aligned with netflow.
dst_nat_ip
(tag)
Translated destination IP used by the probe when DNAT is present, aligned with netflow.
dst_nat_port
(tag)
Translated destination port used by the probe when DNAT is present, aligned with netflow.
dst_port
(tag)
Original destination port; * when unavailable, aligned with netflow.
e2e_status
(tag)
End-to-end status: reached, partial, unknown, or failed.
namespace
(tag)
Source namespace.
netns
(tag)
Linux network namespace.
origin
(tag)
Original source name, such as config or ebpf_netflow.
path_key
(tag)
Stable logical path key used to group probe history.
probe_gateway_ip
(tag)
Next-hop gateway selected by the route used for the active probe.
probe_interface
(tag)
Outbound interface selected by the route used for the active probe.
probe_interface_mac
(tag)
MAC address of the outbound interface used for the active probe.
probe_netns
(tag)
Network namespace of the active probe.
probe_source_ip
(tag)
Source IP selected by the route used for the active probe.
protocol
(tag)
Probe protocol.
run_type
(tag)
Run type: scheduled, on_demand, or dynamic.
source_container_id
(tag)
Source container ID.
source_host
(tag)
Source host that discovered the candidate.
source_process
(tag)
Source process name.
source_service
(tag)
Source service name.
src_cloud_provider
(tag)
Cloud provider associated with the source endpoint when available.
src_ip
(tag)
Source IP observed by the traffic source, aligned with netflow.
src_port
(tag)
Source port observed by the traffic source; * when unavailable, aligned with netflow.
task_name
(tag)
Probe task name.
task_source
(tag)
Task source: local, server, or dynamic.
traceroute_fail_type
(tag)
Normalized traceroute failure type, such as timeout, dns_error, permission, protocol_unsupported, target_unreachable, or runner_error.
traceroute_protocol
(tag)
Protocol used to generate traceroute probes.
traceroute_status
(tag)
Traceroute status, such as reached, partial, or failed.
dst_reverse_dns Reverse DNS name of destination IP when reverse_dns is enabled.
Type: string | (string)
Unit: N/A
duration Probe execution duration.
Type: int | (gauge)
Unit: time,μs
e2e_dest_ip IPv4 address used by the end-to-end probes.
Type: string | (string)
Unit: N/A
e2e_fail_reason End-to-end probe setup or execution failure reason.
Type: string | (string)
Unit: N/A
e2e_packets_received Number of recognizable destination responses received.
Type: int | (gauge)
Unit: count
e2e_packets_sent Number of end-to-end probes sent.
Type: int | (gauge)
Unit: count
e2e_probe_loss_percent Percentage of determinate probes without a recognizable response; ambiguous UDP silence is excluded.
Type: float | (gauge)
Unit: percent,percent
e2e_queries Configured number of independent end-to-end probes.
Type: int | (gauge)
Unit: count
e2e_rtt_avg Average end-to-end round-trip time across received responses.
Type: float | (gauge)
Unit: time,μs
e2e_rtt_max Maximum end-to-end round-trip time.
Type: float | (gauge)
Unit: time,μs
e2e_rtt_min Minimum end-to-end round-trip time.
Type: float | (gauge)
Unit: time,μs
e2e_rtt_variation_avg Average absolute RTT difference between consecutive successful probes.
Type: float | (gauge)
Unit: time,μs
e2e_rtt_variation_max Maximum absolute RTT difference between consecutive successful probes.
Type: float | (gauge)
Unit: time,μs
e2e_rtt_variation_samples Number of consecutive successful probe pairs used for RTT variation.
Type: int | (gauge)
Unit: count
e2e_tcp_connection_refused Number of TCP probes answered with connection refusal; these still prove endpoint reachability.
Type: int | (gauge)
Unit: count
e2e_unknown Number of probes with ambiguous outcomes, such as a silent UDP application.
Type: int | (gauge)
Unit: count
hop_count Number of traceroute hops.
Type: int | (gauge)
Unit: count
max_ttl Effective traceroute max TTL after protocol limits.
Type: int | (gauge)
Unit: count
message Normalized traceroute JSON, or a failure message when no path is available.
Type: string | (string)
Unit: N/A
scheduled_at Scheduled execution time in Unix microseconds.
Type: int | (gauge)
Unit: timeStamp,usec
source_pid Source process ID reported by the candidate source.
Type: int | (gauge)
Unit: N/A
started_at Probe start time in Unix microseconds.
Type: int | (gauge)
Unit: timeStamp,usec
test_run_id DataKit-generated probe run ID.
Type: string | (string)
Unit: N/A
traceroute_fail_reason Traceroute setup or execution failure reason.
Type: string | (string)
Unit: N/A
traceroute_queries Configured complete traceroute run count.
Type: int | (gauge)
Unit: count

Security and Privacy

  • DataKit uploads hop IPs observed by traceroute because they are required for path visualization.
  • DataKit does not send hop IPs to an external ASN lookup service. Kodo performs ASN enrichment with an offline database.
  • Reverse DNS causes DNS queries from the DataKit network. Keep it disabled if these queries may disclose sensitive information.
  • If raw hop IPs must not leave the customer network, assess whether NetPath can be enabled. Moving ASN enrichment to Kodo does not hide raw IPs from the observability platform.

Troubleshooting

Symptom Check
Candidate API returns HTTP 401 The request token must match dynamic.token.
Candidate API returns HTTP 404 Ensure dynamic.enabled is not disabled.
ip_without_domain Provide a hostname or explicitly enable monitor_ip_without_domain.
traceroute_fail_type=permission Provide the raw socket permissions required by ICMP/UDP traceroute.
Only part of the path is visible Check for traceroute_status=partial; intermediate devices may not respond to TTL probes.
ASN is absent Ensure an ASN/ISP MMDB is deployed in Kodo; private and non-responsive hops do not have ASN fields.