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