Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ and tenant VRFs.
Each node keeps one VRF per VPC, so every series below is one VPC on one
node, keyed by its uSID (Block, Argument). `vpc_attachment` names the
attachment that registered the VRF last, not each attachment's own traffic.
`vrf_interface` is the name of the VPC's VRF device on the node, as
`ip link` shows it.

| Metric | Type | Labels | Meaning |
|---------------------------------------------|---------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| `galactic_usid_vrf_forwarded_packets_total` | counter | `block`, `argument`, `vrf_table_id`, `vpc`, `vpc_attachment`, `direction` | Packets forwarded into (`ingress`) or out of (`egress`) the VRF |
| `galactic_usid_vrf_forwarded_bytes_total` | counter | same | Tenant bytes of those packets: the IP header and payload, in both directions |
| `galactic_usid_vrf_dropped_packets_total` | counter | same | Packets dropped after the datapath resolved them to the VRF, by direction |
| `galactic_usid_vrf_packets_total` | counter | `block`, `argument`, `vrf_table_id`, `vpc`, `vpc_attachment` | Inbound packets that matched the VRF, counted before any later drop |
| `galactic_usid_vrf_bytes_total` | counter | same | Bytes of those packets, measured as the whole tunneled frame with outer headers |
| Metric | Type | Labels | Meaning |
|---------------------------------------------|---------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| `galactic_usid_vrf_forwarded_packets_total` | counter | `block`, `argument`, `vrf_table_id`, `vpc`, `vpc_attachment`, `vrf_interface`, `direction` | Packets forwarded into (`ingress`) or out of (`egress`) the VRF |
| `galactic_usid_vrf_forwarded_bytes_total` | counter | same | Tenant bytes of those packets: the IP header and payload, in both directions |
| `galactic_usid_vrf_dropped_packets_total` | counter | same | Packets dropped after the datapath resolved them to the VRF, by direction |
| `galactic_usid_vrf_packets_total` | counter | `block`, `argument`, `vrf_table_id`, `vpc`, `vpc_attachment`, `vrf_interface` | Inbound packets that matched the VRF, counted before any later drop |
| `galactic_usid_vrf_bytes_total` | counter | same | Bytes of those packets, measured as the whole tunneled frame with outer headers |

What each `direction` counts:

Expand Down Expand Up @@ -85,9 +87,9 @@ What each `direction` counts:
- Registering a VRF again never resets its counters. They reset when the
datapath reloads, or when the VRF's rows are removed and created again,
which `rate()` copes with.
- `vpc` and `vpc_attachment` are empty when the VPC attribution map has no
entry for that Argument. Treat `vpc=""` traffic as unattributed and watch
for it.
- `vpc`, `vpc_attachment` and `vrf_interface` are empty when the VPC
attribution map has no entry for that Argument. Treat `vpc=""` traffic as
unattributed and watch for it.
- Cardinality is one series per active Argument per node for the older pair,
at most 4095 per Block, and twice that for each `direction` metric.

Expand Down
10 changes: 6 additions & 4 deletions internal/gc/datapath_repair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"go.datum.net/galactic/internal/plumbing/ebpf/ifindexvrfmap"
"go.datum.net/galactic/internal/plumbing/ebpf/uformat"
"go.datum.net/galactic/internal/plumbing/ebpf/usidmap"
"go.datum.net/galactic/internal/plumbing/intf"
bgpv1alpha1 "go.datum.net/network/api/v1alpha1"
)

