Skip to content

Commit ae899ec

Browse files
authored
Merge pull request #111 from DCSO/full-flow-aggregation
implement full flow aggregation
2 parents c015441 + ac6a091 commit ae899ec

File tree

5 files changed

+94
-15
lines changed

5 files changed

+94
-15
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to FEVER will be documented in this file.
44

5+
## [1.3.6] - 2024-07-03
6+
7+
### Added
8+
- Add support for sending aggregations from all flows, not just TCP
9+
bidirectional ones.
10+
511
## [1.3.5] - 2023-03-27
612

713
### Fixed

cmd/fever/cmds/run.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,9 @@ func mainfunc(cmd *cobra.Command, args []string) {
409409
"state": "disabled",
410410
}).Info("compression of flow stats")
411411
}
412-
ua := processing.MakeUnicornAggregator(submitter, unicornSleep, dummyMode)
412+
allFlows := viper.GetBool("flowreport.all")
413+
ua := processing.MakeUnicornAggregator(submitter, unicornSleep, dummyMode,
414+
allFlows)
413415
testSrcIP := viper.GetString("flowreport.testdata-srcip")
414416
testDestIP := viper.GetString("flowreport.testdata-destip")
415417
testDestPort := viper.GetInt64("flowreport.testdata-destport")

fever.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ flowreport:
8585
#testdata-srcip: 0.0.0.1
8686
#testdata-destip: 0.0.0.2
8787
#testdata-destport: 99999
88+
# Set to true to count _all_ flows, not just TCP bidirectional ones.
89+
all: false
8890

8991
# Configuration for metrics (i.e. InfluxDB) submission.
9092
metrics:
@@ -165,4 +167,4 @@ mgmt:
165167
socket: /tmp/fever-mgmt.sock
166168
# Use network server for gRPC commmunication.
167169
#network: tcp
168-
#host: localhost:9999
170+
#host: localhost:9999

processing/unicorn_aggregator.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type UnicornAggregator struct {
4545
TestFlowSrcIP string
4646
TestFlowDestIP string
4747
TestFlowDestPort int64
48+
AllFlows bool
4849
}
4950

