filter: prevent escape of QDecode to the heap (#417)

Performance impact:

name              old time/op  new time/op  delta
Filter/tcp_in-4   70.7ns ± 1%  30.9ns ± 1%  -56.30%  (p=0.008 n=5+5)
Filter/tcp_out-4  58.6ns ± 0%  19.4ns ± 0%  -66.87%  (p=0.000 n=5+4)
Filter/udp_in-4   96.8ns ± 2%  55.5ns ± 0%  -42.64%  (p=0.016 n=5+4)
Filter/udp_out-4   120ns ± 1%    79ns ± 1%  -33.87%  (p=0.008 n=5+5)

Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
Dmytro Shynkevych
2020-06-02 08:09:20 -04:00
committed by GitHub
parent 83b6b06cc4
commit 73c40c77b0
2 changed files with 153 additions and 37 deletions
+14 -4
View File
@@ -138,16 +138,26 @@ var acceptBucket = rate.NewLimiter(rate.Every(10*time.Second), 3)
var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10)
func (f *Filter) logRateLimit(runflags RunFlags, b []byte, q *packet.QDecode, r Response, why string) {
var verdict string
if r == Drop && (runflags&LogDrops) != 0 && dropBucket.Allow() {
verdict = "Drop"
runflags &= HexdumpDrops
} else if r == Accept && (runflags&LogAccepts) != 0 && acceptBucket.Allow() {
verdict = "Accept"
runflags &= HexdumpAccepts
}
// Note: it is crucial that q.String() be called only if {accept,drop}Bucket.Allow() passes,
// since it causes an allocation.
if verdict != "" {
var qs string
if q == nil {
qs = fmt.Sprintf("(%d bytes)", len(b))
} else {
qs = q.String()
}
f.logf("Drop: %v %v %s\n%s", qs, len(b), why, maybeHexdump(runflags&HexdumpDrops, b))
} else if r == Accept && (runflags&LogAccepts) != 0 && acceptBucket.Allow() {
f.logf("Accept: %v %v %s\n%s", q, len(b), why, maybeHexdump(runflags&HexdumpAccepts, b))
f.logf("%s: %s %d %s\n%s", verdict, qs, len(b), why, maybeHexdump(runflags, b))
}
}
@@ -254,7 +264,7 @@ func (f *Filter) pre(b []byte, q *packet.QDecode, rf RunFlags) Response {
if q.IPProto == packet.Junk {
// Junk packets are dangerous; always drop them.
f.logRateLimit(rf, b, q, Drop, "junk!")
f.logRateLimit(rf, b, q, Drop, "junk")
return Drop
} else if q.IPProto == packet.Fragment {
// Fragments after the first always need to be passed through.