Skip to content

Commit 0d97d06

Browse files
committed
filter: support filtering dump/flush by flow status
Signed-off-by: Timo Beckers <[email protected]>
1 parent fcafe27 commit 0d97d06

File tree

5 files changed

+99
-31
lines changed

5 files changed

+99
-31
lines changed

attributetype_string.go

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enum.go

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,34 @@ type attributeType uint8
4444

4545
// enum ctattr_type
4646
const (
47-
ctaUnspec attributeType = iota // CTA_UNSPEC
48-
ctaTupleOrig // CTA_TUPLE_ORIG
49-
ctaTupleReply // CTA_TUPLE_REPLY
50-
ctaStatus // CTA_STATUS
51-
ctaProtoInfo // CTA_PROTOINFO
52-
ctaHelp // CTA_HELP
53-
ctaNatSrc // CTA_NAT_SRC, Deprecated
54-
ctaTimeout // CTA_TIMEOUT
55-
ctaMark // CTA_MARK
56-
ctaCountersOrig // CTA_COUNTERS_ORIG
57-
ctaCountersReply // CTA_COUNTERS_REPLY
58-
ctaUse // CTA_USE
59-
ctaID // CTA_ID
60-
ctaNatDst // CTA_NAT_DST, Deprecated
61-
ctaTupleMaster // CTA_TUPLE_MASTER
62-
ctaSeqAdjOrig // CTA_SEQ_ADJ_ORIG
63-
ctaSeqAdjReply // CTA_SEQ_ADJ_REPLY
64-
ctaSecMark // CTA_SECMARK, Deprecated
65-
ctaZone // CTA_ZONE
66-
ctaSecCtx // CTA_SECCTX
67-
ctaTimestamp // CTA_TIMESTAMP
68-
ctaMarkMask // CTA_MARK_MASK
69-
ctaLabels // CTA_LABELS
70-
ctaLabelsMask // CTA_LABELS_MASK
71-
ctaSynProxy // CTA_SYNPROXY
47+
ctaUnspec attributeType = iota // CTA_UNSPEC
48+
ctaTupleOrig // CTA_TUPLE_ORIG
49+
ctaTupleReply // CTA_TUPLE_REPLY
50+
ctaStatus // CTA_STATUS
51+
ctaProtoInfo // CTA_PROTOINFO
52+
ctaHelp // CTA_HELP
53+
ctaNatSrc // CTA_NAT_SRC, Deprecated
54+
ctaTimeout // CTA_TIMEOUT
55+
ctaMark // CTA_MARK
56+
ctaCountersOrig // CTA_COUNTERS_ORIG
57+
ctaCountersReply // CTA_COUNTERS_REPLY
58+
ctaUse // CTA_USE
59+
ctaID // CTA_ID
60+
ctaNatDst // CTA_NAT_DST, Deprecated
61+
ctaTupleMaster // CTA_TUPLE_MASTER
62+
ctaSeqAdjOrig // CTA_SEQ_ADJ_ORIG
63+
ctaSeqAdjReply // CTA_SEQ_ADJ_REPLY
64+
ctaSecMark // CTA_SECMARK, Deprecated
65+
ctaZone // CTA_ZONE
66+
ctaSecCtx // CTA_SECCTX
67+
ctaTimestamp // CTA_TIMESTAMP
68+
ctaMarkMask // CTA_MARK_MASK
69+
ctaLabels // CTA_LABELS
70+
ctaLabelsMask // CTA_LABELS_MASK
71+
ctaSynProxy // CTA_SYNPROXY
72+
ctaFilter // CTA_FILTER
73+
ctaStatusMask // CTA_STATUS_MASK
74+
ctaTimestampEvent // CTA_TIMESTAMP_EVENT
7275
)
7376

7477
// tupleType describes the type of tuple contained in this container.