Expand Down Expand Up @@ -439,9 +440,10 @@ func checkAttachmentRows(t *testing.T, m *attachreg.Maps, block uint64, w wantAt
}
wantVPC, wantAtt, _ := attachreg.DecodeVPCIdentifiers(w.vpc, w.att)
attr, ok, err := m.Registry.VPCAttribution.Get(block, w.arg)
if err != nil || !ok || attr.VPC != wantVPC || attr.VPCAttachment != wantAtt {
t.Errorf("vpc_attribution_table[%#x] = %+v ok %v err %v, want VPC %d attachment %d",
w.arg, attr, ok, err, wantVPC, wantAtt)
wantVRF := intf.GenerateInterfaceNameVRF(w.vpc)
if err != nil || !ok || attr.VPC != wantVPC || attr.VPCAttachment != wantAtt || attr.VRFInterface != wantVRF {
t.Errorf("vpc_attribution_table[%#x] = %+v ok %v err %v, want VPC %d attachment %d VRF interface %s",
w.arg, attr, ok, err, wantVPC, wantAtt, wantVRF)
}
ifEntry, ok, err := m.IfindexVRF.Get(w.ifindex)
if err != nil || !ok || ifEntry.Block != block || ifEntry.Argument != w.arg {
Expand Down Expand Up @@ -798,7 +800,7 @@ func TestRepairAttachmentDatapath_ExistingRowsUntouched(t *testing.T) {
if err := m.Registry.VRF.Register(block, 0x21, 0xF021, usidmap.EgressKindVeth); err != nil {
t.Fatalf("seed vrf_table: %v", err)
}
if err := m.Registry.VPCAttribution.Register(block, 0x21, 1, 2); err != nil {
if err := m.Registry.VPCAttribution.Register(block, 0x21, 1, 2, "G00000001V"); err != nil {
t.Fatalf("seed vpc_attribution_table: %v", err)
}
if err := m.IfindexVRF.Register(11, block, 0x22); err != nil {
Expand Down
5 changes: 4 additions & 1 deletion internal/plumbing/ebpf/attachreg/fill.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"go.datum.net/galactic/internal/plumbing/ebpf/ifindexvrfmap"
"go.datum.net/galactic/internal/plumbing/ebpf/uformat"
"go.datum.net/galactic/internal/plumbing/ebpf/usidmap"
"go.datum.net/galactic/internal/plumbing/intf"
"go.datum.net/galactic/internal/plumbing/srv6"
)

Expand Down Expand Up @@ -456,7 +457,9 @@ func (m *Maps) fillAttribution(a Attachment) (bool, error) {
"vpc", a.VPC, "vpcAttachment", a.VPCAttachment, "err", err)
return false, nil
}
if err := m.Registry.VPCAttribution.Register(a.Block, a.Argument, vpcNum, vpcAttachmentNum); err != nil {
if err := m.Registry.VPCAttribution.Register(
a.Block, a.Argument, vpcNum, vpcAttachmentNum, intf.GenerateInterfaceNameVRF(a.VPC),
); err != nil {
return false, fmt.Errorf("register vpc_attribution_table entry: %w", err)
}
return true, nil
Expand Down
4 changes: 3 additions & 1 deletion internal/plumbing/ebpf/attachreg/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ func RegisterDatapath(pinDir string, r Registration) (registered bool, err error
slog.Warn("ADD: could not decode VPC/VPCAttachment for byte-accounting attribution; "+
"vrf_table counters for this entry will report unattributed until the next ADD/DEL",
"vpc", r.VPC, "vpcAttachment", r.VPCAttachment, "err", err)
} else if err := registry.VPCAttribution.Register(block, r.Argument, vpcNum, vpcAttachmentNum); err != nil {
} else if err := registry.VPCAttribution.Register(
block, r.Argument, vpcNum, vpcAttachmentNum, intf.GenerateInterfaceNameVRF(r.VPC),
); err != nil {
slog.Warn("ADD: could not register eBPF vpc_attribution_table entry; "+
"vrf_table counters for this entry will report unattributed until the next ADD/DEL",
"vpc", r.VPC, "vpcAttachment", r.VPCAttachment, "err", err)
Expand Down
8 changes: 4 additions & 4 deletions internal/plumbing/ebpf/metrics/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const labelBlock = "block"
// vrfLabels are the identity labels of every per-VRF series, and
// vrfDirectionLabels adds the direction of travel.
var (
vrfLabels = []string{labelBlock, "argument", "vrf_table_id", "vpc", "vpc_attachment"}
vrfLabels = []string{labelBlock, "argument", "vrf_table_id", "vpc", "vpc_attachment", "vrf_interface"}
vrfDirectionLabels = append(append([]string(nil), vrfLabels...), "direction")
)

Expand Down Expand Up @@ -324,12 +324,12 @@ func (c *Collector) collectVRF(ch chan<- prometheus.Metric) {
vrfTableID := strconv.FormatUint(uint64(e.VRFTableID), 10)
attributionEntry, found := attribution[e.VRFKey]
vpc, vpcAttachment := vpcLabels(attributionEntry, found, e.VRFKey)
labels := []string{block, argument, vrfTableID, vpc, vpcAttachment, attributionEntry.VRFInterface}
ch <- prometheus.MustNewConstMetric(
vrfPacketsDesc, prometheus.CounterValue, float64(e.Packets), block, argument, vrfTableID, vpc, vpcAttachment)
vrfPacketsDesc, prometheus.CounterValue, float64(e.Packets), labels...)
ch <- prometheus.MustNewConstMetric(
vrfBytesDesc, prometheus.CounterValue, float64(e.Bytes), block, argument, vrfTableID, vpc, vpcAttachment)
vrfBytesDesc, prometheus.CounterValue, float64(e.Bytes), labels...)
if t, ok := traffic[e.VRFKey]; ok {
labels := []string{block, argument, vrfTableID, vpc, vpcAttachment}
emitVRFDirection(ch, t.Ingress, append(labels, directionIngress))
emitVRFDirection(ch, t.Egress, append(labels, directionEgress))
}
Expand Down
24 changes: 22 additions & 2 deletions internal/plumbing/ebpf/metrics/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,34 @@ func TestCollector_VRFPacketsAndBytes(t *testing.T) {

// putVPCAttribution writes a raw vpc_attribution_table entry directly into
// fake, matching putVRFEntry's own bypass-Register shape above.
// The VRF interface is the name intf.GenerateInterfaceNameVRF gives vpc.
func putVPCAttribution(t *testing.T, fake *fakeTable, block uint64, argument uint16, vpc uint64, vpcAttachment uint32) {
t.Helper()
key, err := uformat.NewVRFKey(block, argument)
if err != nil {
t.Fatalf("uformat.NewVRFKey: %v", err)
}
if err := fake.Put(uint64(key), prog.UsidVpcAttributionValue{
value := prog.UsidVpcAttributionValue{
Vpc: vpc,
VpcAttachment: vpcAttachment,
}); err != nil {
}
copy(value.VrfInterface[:], vrfInterfaceFor(t, vpc))
if err := fake.Put(uint64(key), value); err != nil {
t.Fatalf("fake.Put: %v", err)
}
}

// vrfInterfaceFor returns the VRF device name of the VPC with numeric
// identifier vpc.
func vrfInterfaceFor(t *testing.T, vpc uint64) string {
t.Helper()
encoded, err := intf.HexToBase62(strconv.FormatUint(vpc, 16))
if err != nil {
t.Fatalf("HexToBase62: %v", err)
}
return intf.GenerateInterfaceNameVRF(encoded)
}

func TestCollector_VPCAttribution(t *testing.T) {
// 0x2589 base62-round-trips to a short, easy-to-recognize string; the
// exact identifiers don't matter, only that they survive a decode ->
Expand Down Expand Up @@ -197,6 +211,9 @@ func TestCollector_VPCAttribution(t *testing.T) {
if got := labelValue(m, "vpc_attachment"); got != wantVPCAttachment1 {
t.Errorf("argument=1: vpc_attachment label = %q, want %q", got, wantVPCAttachment1)
}
if got, want := labelValue(m, "vrf_interface"), vrfInterfaceFor(t, vpc1); got != want {
t.Errorf("argument=1: vrf_interface label = %q, want %q", got, want)
}
sawAttributed++
case labelValue(m, labelBlock) == formatBlock(testBlock2) && labelValue(m, "argument") == "2":
if got := labelValue(m, "vpc"); got != "" {
Expand All @@ -205,6 +222,9 @@ func TestCollector_VPCAttribution(t *testing.T) {
if got := labelValue(m, "vpc_attachment"); got != "" {
t.Errorf("argument=2: vpc_attachment label = %q, want empty (no attribution row for this block)", got)
}
if got := labelValue(m, "vrf_interface"); got != "" {
t.Errorf("argument=2: vrf_interface label = %q, want empty (no attribution row for this block)", got)
}
sawUnattributed++
}
}
Expand Down
5 changes: 5 additions & 0 deletions internal/plumbing/ebpf/prog/usid.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,15 @@ struct vrf_value {
// only by userspace at registration time, compared against a Reconcile
// sweep's cutoff to tell "existed before this sweep" from "registered
// after".
//
// vrf_interface is the name of the VPC's VRF device on this node, NUL
// padded to IFNAMSIZ (16), so a map dump and the per-VRF metrics name the
// kernel interface the counters belong to.
struct vpc_attribution_value {
__u64 vpc;
__u32 vpc_attachment;
__u64 generation;
__u8 vrf_interface[16];
};

// struct vrf_direction_counter is one direction's traffic for one VRF.
Expand Down
7 changes: 4 additions & 3 deletions internal/plumbing/ebpf/usidmap/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestOpenPinnedRegistry_RoundTrip(t *testing.T) {
t.Fatalf("Function.Register via opened handle: %v", err)
}

if err := reg.VPCAttribution.Register(testBlock, 0x001, 0xC0FFEE, 0x42); err != nil {
if err := reg.VPCAttribution.Register(testBlock, 0x001, 0xC0FFEE, 0x42, testVRFInterface); err != nil {
t.Fatalf("VPCAttribution.Register via opened handle: %v", err)
}
loaderSideVPCAttribution := NewVPCAttributionTable(KernelTable{Map: objs.VpcAttributionTable})
Expand All @@ -84,8 +84,9 @@ func TestOpenPinnedRegistry_RoundTrip(t *testing.T) {
if !ok {
t.Fatal("entry registered via the opened VPCAttribution handle is not visible via the loader-side handle")
}
if attrEntry.VPC != 0xC0FFEE || attrEntry.VPCAttachment != 0x42 {
t.Errorf("VPCAttribution entry = %+v, want VPC=0xC0FFEE VPCAttachment=0x42", attrEntry)
if attrEntry.VPC != 0xC0FFEE || attrEntry.VPCAttachment != 0x42 || attrEntry.VRFInterface != testVRFInterface {
t.Errorf("VPCAttribution entry = %+v, want VPC=0xC0FFEE VPCAttachment=0x42 VRFInterface=%s",
attrEntry, testVRFInterface)
}
}

Expand Down
30 changes: 26 additions & 4 deletions internal/plumbing/ebpf/usidmap/vpcattr.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package usidmap

import (
"bytes"
"errors"
"fmt"

Expand All @@ -29,6 +30,9 @@ type VPCAttributionEntry struct {
VPC uint64
VPCAttachment uint32

// VRFInterface is the name of the VPC's VRF device on this node.
VRFInterface string

// Generation is this table's monotonic-clock reading when this entry was
// last written by Register. See VRFTable's own doc comment for how
// Reconcile uses it; the two tables share the same convention.
Expand Down Expand Up @@ -59,10 +63,12 @@ func (t *VPCAttributionTable) Generation() uint64 {
}

// Register writes, or overwrites, the vpc_attribution_table entry for
// (block, argument), mapping it to (vpc, vpcAttachment) and stamping it with
// this table's current generation. Callers register this alongside
// registry.VRF.Register, keyed identically.
func (t *VPCAttributionTable) Register(block uint64, argument uint16, vpc uint64, vpcAttachment uint32) error {
// (block, argument), mapping it to (vpc, vpcAttachment) and the VPC's VRF
// device vrfInterface, and stamping it with this table's current generation.
// Callers register this alongside registry.VRF.Register, keyed identically.
func (t *VPCAttributionTable) Register(
block uint64, argument uint16, vpc uint64, vpcAttachment uint32, vrfInterface string,
) error {
if err := uformat.ValidateArgument(argument); err != nil {
return fmt.Errorf("usidmap: vpc_attribution_table: register block=%#x argument=%#x: %w", block, argument, err)
}
Expand All @@ -76,6 +82,11 @@ func (t *VPCAttributionTable) Register(block uint64, argument uint16, vpc uint64
VpcAttachment: vpcAttachment,
Generation: t.clock(),
}
if len(vrfInterface) >= len(value.VrfInterface) {
return fmt.Errorf("usidmap: vpc_attribution_table: register block=%#x argument=%#x: "+
"VRF interface name %q is longer than %d bytes", block, argument, vrfInterface, len(value.VrfInterface)-1)
}
copy(value.VrfInterface[:], vrfInterface)
if err := t.table.Put(uint64(key), value); err != nil {
return fmt.Errorf("usidmap: vpc_attribution_table: register block=%#x argument=%#x: %w", block, argument, err)
}
Expand Down Expand Up @@ -120,6 +131,7 @@ func (t *VPCAttributionTable) Get(block uint64, argument uint16) (VPCAttribution
VRFKey: VRFKey{Block: block, Argument: argument},
VPC: value.Vpc,
VPCAttachment: value.VpcAttachment,
VRFInterface: interfaceName(value.VrfInterface),
Generation: value.Generation,
}, true, nil
}
Expand All @@ -140,6 +152,7 @@ func (t *VPCAttributionTable) List() ([]VPCAttributionEntry, error) {
},
VPC: value.Vpc,
VPCAttachment: value.VpcAttachment,
VRFInterface: interfaceName(value.VrfInterface),
Generation: value.Generation,
})
}
Expand Down Expand Up @@ -179,3 +192,12 @@ func (t *VPCAttributionTable) Reconcile(
}
return removed, errors.Join(errs...)
}

// interfaceName returns the NUL-padded interface name in raw as a string.
func interfaceName(raw [16]uint8) string {
n := bytes.IndexByte(raw[:], 0)
if n < 0 {
n = len(raw)
}
return string(raw[:n])
}
Loading
Loading