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) }