<!--startmeta
custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/crates/netflow-plugin/integrations/static_metadata.md"
meta_yaml: "https://github.com/netdata/netdata/edit/master/src/crates/netflow-plugin/metadata.yaml"
sidebar_label: "Static Metadata"
learn_status: "Published"
learn_rel_path: "Network Performance Monitoring/Network Flows/Enrichment Methods"
keywords: ['static metadata', 'exporters', 'interfaces', 'networks', 'cidr', 'sampling rate override', 'asn override', 'country override', 'tenant', 'site', 'region', 'role']
message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE FLOWS' metadata.yaml FILE"
endmeta-->

<!-- markdownlint-disable-file -->

# Static Metadata


<img src="https://netdata.cloud/img/network-wired.svg" width="150"/>


Plugin: netflow-plugin
Module: static-metadata

<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />

## Overview

Annotate network flows with operator-defined labels for exporters, interfaces,
networks, and sampling rates. Static Metadata is a YAML-defined enrichment source
that lives entirely in `netflow.yaml`. Use it when there is no IPAM, no CMDB, and
no MMDB to query -- you simply know your own networks, your own routers, and what
the interfaces on those routers do, and you want those labels visible on the
Network Flows view.

Three independent surfaces are configured under this card, each populating a
different set of flow-record fields:

- `enrichment.metadata_static.exporters.<ip-or-cidr>` -- per-exporter labels
  (matched against the source IP of the UDP datagram) and per-interface labels
  (matched against the ifIndex from the flow record).
- `enrichment.networks.<cidr>` -- labels for arbitrary CIDR blocks (your own
  corp ranges, customer ranges, public blocks you operate). Matched against the
  flow's source and destination IPs.
- `enrichment.override_sampling_rate.<cidr>` -- per-prefix sampling-rate
  substitution for exporters that do not communicate their rate (e.g. NetFlow
  v7, which has no sampling field, or a v9 exporter that never sends a Sampling
  Options Template).

Fields populated:

- From `metadata_static.exporters` (per-exporter):
  `EXPORTER_NAME`, `EXPORTER_GROUP`, `EXPORTER_ROLE`, `EXPORTER_SITE`,
  `EXPORTER_REGION`, `EXPORTER_TENANT`.
- From `metadata_static.exporters.if_indexes` (per-interface):
  `IN_IF_NAME` / `OUT_IF_NAME`, `IN_IF_DESCRIPTION` / `OUT_IF_DESCRIPTION`,
  `IN_IF_SPEED` / `OUT_IF_SPEED` (in **bits per second**), `IN_IF_PROVIDER` /
  `OUT_IF_PROVIDER`, `IN_IF_CONNECTIVITY` / `OUT_IF_CONNECTIVITY`,
  `IN_IF_BOUNDARY` / `OUT_IF_BOUNDARY` (`1` = external, `2` = internal,
  `0`/omitted = removed from output).
- From `enrichment.networks` (per-CIDR):
  `SRC_NET_NAME` / `DST_NET_NAME`, `SRC_NET_ROLE` / `DST_NET_ROLE`,
  `SRC_NET_SITE` / `DST_NET_SITE`, `SRC_NET_REGION` / `DST_NET_REGION`,
  `SRC_NET_TENANT` / `DST_NET_TENANT`, plus overrides for the GeoIP-derived
  `SRC_COUNTRY` / `DST_COUNTRY`, `SRC_GEO_STATE` / `DST_GEO_STATE`,
  `SRC_GEO_CITY` / `DST_GEO_CITY`, `SRC_GEO_LATITUDE` / `DST_GEO_LATITUDE`,
  `SRC_GEO_LONGITUDE` / `DST_GEO_LONGITUDE`. Setting `asn:` on a `networks`
  entry overrides the AS *number* via the same merge path; the AS *name*
  still comes from the ASN database (see "Composition with dynamic sources"
  below).
- From `override_sampling_rate` (longest-prefix match against the exporter IP):
  `SAMPLING_RATE` is set unconditionally when an override prefix matches. This
  is different from `default_sampling_rate`, which only applies when the flow
  does not already carry a rate.