5051
// MakeUnicornAggregate creates a new empty UnicornAggregate object.
@@ -58,7 +59,7 @@ func MakeUnicornAggregate() *UnicornAggregate {
5859

5960
// MakeUnicornAggregator creates a new empty UnicornAggregator object.
6061
func MakeUnicornAggregator(statsSubmitter util.StatsSubmitter,
61-
submitPeriod time.Duration, dummyMode bool) *UnicornAggregator {
62+
submitPeriod time.Duration, dummyMode bool, allFlows bool) *UnicornAggregator {
6263
a := &UnicornAggregator{
6364
Logger: log.WithFields(log.Fields{
6465
"domain": "aggregate",
@@ -70,6 +71,7 @@ func MakeUnicornAggregator(statsSubmitter util.StatsSubmitter,
7071
ClosedChan: make(chan bool),
7172
Aggregate: *MakeUnicornAggregate(),
7273
TestFlowDestPort: 99999,
74+
AllFlows: allFlows,
7375
}
7476
return a
7577
}
@@ -197,7 +199,7 @@ func (a *UnicornAggregator) Stop(stopChan chan bool) {
197199
// aggregated state
198200
func (a *UnicornAggregator) Consume(e *types.Entry) error {
199201
// Unicorn flow aggregation update
200-
if e.EventType == "flow" && e.Proto == "TCP" && e.BytesToClient > 0 {
202+
if e.EventType == "flow" && (a.AllFlows || (e.Proto == "TCP" && e.BytesToClient > 0)) {
201203
a.StringBuf.Write([]byte(e.SrcIP))
202204
a.StringBuf.Write([]byte("_"))
203205
a.StringBuf.Write([]byte(e.DestIP))

processing/unicorn_aggregator_test.go

+78-11
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import (
1515
log "github.com/sirupsen/logrus"
1616
)
1717

18-
func makeUnicornFlowEvent() types.Entry {
18+
func makeUnicornFlowEvent(proto string) types.Entry {
1919
e := types.Entry{
2020
SrcIP: fmt.Sprintf("10.%d.%d.%d", rand.Intn(250), rand.Intn(250), rand.Intn(250)),
2121
SrcPort: []int64{1, 2, 3, 4, 5}[rand.Intn(5)],
2222
DestIP: fmt.Sprintf("10.0.0.%d", rand.Intn(250)),
2323
DestPort: []int64{11, 12, 13, 14, 15}[rand.Intn(5)],
2424
Timestamp: time.Now().Format(types.SuricataTimestampFormat),
2525
EventType: "flow",
26-
Proto: "TCP",
26+
Proto: proto,
2727
BytesToClient: int64(rand.Intn(10000)),
2828
BytesToServer: int64(rand.Intn(10000)),
2929
PktsToClient: int64(rand.Intn(100)),
@@ -101,7 +101,7 @@ func TestUnicornAggregatorNoSubmission(t *testing.T) {
101101
dsub := &testSubmitter{
102102
Data: make([]string, 0),
103103
}
104-
f := MakeUnicornAggregator(dsub, 100*time.Millisecond, false)
104+
f := MakeUnicornAggregator(dsub, 100*time.Millisecond, false, false)
105105
f.Run()
106106

107107
time.Sleep(1 * time.Second)
@@ -128,12 +128,12 @@ func TestUnicornAggregator(t *testing.T) {
128128
dsub := &testSubmitter{
129129
Data: make([]string, 0),
130130
}
131-
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false)
131+
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false, false)
132132
f.Run()
133133

134134
createdFlows := make(map[string]int)
135135
for i := 0; i < 200000; i++ {
136-
ev := makeUnicornFlowEvent()
136+
ev := makeUnicornFlowEvent("TCP")
137137
if ev.BytesToClient > 0 {
138138
key := fmt.Sprintf("%s_%s_%d", ev.SrcIP, ev.DestIP, ev.DestPort)
139139
createdFlows[key]++
@@ -189,7 +189,7 @@ func TestUnicornAggregatorWithTestdata(t *testing.T) {
189189
dsub := &testSubmitter{
190190
Data: make([]string, 0),
191191
}
192-
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false)
192+
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false, false)
193193
f.EnableTestFlow("1.2.3.4", "5.6.7.8", 33333)
194194
f.Run()
195195

@@ -239,7 +239,7 @@ func TestUnicornAggregatorWithDispatch(t *testing.T) {
239239
dsub := &testSubmitter{
240240
Data: make([]string, 0),
241241
}
242-
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false)
242+
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false, false)
243243
feedWaitChan := make(chan bool)
244244
outChan := make(chan types.Entry)
245245

@@ -256,17 +256,21 @@ func TestUnicornAggregatorWithDispatch(t *testing.T) {
256256
f.Run()
257257

258258
createdFlows := make(map[string]int)
259-
for i := 0; i < 200000; i++ {
260-
ev := makeUnicornFlowEvent()
261-
if ev.BytesToClient > 0 {
259+
for i := 0; i < 400000; i++ {
260+
proto := "TCP"
261+
if i%2 == 0 {
262+
proto = "UDP"
263+
}
264+
ev := makeUnicornFlowEvent(proto)
265+
if proto == "TCP" && ev.BytesToClient > 0 {
262266
key := fmt.Sprintf("%s_%s_%d", ev.SrcIP, ev.DestIP, ev.DestPort)
263267
createdFlows[key]++
264268
}
265269
d.Dispatch(&ev)
266270
}
267271

268272
for {
269-
if dsub.GetTotalAggs() < len(createdFlows) {
273+
if dsub.GetTotalAggs() < (len(createdFlows) / 2) {
270274
log.Debug(dsub.GetTotalAggs())
271275
time.Sleep(100 * time.Millisecond)
272276
} else {
@@ -309,3 +313,66 @@ func TestUnicornAggregatorWithDispatch(t *testing.T) {
309313
}
310314
}
311315
}
316+
317+
func TestUnicornMixedUDPTCP(t *testing.T) {
318+
rand.Seed(time.Now().UTC().UnixNano())
319+
dsub := &testSubmitter{
320+
Data: make([]string, 0),
321+
}
322+
f := MakeUnicornAggregator(dsub, 500*time.Millisecond, false, true)
323+
f.Run()
324+
325+
createdFlows := make(map[string]int)
326+
for i := 0; i < 200000; i++ {
327+
proto := "TCP"
328+
if i%2 == 0 {
329+
proto = "UDP"
330+
}
331+
ev := makeUnicornFlowEvent(proto)
332+
key := fmt.Sprintf("%s_%s_%d", ev.SrcIP, ev.DestIP, ev.DestPort)
333+
createdFlows[key]++
334+
f.Consume(&ev)
335+
}
336+
337+
for {
338+
if dsub.GetTotalAggs() < len(createdFlows) {
339+
log.Debug(dsub.GetTotalAggs())
340+
time.Sleep(100 * time.Millisecond)
341+
} else {
342+
break
343+
}
344+
}
345+
346+
consumeWaitChan := make(chan bool)
347+
f.Stop(consumeWaitChan)
348+
<-consumeWaitChan
349+
350+
if len(dsub.Data) == 0 {
351+
t.Fatalf("collected aggregations are empty")
352+
}
353+
354+
log.Info(dsub.GetTotalAggs(), len(createdFlows), len(dsub.Data))
355+
356+
var totallen int
357+
for _, v := range dsub.Data {
358+
totallen += len(v)
359+
}
360+
if totallen == 0 {
361+
t.Fatalf("length of collected aggregations is zero")
362+
}
363+
364+
if dsub.GetTotalAggs() != len(createdFlows) {
365+
t.Fatalf("unexpected number of flow aggregates: %d/%d", dsub.GetTotalAggs(),
366+
len(createdFlows))
367+
}
368+
369+
for k, v := range dsub.GetFlowTuples() {
370+
if _, ok := createdFlows[k]; !ok {
371+
t.Fatalf("missing flow aggregate: %s", k)
372+
}
373+
if v["count"] != int64(createdFlows[k]) {
374+
t.Fatalf("unexpected number of flows for %s: %d/%d",
375+
k, v["count"], createdFlows[k])
376+
}
377+
}
378+
}

0 commit comments

Comments
 (0)