diff --git a/src/ifmib/ifmib.go b/src/ifmib/ifmib.go index 01d5fd4..107f003 100644 --- a/src/ifmib/ifmib.go +++ b/src/ifmib/ifmib.go @@ -77,19 +77,21 @@ type VPPInterface struct { } type InterfaceMIB struct { - mutex sync.RWMutex - handler *agentx.ListHandler - ifEntrySession *agentx.Session - ifXTableSession *agentx.Session - stats map[uint32]*api.InterfaceCounters // indexed by interface index - descriptions map[string]string // interface name -> description mapping + mutex sync.RWMutex + handler *agentx.ListHandler + ifEntrySession *agentx.Session + ifXTableSession *agentx.Session + stats map[uint32]*api.InterfaceCounters // indexed by interface index + descriptions map[string]string // interface name -> description mapping + interfaceDetails map[uint32]*vpp.InterfaceDetails // indexed by interface index } func NewInterfaceMIB() *InterfaceMIB { return &InterfaceMIB{ - handler: &agentx.ListHandler{}, - stats: make(map[uint32]*api.InterfaceCounters), - descriptions: make(map[string]string), + handler: &agentx.ListHandler{}, + stats: make(map[uint32]*api.InterfaceCounters), + descriptions: make(map[string]string), + interfaceDetails: make(map[uint32]*vpp.InterfaceDetails), } } @@ -142,6 +144,22 @@ func (m *InterfaceMIB) LoadVPPConfig(configPath string) error { return nil } +func (m *InterfaceMIB) UpdateInterfaceDetails(details []vpp.InterfaceDetails) { + m.mutex.Lock() + defer m.mutex.Unlock() + + logger.Debugf("Updating interface details for %d interfaces", len(details)) + + // Update interface details map + for _, detail := range details { + m.interfaceDetails[uint32(detail.SwIfIndex)] = &detail + logger.Debugf("Updated details for interface %d (%s): MAC=%x, Speed=%d", + detail.SwIfIndex, detail.InterfaceName, detail.MacAddress, detail.Speed) + } + + logger.Debugf("Interface details updated for %d interfaces", len(details)) +} + func (m *InterfaceMIB) UpdateStats(interfaceStats *api.InterfaceStats) { m.mutex.Lock() defer m.mutex.Unlock() @@ -186,6 +204,9 @@ func (m *InterfaceMIB) addInterfaceToMIB(iface *api.InterfaceCounters) { func (m *InterfaceMIB) addIfEntry(iface *api.InterfaceCounters, idx int) { var item *agentx.ListItem + // Get interface details if available + details := m.interfaceDetails[iface.InterfaceIndex] + // ifIndex (.1) item = m.handler.Add(fmt.Sprintf("%s.1.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeInteger @@ -201,30 +222,58 @@ func (m *InterfaceMIB) addIfEntry(iface *api.InterfaceCounters, idx int) { item.Type = pdu.VariableTypeInteger item.Value = int32(6) - // ifMtu (.4) - Default MTU 1500 + // ifMtu (.4) - Use real MTU if available, otherwise default to 1500 + mtu := int32(1500) + if details != nil { + mtu = int32(details.MTU) + } item = m.handler.Add(fmt.Sprintf("%s.4.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeInteger - item.Value = int32(1500) + item.Value = mtu - // ifSpeed (.5) - Default to 1Gbps (1000000000 bits/sec) + // ifSpeed (.5) - Use real speed if available, otherwise default to 1Gbps + speed := uint32(1000000000) + if details != nil && details.Speed > 0 { + speed = uint32(details.Speed) + } item = m.handler.Add(fmt.Sprintf("%s.5.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeGauge32 - item.Value = uint32(1000000000) + item.Value = speed - // ifPhysAddress (.6) - Empty for now + // ifPhysAddress (.6) - Use real MAC address if available + macAddr := "" + if details != nil && len(details.MacAddress) > 0 { + macAddr = string(details.MacAddress) + } item = m.handler.Add(fmt.Sprintf("%s.6.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeOctetString - item.Value = "" + item.Value = macAddr - // ifAdminStatus (.7) - up(1) + // ifAdminStatus (.7) - Use real admin status if available + adminStatus := int32(1) // default up + if details != nil { + if details.AdminStatus { + adminStatus = 1 // up + } else { + adminStatus = 2 // down + } + } item = m.handler.Add(fmt.Sprintf("%s.7.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeInteger - item.Value = int32(1) + item.Value = adminStatus - // ifOperStatus (.8) - up(1) + // ifOperStatus (.8) - Use real operational status if available + operStatus := int32(1) // default up + if details != nil { + if details.OperStatus { + operStatus = 1 // up + } else { + operStatus = 2 // down + } + } item = m.handler.Add(fmt.Sprintf("%s.8.%d", ifEntryOID, idx)) item.Type = pdu.VariableTypeInteger - item.Value = int32(1) + item.Value = operStatus // ifLastChange (.9) - 0 (unknown) item = m.handler.Add(fmt.Sprintf("%s.9.%d", ifEntryOID, idx)) diff --git a/src/main.go b/src/main.go index b431f45..75e977d 100644 --- a/src/main.go +++ b/src/main.go @@ -40,6 +40,9 @@ func main() { log.Fatalf("Failed to start AgentX: %v", err) } + // Set up interface event callback to update interface details + vpp.SetInterfaceEventCallback(interfaceMIB.UpdateInterfaceDetails) + // Start VPP stats routine with callback to update MIB vpp.StartStatsRoutine(interfaceMIB.UpdateStats) diff --git a/src/vpp/vpp_iface.go b/src/vpp/vpp_iface.go index c110cf5..1fc2f6e 100644 --- a/src/vpp/vpp_iface.go +++ b/src/vpp/vpp_iface.go @@ -7,11 +7,73 @@ import ( "go.fd.io/govpp/api" interfaces "go.fd.io/govpp/binapi/interface" + "go.fd.io/govpp/binapi/interface_types" "govpp-snmp-agentx/logger" ) -func WatchInterfaceEvents(ch api.Channel) error { +// 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) + +// GetAllInterfaceDetails retrieves detailed information for all interfaces +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 +} + +func WatchInterfaceEvents(ch api.Channel, callback InterfaceEventCallback) error { logger.Debugf("WatchInterfaceEvents() called - starting interface event monitoring") notifChan := make(chan api.Message, 100) @@ -64,6 +126,17 @@ func WatchInterfaceEvents(ch api.Channel) error { e := notif.(*interfaces.SwInterfaceEvent) logger.Debugf("interface event: SwIfIndex=%d, Flags=%d, Deleted=%t", e.SwIfIndex, e.Flags, e.Deleted) + + // When an interface event occurs, retrieve all interface details and call callback + if callback != nil { + details, err := GetAllInterfaceDetails(ch) + 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)) + callback(details) + } + } } logger.Debugf("Interface event listener goroutine ended") }() diff --git a/src/vpp/vpp_stats.go b/src/vpp/vpp_stats.go index d6c3062..fed5df7 100644 --- a/src/vpp/vpp_stats.go +++ b/src/vpp/vpp_stats.go @@ -17,6 +17,9 @@ import ( type StatsCallback func(*api.InterfaceStats) +// Global callback for interface events +var interfaceEventCallback InterfaceEventCallback + var ( // Flags for VPP stats configuration ApiAddr = flag.String("vppstats.api.addr", "/var/run/vpp/api.sock", "VPP API socket path") @@ -25,6 +28,11 @@ var ( Period = flag.Int("vppstats.period", 10, "Interval in seconds for querying VPP interface stats") ) +// SetInterfaceEventCallback sets the callback for interface events +func SetInterfaceEventCallback(callback InterfaceEventCallback) { + interfaceEventCallback = callback +} + // StartStatsRoutine starts a goroutine that queries VPP interface stats at the configured interval func StartStatsRoutine(callback StatsCallback) { period := time.Duration(*Period) * time.Second @@ -139,11 +147,22 @@ func statsRoutine(period time.Duration, callback StatsCallback) { logger.Debugf("Failed to create API channel for interface events: %v", err) } else { logger.Debugf("API channel created successfully, calling WatchInterfaceEvents...") - if err := WatchInterfaceEvents(ch); err != nil { + if err := WatchInterfaceEvents(ch, interfaceEventCallback); err != nil { logger.Debugf("Failed to start interface event watching: %v", err) ch.Close() } else { logger.Printf("Interface event watching started successfully") + + // Do initial retrieval of interface details + if interfaceEventCallback != nil { + details, err := GetAllInterfaceDetails(ch) + if err != nil { + logger.Debugf("Failed to get initial interface details: %v", err) + } else { + logger.Debugf("Retrieved initial interface details for %d interfaces", len(details)) + interfaceEventCallback(details) + } + } } } }