Skip to content

Commit 45e8801

Browse files
authored
conntrack: Add _IsFirst field (#328)
* Add isFirst field to connections * Fix tests * Rename variable "test" to avoid collision with imported package name * Add test for short and long connections with isFirst * Extend buildMockConnTrackConfig() to accept updateConnectionInterval & endConnectionTimeout * Update README.md
1 parent 15efbb8 commit 45e8801

File tree

7 files changed

+257
-54
lines changed

7 files changed

+257
-54
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ A possible output would look like:
617617
{
618618
"_RecordType": "endConnection",
619619
"_HashId": "3e8ba98164baecaf",
620+
"_IsFirst": true,
620621
"SrcAddr": "10.0.0.1",
621622
"SrcPort": 1234,
622623
"DstAddr": "10.0.0.2",
@@ -648,6 +649,11 @@ Output fields that set `splitAB: true` (like in `Bytes`) are split into 2 fields
648649
aggregate values separately based on direction A->B and B->A respectively.
649650
When `splitAB` is absent, its default value is `false`.
650651
652+
The boolean field `_IsFirst` exists only in records of type `newConnection`, `updateConnection` and `endConnection`.
653+
It is set to true only on the first record of the connection.
654+
The `_IsFirst` fields is useful in cases where `newConnection` records are not outputted (to reduce the number output records)
655+
and there is a need to count the total number of connections: simply counting `_IsFirst=true`
656+
651657
### Timebased TopK
652658
653659
It is sometimes desirable to return only a subset of records, such as those connections that use the most bandwidth.

pkg/api/conntrack.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
const (
2525
HashIdFieldName = "_HashId"
2626
RecordTypeFieldName = "_RecordType"
27+
IsFirstFieldName = "_IsFirst"
2728
)
2829

2930
type ConnTrack struct {

pkg/pipeline/conntrack_integ_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func TestConnTrack(t *testing.T) {
138138
"TimeFlowStart": 1_637_501_079.0,
139139
"_HashId": "d28db42bcd8aea8f",
140140
"_RecordType": "endConnection",
141+
"_IsFirst": false,
141142
"numFlowLogs": 5.0,
142143
}
143144
// Wait for the record to be eventually forwarded to the writer

pkg/pipeline/extract/conntrack/conn.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ type connection interface {
3636
getNextUpdateReportTime() time.Time
3737
toGenericMap() config.GenericMap
3838
getHash() totalHashType
39+
// markReported marks the connection as has been reported. That is, at least one connection record has been emitted
40+
// for this connection (i.e. newConnection, updateConnection, endConnection).
41+
// It returns true on the first invocation to indicate the first report. Otherwise, it returns false.
42+
markReported() bool
3943
}
4044

4145
type connType struct {
@@ -44,6 +48,7 @@ type connType struct {
4448
aggFields map[string]float64
4549
expiryTime time.Time
4650
nextUpdateReportTime time.Time
51+
isReported bool
4752
}
4853

4954
func (c *connType) addAgg(fieldName string, initValue float64) {
@@ -95,15 +100,22 @@ func (c *connType) getHash() totalHashType {
95100
return c.hash
96101
}
97102

103+
func (c *connType) markReported() bool {
104+
isFirst := !c.isReported
105+
c.isReported = true
106+
return isFirst
107+
}
108+
98109
type connBuilder struct {
99110
conn *connType
100111
}
101112

102113
func NewConnBuilder() *connBuilder {
103114
return &connBuilder{
104115
conn: &connType{
105-
aggFields: make(map[string]float64),
106-
keys: config.GenericMap{},
116+
aggFields: make(map[string]float64),
117+
keys: config.GenericMap{},
118+
isReported: false,
107119
},
108120
}
109121
}

pkg/pipeline/extract/conntrack/conntrack.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ func (ct *conntrackImpl) Extract(flowLogs []config.GenericMap) []config.GenericM
8181
record := conn.toGenericMap()
8282
addHashField(record, computedHash.hashTotal)
8383
addTypeField(record, api.ConnTrackOutputRecordTypeName("NewConnection"))
84+
isFirst := conn.markReported()
85+
addIsFirstField(record, isFirst)
8486
outputRecords = append(outputRecords, record)
8587
ct.metrics.outputRecords.WithLabelValues("newConnection").Inc()
8688
}
@@ -123,6 +125,11 @@ func (ct *conntrackImpl) popEndConnections() []config.GenericMap {
123125
record := conn.toGenericMap()
124126
addHashField(record, conn.getHash().hashTotal)
125127
addTypeField(record, api.ConnTrackOutputRecordTypeName("EndConnection"))
128+
var isFirst bool
129+
if ct.shouldOutputEndConnection {
130+
isFirst = conn.markReported()
131+
}
132+
addIsFirstField(record, isFirst)
126133
outputRecords = append(outputRecords, record)
127134
shouldDelete, shouldStop = true, false
128135
} else {
@@ -144,6 +151,11 @@ func (ct *conntrackImpl) prepareUpdateConnectionRecords() []config.GenericMap {
144151
record := conn.toGenericMap()
145152
addHashField(record, conn.getHash().hashTotal)
146153
addTypeField(record, api.ConnTrackOutputRecordTypeName("UpdateConnection"))
154+
var isFirst bool
155+
if ct.shouldOutputUpdateConnection {
156+
isFirst = conn.markReported()
157+
}
158+
addIsFirstField(record, isFirst)
147159
outputRecords = append(outputRecords, record)
148160
newNextUpdate := ct.clock.Now().Add(ct.config.UpdateConnectionInterval.Duration)
149161
ct.connStore.updateNextReportTime(conn.getHash().hashTotal, newNextUpdate)
@@ -236,3 +248,7 @@ func addHashField(record config.GenericMap, hashId uint64) {
236248
func addTypeField(record config.GenericMap, recordType string) {
237249
record[api.RecordTypeFieldName] = recordType
238250
}
251+
252+
func addIsFirstField(record config.GenericMap, isFirst bool) {
253+
record[api.IsFirstFieldName] = isFirst
254+
}

0 commit comments

Comments
 (0)