Add netflow manager (#3398)

* Add netflow manager

* fix linter issues
This commit is contained in:
Maycon Santos
2025-02-27 12:05:20 +00:00
committed by GitHub
parent 994b923d56
commit 8276236dfa
8 changed files with 345 additions and 169 deletions
+105
View File
@@ -0,0 +1,105 @@
package logger
import (
"context"
"sync/atomic"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/netflow/store"
"github.com/netbirdio/netbird/client/internal/netflow/types"
)
type rcvChan chan *types.EventFields
type Logger struct {
ctx context.Context
cancel context.CancelFunc
enabled atomic.Bool
rcvChan atomic.Pointer[rcvChan]
stopChan chan struct{}
Store types.Store
}
func New(ctx context.Context) *Logger {
ctx, cancel := context.WithCancel(ctx)
return &Logger{
ctx: ctx,
cancel: cancel,
Store: store.NewMemoryStore(),
stopChan: make(chan struct{}),
}
}
func (l *Logger) StoreEvent(flowEvent types.EventFields) {
if !l.enabled.Load() {
return
}
c := l.rcvChan.Load()
if c == nil {
return
}
select {
case *c <- &flowEvent:
default:
// todo: we should collect or log on this
}
}
func (l *Logger) Enable() {
go l.startReceiver()
}
func (l *Logger) startReceiver() {
if l.enabled.Load() {
return
}
c := make(rcvChan, 100)
l.rcvChan.Swap(&c)
l.enabled.Store(true)
for {
select {
case <-l.ctx.Done():
log.Info("flow Memory store receiver stopped")
return
case eventFields := <-c:
id := uuid.NewString()
event := types.Event{
ID: id,
EventFields: *eventFields,
Timestamp: time.Now(),
}
l.Store.StoreEvent(&event)
case <-l.stopChan:
return
}
}
}
func (l *Logger) Disable() {
l.stop()
l.Store.Close()
}
func (l *Logger) stop() {
if !l.enabled.Load() {
return
}
l.enabled.Store(false)
l.stopChan <- struct{}{}
}
func (l *Logger) GetEvents() []*types.Event {
return l.Store.GetEvents()
}
func (l *Logger) Close() {
l.stop()
l.cancel()
}
@@ -0,0 +1,38 @@
package logger_test
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/netbirdio/netbird/client/internal/netflow/logger"
"github.com/netbirdio/netbird/client/internal/netflow/types"
)
func TestStore(t *testing.T) {
logger := logger.New(context.Background())
logger.Enable()
event := types.EventFields{
FlowID: uuid.New(),
Type: types.TypeStart,
Direction: types.Ingress,
Protocol: 6,
}
time.Sleep(time.Millisecond)
logger.StoreEvent(event)
time.Sleep(time.Millisecond)
allEvents := logger.GetEvents()
matched := false
for _, e := range allEvents {
if e.EventFields.FlowID == event.FlowID {
matched = true
}
}
if !matched {
t.Errorf("didn't match any event")
}
}
+48
View File
@@ -0,0 +1,48 @@
package netflow
import (
"context"
"sync"
"github.com/netbirdio/netbird/client/internal/netflow/logger"
"github.com/netbirdio/netbird/client/internal/netflow/types"
)
type Manager struct {
mux sync.Mutex
logger types.FlowLogger
flowConfig *types.FlowConfig
}
func NewManager(ctx context.Context) *Manager {
return &Manager{
logger: logger.New(ctx),
}
}
func (m *Manager) Update(update *types.FlowConfig) error {
m.mux.Lock()
defer m.mux.Unlock()
if update == nil {
return nil
}
m.flowConfig = update
if update.Enabled {
m.logger.Enable()
return nil
}
m.logger.Disable()
return nil
}
func (m *Manager) Close() {
m.logger.Close()
}
func (m *Manager) GetLogger() types.FlowLogger {
return m.logger
}
+48
View File
@@ -0,0 +1,48 @@
package store
import (
"sync"
"github.com/netbirdio/netbird/client/internal/netflow/types"
)
func NewMemoryStore() *Memory {
return &Memory{
events: make(map[string]*types.Event),
}
}
type Memory struct {
mux sync.Mutex
events map[string]*types.Event
}
func (m *Memory) StoreEvent(event *types.Event) {
m.mux.Lock()
defer m.mux.Unlock()
m.events[event.ID] = event
}
func (m *Memory) Close() {
m.mux.Lock()
defer m.mux.Unlock()
m.events = make(map[string]*types.Event)
}
func (m *Memory) GetEvents() []*types.Event {
m.mux.Lock()
defer m.mux.Unlock()
events := make([]*types.Event, 0, len(m.events))
for _, event := range m.events {
events = append(events, event)
}
return events
}
func (m *Memory) DeleteEvents(ids []string) {
m.mux.Lock()
defer m.mux.Unlock()
for _, id := range ids {
delete(m.events, id)
}
}
+82
View File
@@ -0,0 +1,82 @@
package types
import (
"net/netip"
"time"
"github.com/google/uuid"
)
type Type int
const (
TypeStart = iota
TypeEnd
)
type Direction int
const (
Ingress = iota
Egress
)
type Event struct {
ID string
Timestamp time.Time
EventFields
}
type EventFields struct {
FlowID uuid.UUID
Type Type
Direction Direction
Protocol uint8
SourceIP netip.Addr
DestIP netip.Addr
SourcePort uint16
DestPort uint16
ICMPType uint8
ICMPCode uint8
}
type FlowConfig struct {
URL string
Interval time.Duration
Enabled bool
TokenPayload string
TokenSignature string
}
type FlowManager interface {
// FlowConfig handles network map updates
Update(update *FlowConfig) error
// Close closes the manager
Close()
// GetLogger returns a flow logger
GetLogger() FlowLogger
}
type FlowLogger interface {
// StoreEvent stores a flow event
StoreEvent(flowEvent EventFields)
// GetEvents returns all stored events
GetEvents() []*Event
// Close closes the logger
Close()
// Enable enables the flow logger receiver
Enable()
// Disable disables the flow logger receiver
Disable()
}
type Store interface {
// StoreEvent stores a flow event
StoreEvent(event *Event)
// GetEvents returns all stored events
GetEvents() []*Event
// DeleteEvents deletes events from the store
DeleteEvents([]string)
// Close closes the store
Close()
}