Files
govpp-snmp-agentx/src/vpp/vpp_iface.go
2025-06-24 07:51:37 +02:00

280 lines
8.0 KiB
Go

// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
package vpp
import (
"os"
"time"
"go.fd.io/govpp/api"
interfaces "go.fd.io/govpp/binapi/interface"
"go.fd.io/govpp/binapi/interface_types"
"govpp-snmp-agentx/logger"
)
// InterfaceDetails holds detailed information about a VPP interface
type InterfaceDetails struct {
SwIfIndex interface_types.InterfaceIndex
InterfaceName string
MacAddress []byte
Speed uint64
AdminStatus bool
OperStatus bool
MTU uint32
}
// InterfaceEventCallback is called when interface events occur
type InterfaceEventCallback func(details []InterfaceDetails)
// InterfaceManager handles interface-related VPP operations
type InterfaceManager struct {
client *VPPClient
eventCallback InterfaceEventCallback
running bool
watchingEvents bool
}
// NewInterfaceManager creates a new interface manager
func NewInterfaceManager(client *VPPClient) *InterfaceManager {
return &InterfaceManager{
client: client,
}
}
// SetEventCallback sets the callback for interface events
func (im *InterfaceManager) SetEventCallback(callback InterfaceEventCallback) {
im.eventCallback = callback
}
// InitializeEventWatching starts event watching and retrieves initial interface details
func (im *InterfaceManager) InitializeEventWatching() error {
if !im.client.IsConnected() {
return &VPPError{Message: "VPP client not connected"}
}
// Start watching interface events
if err := im.StartEventWatcher(); err != nil {
logger.Debugf("Failed to start interface event watching: %v", err)
return err
}
logger.Debugf("Interface event watching started")
// Get initial interface details
if details, err := im.GetAllInterfaceDetails(); err != nil {
logger.Debugf("Failed to get initial interface details: %v", err)
return err
} else {
logger.Debugf("Retrieved initial interface details for %d interfaces", len(details))
if im.eventCallback != nil {
im.eventCallback(details)
}
}
im.watchingEvents = true
return nil
}
// StartEventMonitoring starts continuous monitoring for VPP connection and restarts event watching as needed
func (im *InterfaceManager) StartEventMonitoring() {
if im.running {
logger.Debugf("Interface event monitoring already running")
return
}
im.running = true
go im.eventMonitoringRoutine()
}
// StopEventMonitoring stops the event monitoring routine
func (im *InterfaceManager) StopEventMonitoring() {
im.running = false
}
// eventMonitoringRoutine continuously monitors VPP connection and manages event watching
func (im *InterfaceManager) eventMonitoringRoutine() {
logger.Debugf("Starting interface event monitoring routine")
for {
if !im.running {
logger.Debugf("Interface event monitoring routine stopping")
break
}
if im.client.IsConnected() {
if !im.watchingEvents {
if err := im.InitializeEventWatching(); err != nil {
logger.Printf("Failed to initialize interface event watching: %v", err)
} else {
logger.Printf("Interface event watching started")
}
}
} else {
if im.watchingEvents {
logger.Printf("VPP connection lost, interface event watching will restart on reconnection")
im.watchingEvents = false
}
}
time.Sleep(time.Second)
}
logger.Debugf("Interface event monitoring routine ended")
}
// GetAllInterfaceDetails retrieves detailed information for all interfaces
func (im *InterfaceManager) GetAllInterfaceDetails() ([]InterfaceDetails, error) {
if !im.client.IsConnected() {
return nil, &VPPError{Message: "VPP client not connected"}
}
ch, err := im.client.NewAPIChannel()
if err != nil {
return nil, err
}
defer ch.Close()
return getAllInterfaceDetails(ch)
}
// StartEventWatcher starts watching for interface events
func (im *InterfaceManager) StartEventWatcher() error {
if !im.client.IsConnected() {
return &VPPError{Message: "VPP client not connected"}
}
ch, err := im.client.NewAPIChannel()
if err != nil {
return err
}
return watchInterfaceEvents(ch, im.handleInterfaceEvent)
}
// handleInterfaceEvent handles interface events and calls the callback
func (im *InterfaceManager) handleInterfaceEvent() {
if im.eventCallback != nil {
details, err := im.GetAllInterfaceDetails()
if err != nil {
logger.Debugf("Failed to retrieve interface details after event: %v", err)
} else {
logger.Debugf("Calling interface event callback with %d interfaces", len(details))
im.eventCallback(details)
}
}
}
// getAllInterfaceDetails retrieves detailed information for all interfaces (internal function)
func getAllInterfaceDetails(ch api.Channel) ([]InterfaceDetails, error) {
logger.Debugf("Retrieving all interface details from VPP")
// Get all interfaces
reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{
SwIfIndex: ^interface_types.InterfaceIndex(0), // All interfaces
})
var details []InterfaceDetails
for {
iface := &interfaces.SwInterfaceDetails{}
stop, err := reqCtx.ReceiveReply(iface)
if stop {
break
}
if err != nil {
logger.Debugf("Error retrieving interface details: %v", err)
return nil, err
}
// Convert VPP interface flags to admin/oper status
adminUp := (iface.Flags & interface_types.IF_STATUS_API_FLAG_ADMIN_UP) != 0
operUp := (iface.Flags & interface_types.IF_STATUS_API_FLAG_LINK_UP) != 0
detail := InterfaceDetails{
SwIfIndex: iface.SwIfIndex,
InterfaceName: string(iface.InterfaceName),
MacAddress: iface.L2Address[:],
Speed: uint64(iface.LinkSpeed) * 1000, // Convert Kbps to bps
AdminStatus: adminUp,
OperStatus: operUp,
MTU: uint32(iface.LinkMtu),
}
details = append(details, detail)
logger.Debugf("Interface %d (%s): MAC=%x, Speed=%d, Admin=%t, Oper=%t, MTU=%d",
detail.SwIfIndex, detail.InterfaceName, detail.MacAddress,
detail.Speed, detail.AdminStatus, detail.OperStatus, detail.MTU)
}
logger.Debugf("Retrieved details for %d interfaces", len(details))
return details, nil
}
// watchInterfaceEvents watches for VPP interface events (internal function)
func watchInterfaceEvents(ch api.Channel, callback func()) error {
logger.Debugf("WatchInterfaceEvents() called - starting interface event monitoring")
notifChan := make(chan api.Message, 100)
// subscribe for specific event message
logger.Debugf("Subscribing to interface events...")
sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
if err != nil {
logger.Debugf("error subscribing to interface events: %v", err)
return err
}
logger.Debugf("Successfully subscribed to interface events")
// enable interface events in VPP
logger.Debugf("Enabling interface events in VPP...")
err = ch.SendRequest(&interfaces.WantInterfaceEvents{
PID: uint32(os.Getpid()),
EnableDisable: 1,
}).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
if err != nil {
logger.Debugf("error enabling interface events: %v", err)
return err
}
logger.Debugf("Interface events enabled in VPP, starting event listener goroutine")
// receive notifications
go func() {
logger.Debugf("Interface event listener goroutine started")
defer func() {
logger.Debugf("Interface event listener goroutine shutting down")
// disable interface events in VPP
err = ch.SendRequest(&interfaces.WantInterfaceEvents{
PID: uint32(os.Getpid()),
EnableDisable: 0,
}).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
if err != nil {
logger.Debugf("error disabling interface events: %v", err)
}
// unsubscribe from receiving events
err = sub.Unsubscribe()
if err != nil {
logger.Debugf("error unsubscribing from interface events: %v", err)
}
}()
logger.Debugf("Interface event listener waiting for events...")
for notif := range notifChan {
e := notif.(*interfaces.SwInterfaceEvent)
logger.Printf("interface event: SwIfIndex=%d, Flags=%d, Deleted=%t",
e.SwIfIndex, e.Flags, e.Deleted)
// When an interface event occurs, call the callback
if callback != nil {
callback()
}
}
logger.Debugf("Interface event listener goroutine ended")
}()
return nil
}