For the cross-cutting Enrichment concept (provider chains, merge order rules,
how static composes with dynamic sources, the static-blocks-classifiers
interaction, and shared failure modes), see
[Enrichment](https://github.com/netdata/netdata/blob/master/docs/npm/network-flows/enrichment.md).


Edit `netflow.yaml`, restart the plugin, and the YAML-defined data is loaded
into the same in-memory tries the GeoIP, IPAM, and BGP enrichment paths read
from. There is no network access, no file watching, and no hot reload --
changes take effect on the next plugin startup.


This integration is supported on all platforms.

This integration runs as a single instance per Netdata Agent.


### Default Behavior

#### Auto-Detection

Disabled by default. Add entries under `enrichment.metadata_static`, `enrichment.networks`, and / or `enrichment.override_sampling_rate` to populate it.

#### Limits

Resource use scales with the number of exporter, interface, network, and sampling-override entries. Keep CIDR ranges and interface maps specific enough to match the routers you actually export.

#### Performance Impact

Static lookups are local map/trie lookups during enrichment. Cost is usually small compared with flow decode and journal writes, but very large maps add memory and lookup work.

## Setup


### Prerequisites

#### An understanding of your own topology

You need to know which IP addresses your routers / switches / firewalls
export from, the integer ifIndex values they use for each interface, and
the CIDR ranges that belong to each of your sites / tenants / roles. None
of this is auto-discovered -- this card is the "I will tell you" path.
For sources that auto-discover the same data, see Generic IPAM, NetBox,
and the cloud IP-range cards.


#### Persistent ifIndex values

`if_indexes` keys are the integer ifIndex the router puts in the flow
record. Some platforms reassign ifIndex on line-card reseat or stack
rebuild. After hardware changes, audit the labels -- a stale ifIndex
entry silently no longer applies. The `default` interface block is used
instead, or no labels are written when `skip_missing_interfaces: true` is set.



### Configuration

#### Options

All keys live under `enrichment:` in `netflow.yaml`. Unknown keys fail config
load with a parse error.


<details open><summary>Config options</summary>



| Option | Description | Default | Required |
|:-----|:------------|:--------|:---------:|
| enrichment.metadata_static.exporters | Map keyed by exporter IP or CIDR. Longest-prefix match wins. A `/32` key and a bare IP are equivalent. Each entry holds exporter-level labels (`name`, `group`, `role`, `site`, `region`, `tenant`), an optional `default` interface block, an `if_indexes` map keyed by integer ifIndex, and `skip_missing_interfaces`. | {} | no |
| enrichment.metadata_static.exporters.&lt;key&gt;.if_indexes | Map keyed by integer ifIndex (the value the router puts in the flow record). Each entry holds `name`, `description`, `speed` (bits per second), `provider`, `connectivity`, and `boundary`. | {} | no |
| enrichment.metadata_static.exporters.&lt;key&gt;.default | Interface block applied to flow records whose ifIndex is not present in `if_indexes`. Same shape as an `if_indexes` entry. Ignored when `skip_missing_interfaces: true` is set. | {} | no |
| enrichment.metadata_static.exporters.&lt;key&gt;.skip_missing_interfaces | When `true`, flow records whose ifIndex is not in `if_indexes` get no interface labels at all (not even from `default`). Useful when you only care about a known set of WAN interfaces and want unknown ones to stay blank instead of inheriting a placeholder. | false | no |
| enrichment.metadata_static.exporters.&lt;key&gt;.if_indexes.&lt;n&gt;.boundary | Interface boundary marker. Accepts the integers `0` (undefined), `1` (external -- faces the outside world: Internet, peer, transit), `2` (internal -- faces your own infrastructure), or the case-insensitive strings `"undefined"`, `"external"`, `"internal"`. Any other value fails config load. Filtering for `IN_IF_BOUNDARY=1` cleanly gives you "traffic that arrived from outside". | 0 | no |
| enrichment.metadata_static.exporters.&lt;key&gt;.if_indexes.&lt;n&gt;.speed | Interface speed in **bits per second**. A 1 Gbps interface is `1000000000`, not `1000` and not `1000000`. Operators thinking in megabits get the value wrong by a factor of 1000 to 1000000. A `0` value means "not set" and removes the field from the output. | 0 | no |
| enrichment.networks | Map keyed by CIDR. Longest-prefix match contributes the most-specific fields; less-specific containing prefixes contribute their non-empty fields too. The same merge rule is used by `network_sources`. Each value is either a string (shorthand for `name:`) or a map with `name`, `role`, `site`, `region`, `country`, `state`, `city`, `latitude`, `longitude`, `tenant`, `asn`. | {} | no |
| enrichment.networks.&lt;cidr&gt;.asn | Forces the AS *number* for traffic in this prefix, overriding whatever the `asn_providers` chain computed. The AS *name* is still resolved from the ASN MMDB -- there is no `asn_name` config field. See the ASN section of [Enrichment](https://github.com/netdata/netdata/blob/master/docs/npm/network-flows/enrichment.md). | 0 | no |
| enrichment.networks.&lt;cidr&gt;.latitude / longitude | Per-CIDR coordinate override. Out-of-range values (latitude not in [-90, 90] or longitude not in [-180, 180]) and non-finite values are silently coerced to empty strings -- the field is dropped, no error. Validate input out of band if your data matters. | null | no |
| enrichment.default_sampling_rate | Global fallback applied only when the flow record does not already carry a sampling rate. Either a single integer or a CIDR-keyed map (longest-prefix match against the exporter IP). | 0 | no |
| enrichment.override_sampling_rate | Per-exporter substitution that **always** wins when its prefix matches the exporter IP, regardless of what the flow record carried. Either a single integer or a CIDR-keyed map. | {} | no |


</details>



#### via File

The configuration file name for this integration is `netflow.yaml`.


You can edit the configuration file using the [`edit-config`](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#edit-configuration-files) script from the
Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#locate-your-config-directory).

```bash
cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
sudo ./edit-config netflow.yaml
```

##### Examples

###### Naming exporters and their interfaces

One edge router and one spine. The `default` block applies to any
ifIndex not listed under `if_indexes` -- handy when you only care about
a few WAN ports and want everything else to inherit a placeholder.


```yaml
enrichment:
  metadata_static:
    exporters:
      192.0.2.10:                    # bare IP == /32
        name: edge-router-1
        site: par1
        region: eu-west
        role: edge
        tenant: tenant-a
        default:
          description: unclassified port
        if_indexes:
          1:
            name: Gi0/0/1
            description: uplink to ISP-A
            speed: 10000000000        # 10 Gbps in bits per second
            provider: isp-a
            connectivity: transit
            boundary: external
          2:
            name: Gi0/0/2
            description: LAN core
            speed: 1000000000
            connectivity: lan
            boundary: internal
      198.51.100.0/24:                # all routers in this subnet
        site: dc-fra1
        region: eu-central
        role: spine
        default:
          connectivity: lan
          boundary: internal

```
###### Tagging your own networks

The shorthand form (`203.0.113.0/24: transit-a`) sets only the `name`.
The map form takes any subset of the schema fields. Less-specific
supernets contribute non-empty fields to more-specific subnets via
ascending-prefix-length merge -- declare RFC1918 once at `/8` and
a `/24` inherits everything you did not override.


<details open><summary>Config</summary>

```yaml
enrichment:
  networks:
    10.0.0.0/8:
      name: corp-internal
      role: internal
      tenant: tenant-a
    198.51.100.0/24:                  # a public block you operate
      name: customer-acme
      role: customer
      site: par1
      country: FR
      city: Paris
      latitude: 48.8566
      longitude: 2.3522
      asn: 64500
    203.0.113.0/24: transit-a         # shorthand: name only

```
</details>

###### Skipping interfaces you have not labelled

With `skip_missing_interfaces: true`, an ifIndex that is not in
`if_indexes` produces no interface labels at all -- the `default`
block is ignored. Useful when you want clearly-blank values for
unmodelled interfaces instead of a placeholder description.


<details open><summary>Config</summary>

```yaml
enrichment:
  metadata_static:
    exporters:
      192.0.2.10:
        name: edge-router-1
        skip_missing_interfaces: true
        if_indexes:
          1:
            name: Gi0/0/1
            description: uplink to ISP-A
            connectivity: transit
            boundary: external

```
</details>

###### Sampling rate override for old or silent exporters

NetFlow v7 has no sampling field; some v9 exporters never send a
Sampling Options Template. Without an override, the bytes/packets
stored in the journal are exporter-side **sampled** counters with
no scaling factor. `override_sampling_rate` always wins;
`default_sampling_rate` is the fallback used only when the flow
did not carry a rate.


<details open><summary>Config</summary>

```yaml
enrichment:
  default_sampling_rate: 1            # global fallback
  override_sampling_rate:
    10.1.0.0/16: 1024                 # an old NetFlow v7 stack
    192.0.2.10/32: 4096               # one specific exporter

```
</details>



### A more-specific entry inherits the wrong field from its supernet

`enrichment.networks` merges containing prefixes from least-specific to
most-specific. Leaving a field blank on a `/24` does **not** clear the `/16`'s value
for that field -- you must explicitly set the field on the more-specific
entry to overwrite. The same merge rule applies to entries from
`network_sources`, which interleave at the same prefix lengths.


### GeoIP returns spurious data for an internal range

Declare an `enrichment.networks` entry for the range to override the GeoIP layer at the merge step -- see [Private and non-routable IPs](https://github.com/netdata/netdata/blob/master/docs/npm/network-flows/enrichment.md#private-and-non-routable-ips) for why GeoIP is unreliable for RFC1918 / RFC6598 / link-local ranges and how the override works.


### ifIndex changed after a hardware swap

`if_indexes` keys are the numeric ifIndex sent in the flow record. A
line-card reseat or stack rebuild can renumber the interfaces; the old
ifIndex no longer matches and the per-interface block silently no longer
applies. Audit after hardware changes.


### speed shows up wrong by a factor of 1000

`speed:` is in **bits per second**. `speed: 1000` means 1 kbps, not
1 Mbps. A 1 Gbps interface is `1000000000`. `speed: 0` means "not set"
and removes the field from the output.


### An interface block silently no longer applies

When the configured ifIndex is not present in the flow record, the
`default` block is used instead -- unless `skip_missing_interfaces: true`
is set, in which case no interface labels are written at all. If you expected your block to apply but the labels are blank, the
router is sending a different ifIndex.


### Coordinates dropped silently

Out-of-range latitude / longitude (`latitude: 91.5`) and non-finite
values become empty strings without an error. The map quietly stops drawing the marker. Validate input externally if
the data matters.


### Static labels block the classifiers

When static metadata sets **any** of `group`, `role`, `site`, `region`,
`tenant` for an exporter, the `exporter_classifiers` rule chain does not
run for that exporter at all. The same is true for an interface: any of `provider`, `connectivity`,
`boundary` set by static metadata short-circuits
`interface_classifiers`. If
you want classifiers to run on top of static metadata, drop the static
fields they are supposed to set.


### A typo fails config load

The schema is `deny_unknown_fields` at every level. A typo such as
`if_index` (the canonical key is `if_indexes`; aliases `ifindexes`,
`if-indexes` are accepted) or a misspelt attribute (`teannt:`) fails plugin
start with a YAML parse error rather than being silently ignored.


### Sampling override looks ignored

`override_sampling_rate` always wins when its prefix matches; if the
field still looks unset, check that the **exporter IP** -- not the
flow's source / destination IP -- falls under the configured prefix.
`default_sampling_rate` is only consulted when the flow did not already
carry a rate.


### Changes do not take effect

Static metadata is loaded at plugin startup and there is no file-change
watcher. Restart the plugin (or the agent) after editing
`netflow.yaml`.
