Files
vpp-maglev/internal/vpp/fibstats.go

88 lines
2.5 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package vpp
import (
"fmt"
"net"
"go.fd.io/govpp/adapter"
"go.fd.io/govpp/binapi/ip"
"go.fd.io/govpp/binapi/ip_types"
)
// routeToStatPath is the VPP stats-segment path exposing the per-FIB-entry
// "route-to" combined counter (packets + bytes), indexed by the load-
// balance index of each FIB entry. See lbm_to_counters in
// src/vnet/dpo/load_balance.c.
const routeToStatPath = "/net/route/to"
// fibStatsIndex returns the FIB entry's stats_index (load_balance index)
// for the host prefix of addr. Uses exact=0 (longest-match) so a covering
// route is returned if there is no host-prefix entry — note this means
// two maglev entities sharing a covering route will report identical
// /net/route/to counters.
func fibStatsIndex(ch *loggedChannel, addr net.IP) (uint32, error) {
var prefix ip_types.Prefix
if v4 := addr.To4(); v4 != nil {
prefix.Address.Af = ip_types.ADDRESS_IP4
copy(prefix.Address.Un.XXX_UnionData[:4], v4)
prefix.Len = 32
} else {
prefix.Address.Af = ip_types.ADDRESS_IP6
copy(prefix.Address.Un.XXX_UnionData[:], addr.To16())
prefix.Len = 128
}
req := &ip.IPRouteLookup{
TableID: 0,
Exact: 0,
Prefix: prefix,
}
reply := &ip.IPRouteLookupReply{}
if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
return 0, fmt.Errorf("ip_route_lookup: %w", err)
}
if reply.Retval != 0 {
return 0, fmt.Errorf("ip_route_lookup: retval=%d", reply.Retval)
}
return reply.Route.StatsIndex, nil
}
// findCombinedCounter returns the CombinedCounterStat matching name, or
// nil if not found or the wrong type.
func findCombinedCounter(entries []adapter.StatEntry, name string) adapter.CombinedCounterStat {
for _, e := range entries {
if string(e.Name) != name {
continue
}
if s, ok := e.Data.(adapter.CombinedCounterStat); ok {
return s
}
}
return nil
}
// reduceCombinedCounter sums the (packets, bytes) CombinedCounter across
// workers at column i, tolerating short per-worker vectors.
func reduceCombinedCounter(s adapter.CombinedCounterStat, i int) (pkts, byts uint64) {
for _, thread := range s {
if i >= 0 && i < len(thread) {
pkts += thread[i][0]
byts += thread[i][1]
}
}
return pkts, byts
}
// vipKeyToIP extracts the VIP address from a vipKey's CIDR string. The
// second return is the prefix length. Used by the scrape path to feed
// a VIP prefix into fibStatsIndex.
func vipKeyToIP(k vipKey) (net.IP, int, error) {
ip, ipnet, err := net.ParseCIDR(k.prefix)
if err != nil {
return nil, 0, err
}
ones, _ := ipnet.Mask.Size()
return ip, ones, nil
}