filter.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import (
66

77
// Filter is an object used to limit dump and flush operations to flows matching
88
// certain fields. Use [NewFilter] to create a new filter, then chain methods to
9-
// set filter fields.
9+
// set filter fields. Methods mutate the Filter in place and return it for
10+
// chaining purposes.
1011
//
1112
// Pass a filter to [Conn.DumpFilter] or [Conn.FlushFilter].
1213
type Filter interface {
@@ -23,6 +24,22 @@ type Filter interface {
2324
// match exactly.
2425
MarkMask(mask uint32) Filter
2526

27+
// Status sets the conntrack status bits to filter on, similar to conntrack's
28+
// -u/--status option.
29+
//
30+
// Requires Linux 5.15 or later.
31+
Status(status Status) Filter
32+
33+
// StatusMask overrides the mask to apply before filtering on flow status.
34+
// Since Status is a bitfield, mask defaults to the mark value itself since
35+
// matching on the entire field would typically yield few matches. It's
36+
// recommended to leave this unset unless you have a specific need.
37+
//
38+
// Doesn't have an equivalent in the conntrack CLI.
39+
//
40+
// Requires Linux 5.15 or later.
41+
StatusMask(mask uint32) Filter
42+
2643
// Zone sets the conntrack zone to filter on, similar to conntrack's -w/--zone
2744
// option.
2845
//
@@ -53,6 +70,16 @@ func (f *filter) MarkMask(mask uint32) Filter {
5370
return f
5471
}
5572

73+
func (f *filter) Status(status Status) Filter {
74+
f.f[ctaStatus] = netfilter.Uint32Bytes(uint32(status.Value))
75+
return f
76+
}
77+
78+
func (f *filter) StatusMask(mask uint32) Filter {
79+
f.f[ctaStatusMask] = netfilter.Uint32Bytes(mask)
80+
return f
81+
}
82+
5683
func (f *filter) Zone(zone uint16) Filter {
5784
f.f[ctaZone] = netfilter.Uint16Bytes(zone)
5885
return f

filter_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,16 @@ import (
1010
)
1111

1212
func TestFilterMarshal(t *testing.T) {
13-
f := NewFilter().Mark(0xf0000000).MarkMask(0x0000000f).Zone(42)
13+
f := NewFilter().
14+
Mark(0xf0000000).MarkMask(0x0000000f).
15+
Zone(42).
16+
Status(Status{StatusDying}).StatusMask(0xdeadbeef)
17+
1418
want := []netfilter.Attribute{
19+
{
20+
Type: uint16(ctaStatus),
21+
Data: []byte{0, 0, 0x2, 0},
22+
},
1523
{
1624
Type: uint16(ctaMark),
1725
Data: []byte{0xf0, 0, 0, 0},
@@ -24,6 +32,10 @@ func TestFilterMarshal(t *testing.T) {
2432
Type: uint16(ctaMarkMask),
2533
Data: []byte{0, 0, 0, 0x0f},
2634
},
35+
{
36+
Type: uint16(ctaStatusMask),
37+
Data: []byte{0xde, 0xad, 0xbe, 0xef},
38+
},
2739
}
2840

2941
got := f.marshal()

flow_integration_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,6 @@ func BenchmarkCreateDeleteFlow(b *testing.B) {
413413
}
414414
}
415415

416-
// Creates flows in a specific zone, dumps them using zone filter, flushes them using zone filter,
417-
// and verifies they are removed. Requires Linux kernel 6.8 or greater for zone filtering support.
418416
func TestZoneFilter(t *testing.T) {
419417
c, _, err := makeNSConn()
420418
require.NoError(t, err)
@@ -467,3 +465,28 @@ func TestZoneFilter(t *testing.T) {
467465
require.NoError(t, err)
468466
assert.Empty(t, flows, "expected no flows in zone 100 after flush")
469467
}
468+
469+
func TestStatusFilter(t *testing.T) {
470+
c, _, err := makeNSConn()
471+
require.NoError(t, err)
472+
473+
require.NoError(t, c.Create(NewFlow(6, StatusConfirmed, netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("0.0.0.0"), 1234, 80, 120, 0)))
474+
require.NoError(t, c.Create(NewFlow(6, StatusConfirmed, netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("0.0.0.0"), 1234, 80, 120, 0)))
475+
476+
flows, err := c.Dump(nil)
477+
require.NoError(t, err)
478+
assert.Len(t, flows, 2, "expected 2 flows in total")
479+
480+
flows, err = c.DumpFilter(NewFilter().Status(Status{StatusConfirmed}), nil)
481+
require.NoError(t, err)
482+
assert.Len(t, flows, 2)
483+
484+
flows, err = c.DumpFilter(NewFilter().Status(Status{StatusDying}), nil)
485+
require.NoError(t, err)
486+
assert.Len(t, flows, 0)
487+
488+
// This filter can never return anything since status and mask don't overlap.
489+
flows, err = c.DumpFilter(NewFilter().Status(Status{StatusConfirmed}).StatusMask(0x1), nil)
490+
require.NoError(t, err)
491+
assert.Len(t, flows, 0)
492+
}

0 commit comments

Comments
 (0)