280 lines
8.0 KiB
Go
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
|
|
}
|