mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-05 04:59:06 +02:00
[client,management] add netflow support to client and update management (#3414)
adds NetFlow functionality to track and log network traffic information between peers, with features including: - Flow logging for TCP, UDP, and ICMP traffic - Integration with connection tracking system - Resource ID tracking in NetFlow events - DNS and exit node collection configuration - Flow API and Redis cache in management - Memory-based flow storage implementation - Kernel conntrack counters and userspace counters - TCP state machine improvements for more accurate tracking - Migration from net.IP to netip.Addr in the userspace firewall
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type routeIDLookup struct {
|
||||
localMap sync.Map
|
||||
remoteMap sync.Map
|
||||
resolvedIPs sync.Map
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) AddLocalRouteID(resourceID string, route netip.Prefix) {
|
||||
_, exists := r.localMap.LoadOrStore(route, resourceID)
|
||||
if exists {
|
||||
log.Tracef("resourceID %s already exists in local map", resourceID)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) RemoveLocalRouteID(route netip.Prefix) {
|
||||
r.localMap.Delete(route)
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) AddRemoteRouteID(resourceID string, route netip.Prefix) {
|
||||
_, exists := r.remoteMap.LoadOrStore(route, resourceID)
|
||||
if exists {
|
||||
log.Tracef("resourceID %s already exists in remote map", resourceID)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) RemoveRemoteRouteID(route netip.Prefix) {
|
||||
r.remoteMap.Delete(route)
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) AddResolvedIP(resourceID string, route netip.Prefix) {
|
||||
r.resolvedIPs.Store(route.Addr(), resourceID)
|
||||
}
|
||||
|
||||
func (r *routeIDLookup) RemoveResolvedIP(route netip.Prefix) {
|
||||
r.resolvedIPs.Delete(route.Addr())
|
||||
}
|
||||
|
||||
// Lookup returns the resource ID for the given IP address
|
||||
// and a bool indicating if the IP is an exit node
|
||||
func (r *routeIDLookup) Lookup(ip netip.Addr) (string, bool) {
|
||||
var isExitNode bool
|
||||
|
||||
resId, ok := r.resolvedIPs.Load(ip)
|
||||
if ok {
|
||||
return resId.(string), false
|
||||
}
|
||||
|
||||
var resourceID string
|
||||
r.localMap.Range(func(key, value interface{}) bool {
|
||||
pref := key.(netip.Prefix)
|
||||
if pref.Contains(ip) {
|
||||
resourceID = value.(string)
|
||||
isExitNode = pref.Bits() == 0
|
||||
return false
|
||||
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if resourceID == "" {
|
||||
r.remoteMap.Range(func(key, value interface{}) bool {
|
||||
pref := key.(netip.Prefix)
|
||||
if pref.Contains(ip) {
|
||||
resourceID = value.(string)
|
||||
isExitNode = pref.Bits() == 0
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return resourceID, isExitNode
|
||||
}
|
||||
@@ -176,6 +176,8 @@ type Status struct {
|
||||
eventQueue *EventQueue
|
||||
|
||||
ingressGwMgr *ingressgw.Manager
|
||||
|
||||
routeIDLookup routeIDLookup
|
||||
}
|
||||
|
||||
// NewRecorder returns a new Status instance
|
||||
@@ -311,7 +313,7 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Status) AddPeerStateRoute(peer string, route string) error {
|
||||
func (d *Status) AddPeerStateRoute(peer string, route string, resourceId string) error {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
@@ -323,6 +325,11 @@ func (d *Status) AddPeerStateRoute(peer string, route string) error {
|
||||
peerState.AddRoute(route)
|
||||
d.peers[peer] = peerState
|
||||
|
||||
pref, err := netip.ParsePrefix(route)
|
||||
if err == nil {
|
||||
d.routeIDLookup.AddRemoteRouteID(resourceId, pref)
|
||||
}
|
||||
|
||||
// todo: consider to make sense of this notification or not
|
||||
d.notifyPeerListChanged()
|
||||
return nil
|
||||
@@ -340,11 +347,26 @@ func (d *Status) RemovePeerStateRoute(peer string, route string) error {
|
||||
peerState.DeleteRoute(route)
|
||||
d.peers[peer] = peerState
|
||||
|
||||
pref, err := netip.ParsePrefix(route)
|
||||
if err == nil {
|
||||
d.routeIDLookup.RemoveRemoteRouteID(pref)
|
||||
}
|
||||
|
||||
// todo: consider to make sense of this notification or not
|
||||
d.notifyPeerListChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckRoutes checks if the source and destination addresses are within the same route
|
||||
// and returns the resource ID of the route that contains the addresses
|
||||
func (d *Status) CheckRoutes(ip netip.Addr) ([]byte, bool) {
|
||||
if d == nil {
|
||||
return nil, false
|
||||
}
|
||||
resId, isExitNode := d.routeIDLookup.Lookup(ip)
|
||||
return []byte(resId), isExitNode
|
||||
}
|
||||
|
||||
func (d *Status) UpdatePeerICEState(receivedState State) error {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
@@ -558,6 +580,50 @@ func (d *Status) UpdateLocalPeerState(localPeerState LocalPeerState) {
|
||||
d.notifyAddressChanged()
|
||||
}
|
||||
|
||||
// AddLocalPeerStateRoute adds a route to the local peer state
|
||||
func (d *Status) AddLocalPeerStateRoute(route, resourceId string) {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
pref, err := netip.ParsePrefix(route)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse prefix %s: %v", route, err)
|
||||
return
|
||||
}
|
||||
|
||||
if d.localPeer.Routes == nil {
|
||||
d.localPeer.Routes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
d.localPeer.Routes[route] = struct{}{}
|
||||
|
||||
d.routeIDLookup.AddLocalRouteID(resourceId, pref)
|
||||
}
|
||||
|
||||
// RemoveLocalPeerStateRoute removes a route from the local peer state
|
||||
func (d *Status) RemoveLocalPeerStateRoute(route string) {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
pref, err := netip.ParsePrefix(route)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse prefix %s: %v", route, err)
|
||||
return
|
||||
}
|
||||
|
||||
delete(d.localPeer.Routes, route)
|
||||
|
||||
d.routeIDLookup.RemoveLocalRouteID(pref)
|
||||
}
|
||||
|
||||
// CleanLocalPeerStateRoutes cleans all routes from the local peer state
|
||||
func (d *Status) CleanLocalPeerStateRoutes() {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
d.localPeer.Routes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
// CleanLocalPeerState cleans local peer status
|
||||
func (d *Status) CleanLocalPeerState() {
|
||||
d.mux.Lock()
|
||||
@@ -641,7 +707,7 @@ func (d *Status) UpdateDNSStates(dnsStates []NSGroupState) {
|
||||
d.nsGroupStates = dnsStates
|
||||
}
|
||||
|
||||
func (d *Status) UpdateResolvedDomainsStates(originalDomain domain.Domain, resolvedDomain domain.Domain, prefixes []netip.Prefix) {
|
||||
func (d *Status) UpdateResolvedDomainsStates(originalDomain domain.Domain, resolvedDomain domain.Domain, prefixes []netip.Prefix, resourceId string) {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
@@ -650,6 +716,10 @@ func (d *Status) UpdateResolvedDomainsStates(originalDomain domain.Domain, resol
|
||||
Prefixes: prefixes,
|
||||
ParentDomain: originalDomain,
|
||||
}
|
||||
|
||||
for _, prefix := range prefixes {
|
||||
d.routeIDLookup.AddResolvedIP(resourceId, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Status) DeleteResolvedDomainsStates(domain domain.Domain) {
|
||||
@@ -660,6 +730,10 @@ func (d *Status) DeleteResolvedDomainsStates(domain domain.Domain) {
|
||||
for k, v := range d.resolvedDomainsStates {
|
||||
if v.ParentDomain == domain {
|
||||
delete(d.resolvedDomainsStates, k)
|
||||
|
||||
for _, prefix := range v.Prefixes {
|
||||
d.routeIDLookup.RemoveResolvedIP(prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user