From f394144896406bd018266aa355560ca0d86848f0 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Thu, 15 Oct 2026 13:31:59 -0400 Subject: [PATCH] feat(cni): Name the VRF device in per-VPC metrics vpc_attribution_table now stores the name of the VPC's VRF device next to the VPC and VPCAttachment identifiers, and the per-VRF counters carry it as a vrf_interface label. A map dump and a dashboard can then name the kernel interface a VPC's traffic belongs to, without decoding the base62 VPC identifier by hand. CNI ADD and the datapath repair pass both write the name, derived the same way the VRF itself is named. Register rejects a name that does not fit IFNAMSIZ. The map value grows, so the loader recreates vpc_attribution_table empty on rollout and the repair pass refills it right after the datapath loads. Forwarding never reads this map. Until the refill, the per-VRF series report vpc="" as unattributed. Co-Authored-By: Claude Opus 5.5 --- docs/metrics.md | 22 ++++----- internal/gc/datapath_repair_test.go | 10 +++-- internal/plumbing/ebpf/attachreg/fill.go | 5 ++- internal/plumbing/ebpf/attachreg/register.go | 4 +- internal/plumbing/ebpf/metrics/collector.go | 8 ++-- .../plumbing/ebpf/metrics/collector_test.go | 24 +++++++++- internal/plumbing/ebpf/prog/usid.c | 5 +++ .../plumbing/ebpf/usidmap/registry_test.go | 7 +-- internal/plumbing/ebpf/usidmap/vpcattr.go | 30 +++++++++++-- .../plumbing/ebpf/usidmap/vpcattr_test.go | 45 +++++++++++++------ 10 files changed, 118 insertions(+), 42 deletions(-) diff --git a/docs/metrics.md b/docs/metrics.md index 1559bc96..fb82b78f 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -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: @@ -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. diff --git a/internal/gc/datapath_repair_test.go b/internal/gc/datapath_repair_test.go index b6afafda..46664558 100644 --- a/internal/gc/datapath_repair_test.go +++ b/internal/gc/datapath_repair_test.go @@ -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" ) @@ -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 { @@ -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 { diff --git a/internal/plumbing/ebpf/attachreg/fill.go b/internal/plumbing/ebpf/attachreg/fill.go index 853a5ebf..cb33a2b5 100644 --- a/internal/plumbing/ebpf/attachreg/fill.go +++ b/internal/plumbing/ebpf/attachreg/fill.go @@ -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" ) @@ -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 diff --git a/internal/plumbing/ebpf/attachreg/register.go b/internal/plumbing/ebpf/attachreg/register.go index e92b627a..c271b7d5 100644 --- a/internal/plumbing/ebpf/attachreg/register.go +++ b/internal/plumbing/ebpf/attachreg/register.go @@ -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) diff --git a/internal/plumbing/ebpf/metrics/collector.go b/internal/plumbing/ebpf/metrics/collector.go index 7e8a850e..95edf2b8 100644 --- a/internal/plumbing/ebpf/metrics/collector.go +++ b/internal/plumbing/ebpf/metrics/collector.go @@ -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") ) @@ -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)) } diff --git a/internal/plumbing/ebpf/metrics/collector_test.go b/internal/plumbing/ebpf/metrics/collector_test.go index 2d9a75ea..f001499a 100644 --- a/internal/plumbing/ebpf/metrics/collector_test.go +++ b/internal/plumbing/ebpf/metrics/collector_test.go @@ -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 -> @@ -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 != "" { @@ -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++ } } diff --git a/internal/plumbing/ebpf/prog/usid.c b/internal/plumbing/ebpf/prog/usid.c index 8c3893ca..dda094a2 100644 --- a/internal/plumbing/ebpf/prog/usid.c +++ b/internal/plumbing/ebpf/prog/usid.c @@ -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. diff --git a/internal/plumbing/ebpf/usidmap/registry_test.go b/internal/plumbing/ebpf/usidmap/registry_test.go index cf52b631..5abcf254 100644 --- a/internal/plumbing/ebpf/usidmap/registry_test.go +++ b/internal/plumbing/ebpf/usidmap/registry_test.go @@ -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}) @@ -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) } } diff --git a/internal/plumbing/ebpf/usidmap/vpcattr.go b/internal/plumbing/ebpf/usidmap/vpcattr.go index e7101943..c36de37d 100644 --- a/internal/plumbing/ebpf/usidmap/vpcattr.go +++ b/internal/plumbing/ebpf/usidmap/vpcattr.go @@ -5,6 +5,7 @@ package usidmap import ( + "bytes" "errors" "fmt" @@ -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. @@ -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) } @@ -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) } @@ -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 } @@ -140,6 +152,7 @@ func (t *VPCAttributionTable) List() ([]VPCAttributionEntry, error) { }, VPC: value.Vpc, VPCAttachment: value.VpcAttachment, + VRFInterface: interfaceName(value.VrfInterface), Generation: value.Generation, }) } @@ -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]) +} diff --git a/internal/plumbing/ebpf/usidmap/vpcattr_test.go b/internal/plumbing/ebpf/usidmap/vpcattr_test.go index 95689f77..4d7f0244 100644 --- a/internal/plumbing/ebpf/usidmap/vpcattr_test.go +++ b/internal/plumbing/ebpf/usidmap/vpcattr_test.go @@ -9,6 +9,10 @@ import ( "testing" ) +// testVRFInterface is a VRF device name of the length +// intf.GenerateInterfaceNameVRF produces. +const testVRFInterface = "G0000002tgV" + func newTestVPCAttributionTable(clock func() uint64) (*VPCAttributionTable, *fakeTable) { ft := newFakeTable() return &VPCAttributionTable{table: ft, clock: clock}, ft @@ -17,7 +21,7 @@ func newTestVPCAttributionTable(clock func() uint64) (*VPCAttributionTable, *fak func TestVPCAttributionTable_RegisterAndGet(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(7)) - if err := vt.Register(testBlock, 0x123, 0xC0FFEE, 0x42); err != nil { + if err := vt.Register(testBlock, 0x123, 0xC0FFEE, 0x42, testVRFInterface); err != nil { t.Fatalf("Register: unexpected error: %v", err) } @@ -32,6 +36,7 @@ func TestVPCAttributionTable_RegisterAndGet(t *testing.T) { VRFKey: VRFKey{Block: testBlock, Argument: 0x123}, VPC: 0xC0FFEE, VPCAttachment: 0x42, + VRFInterface: testVRFInterface, Generation: 7, } if entry != want { @@ -39,6 +44,20 @@ func TestVPCAttributionTable_RegisterAndGet(t *testing.T) { } } +// TestVPCAttributionTable_RegisterRejectsLongVRFInterface covers a name that +// does not fit IFNAMSIZ with its terminating NUL, which the kernel would never +// have accepted as an interface name either. +func TestVPCAttributionTable_RegisterRejectsLongVRFInterface(t *testing.T) { + vt, ft := newTestVPCAttributionTable(constClock(1)) + + if err := vt.Register(testBlock, 0x123, 0xC0FFEE, 0x42, "G0123456789abcdV"); err == nil { + t.Errorf("Register(16-byte VRF interface) = nil error, want rejection") + } + if ft.len() != 0 { + t.Errorf("Register(16-byte VRF interface) wrote %d entries, want 0", ft.len()) + } +} + func TestVPCAttributionTable_GetMissingEntry(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(1)) @@ -54,7 +73,7 @@ func TestVPCAttributionTable_GetMissingEntry(t *testing.T) { func TestVPCAttributionTable_RegisterRejectsReservedArgumentZero(t *testing.T) { vt, ft := newTestVPCAttributionTable(constClock(1)) - if err := vt.Register(testBlock, 0x000, 0xC0FFEE, 0x42); err == nil { + if err := vt.Register(testBlock, 0x000, 0xC0FFEE, 0x42, testVRFInterface); err == nil { t.Errorf("Register(argument=0x000) = nil error, want rejection, matching VRFTable's own reserved-Argument rule") } if ft.len() != 0 { @@ -71,12 +90,12 @@ func TestVPCAttributionTable_RegisterRejectsReservedArgumentZero(t *testing.T) { func TestVPCAttributionTable_RegisterOverwritesOnReRegister(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(1)) - if err := vt.Register(testBlock, 0x123, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x123, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("first Register: unexpected error: %v", err) } vt.clock = constClock(99) - if err := vt.Register(testBlock, 0x123, 0x2222, 0x02); err != nil { + if err := vt.Register(testBlock, 0x123, 0x2222, 0x02, testVRFInterface); err != nil { t.Fatalf("second Register: unexpected error: %v", err) } @@ -92,7 +111,7 @@ func TestVPCAttributionTable_RegisterOverwritesOnReRegister(t *testing.T) { func TestVPCAttributionTable_Unregister(t *testing.T) { vt, ft := newTestVPCAttributionTable(constClock(1)) - if err := vt.Register(testBlock, 0x123, 0xC0FFEE, 0x42); err != nil { + if err := vt.Register(testBlock, 0x123, 0xC0FFEE, 0x42, testVRFInterface); err != nil { t.Fatalf("Register: unexpected error: %v", err) } if err := vt.Unregister(testBlock, 0x123); err != nil { @@ -117,10 +136,10 @@ func TestVPCAttributionTable_UnregisterAbsentIsNotError(t *testing.T) { func TestVPCAttributionTable_List(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(1)) - if err := vt.Register(testBlock, 0x001, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x001, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) } - if err := vt.Register(testBlock, 0x002, 0x2222, 0x02); err != nil { + if err := vt.Register(testBlock, 0x002, 0x2222, 0x02, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) } @@ -162,7 +181,7 @@ func TestVPCAttributionTable_Generation(t *testing.T) { func TestVPCAttributionTable_Reconcile_RemovesStaleEntry(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(10)) - if err := vt.Register(testBlock, 0x100, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x100, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) } @@ -181,7 +200,7 @@ func TestVPCAttributionTable_Reconcile_RemovesStaleEntry(t *testing.T) { func TestVPCAttributionTable_Reconcile_KeepsLiveEntry(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(10)) - if err := vt.Register(testBlock, 0x100, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x100, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) } @@ -198,13 +217,13 @@ func TestVPCAttributionTable_Reconcile_KeepsLiveEntry(t *testing.T) { func TestVPCAttributionTable_Reconcile_RegistrationMidSweepSurvives(t *testing.T) { vt, _ := newTestVPCAttributionTable(constClock(10)) - if err := vt.Register(testBlock, 0x100, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x100, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("Register(stale): unexpected error: %v", err) } const cutoff = 20 vt.clock = constClock(30) - if err := vt.Register(testBlock, 0x200, 0x2222, 0x02); err != nil { + if err := vt.Register(testBlock, 0x200, 0x2222, 0x02, testVRFInterface); err != nil { t.Fatalf("Register(mid-sweep): unexpected error: %v", err) } @@ -226,10 +245,10 @@ func TestVPCAttributionTable_Reconcile_RegistrationMidSweepSurvives(t *testing.T func TestVPCAttributionTable_Reconcile_ContinuesPastDeleteFailure(t *testing.T) { vt, ft := newTestVPCAttributionTable(constClock(10)) - if err := vt.Register(testBlock, 0x100, 0x1111, 0x01); err != nil { + if err := vt.Register(testBlock, 0x100, 0x1111, 0x01, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) } - if err := vt.Register(testBlock, 0x200, 0x2222, 0x02); err != nil { + if err := vt.Register(testBlock, 0x200, 0x2222, 0x02, testVRFInterface); err != nil { t.Fatalf("Register: %v", err) }