github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/ebpftracer/policy_filters.go (about) 1 package ebpftracer 2 3 import ( 4 "errors" 5 "log/slog" 6 "time" 7 8 "github.com/castai/kvisor/pkg/ebpftracer/events" 9 "github.com/castai/kvisor/pkg/ebpftracer/types" 10 "github.com/castai/kvisor/pkg/logging" 11 "github.com/cespare/xxhash" 12 "github.com/elastic/go-freelru" 13 "github.com/samber/lo" 14 "golang.org/x/time/rate" 15 ) 16 17 var ( 18 FilterPass error = nil 19 FilterErrRateLimit = errors.New("rate limit") 20 FilterErrEmptyDNSResponse = errors.New("empty dns response") 21 FilterErrDNSDuplicateDetected = errors.New("dns duplicate detected") 22 ) 23 24 // GlobalPreEventFilterGenerator always returns the given filter on each generator invocation. This is useful, 25 // if you want some global filtering across cgroups. 26 func GlobalPreEventFilterGenerator(filter PreEventFilter) PreEventFilterGenerator { 27 return func() PreEventFilter { 28 return filter 29 } 30 } 31 32 // GlobalEventFilterGenerator always returns the given filter on each generator invocation. This is useful, 33 // if you want some global filtering across cgroups. 34 func GlobalEventFilterGenerator(filter EventFilter) EventFilterGenerator { 35 return func() EventFilter { 36 return filter 37 } 38 } 39 40 func FilterAnd(filtersGenerators ...EventFilterGenerator) EventFilterGenerator { 41 return func() EventFilter { 42 filters := lo.Map(filtersGenerators, func(generator EventFilterGenerator, index int) EventFilter { 43 return generator() 44 }) 45 46 return func(event *types.Event) error { 47 for _, f := range filters { 48 if err := f(event); err != nil { 49 return err 50 } 51 } 52 53 return FilterPass 54 } 55 } 56 } 57 58 // PreRateLimit creates an pre event filter that limits the amount of events that will be 59 // processed accoring to the specified limits 60 func PreRateLimit(spec RateLimitPolicy) PreEventFilterGenerator { 61 return func() PreEventFilter { 62 rateLimiter := newRateLimiter(spec) 63 64 return func(ctx *types.EventContext) error { 65 if rateLimiter.Allow() { 66 return FilterPass 67 } 68 69 return FilterErrRateLimit 70 } 71 } 72 } 73 74 func RateLimit(spec RateLimitPolicy) EventFilterGenerator { 75 return func() EventFilter { 76 rateLimiter := newRateLimiter(spec) 77 78 return func(event *types.Event) error { 79 if rateLimiter.Allow() { 80 return FilterPass 81 } 82 83 return FilterErrRateLimit 84 } 85 } 86 } 87 88 func newRateLimiter(spec RateLimitPolicy) *rate.Limiter { 89 var limit rate.Limit 90 91 if spec.Interval != 0 { 92 limit = rate.Every(spec.Interval) 93 spec.Burst = 1 94 } else { 95 limit = rate.Limit(spec.Rate) 96 if spec.Burst == 0 { 97 spec.Burst = 1 98 } 99 } 100 101 rateLimiter := rate.NewLimiter(limit, spec.Burst) 102 return rateLimiter 103 } 104 105 // FilterEmptyDnsAnswers will drop any DNS event, that is missing an answer section 106 func FilterEmptyDnsAnswers(l *logging.Logger) EventFilterGenerator { 107 return func() EventFilter { 108 return func(event *types.Event) error { 109 if event.Context.EventID != events.NetPacketDNSBase { 110 return FilterPass 111 } 112 113 dnsEventArgs, ok := event.Args.(types.NetPacketDNSBaseArgs) 114 if !ok { 115 return FilterPass 116 } 117 118 if dnsEventArgs.Payload == nil { 119 l.Warn("retreived invalid event for event type dns") 120 return FilterPass 121 } 122 123 if len(dnsEventArgs.Payload.Answers) == 0 { 124 return FilterErrEmptyDNSResponse 125 } 126 127 return FilterPass 128 } 129 } 130 } 131 132 // more hash function in https://github.com/elastic/go-freelru/blob/main/bench/hash.go 133 func hashStringXXHASH(s string) uint32 { 134 return uint32(xxhash.Sum64String(s)) 135 } 136 137 // DeduplicateDnsEvents creates a filter that will drop any DNS event with questions already seen in `ttl` time 138 func DeduplicateDnsEvents(l *logging.Logger, size uint32, ttl time.Duration) EventFilterGenerator { 139 type cacheValue struct{} 140 141 return func() EventFilter { 142 cache, err := freelru.New[string, cacheValue](size, hashStringXXHASH) 143 // err is only ever returned on configuration issues. There is nothing we can really do here, besides 144 // panicing and surfacing the error to the user. 145 if err != nil { 146 panic(err) 147 } 148 149 cache.SetLifetime(ttl) 150 151 return func(event *types.Event) error { 152 if event.Context.EventID != events.NetPacketDNSBase { 153 return FilterPass 154 } 155 156 dnsEventArgs, ok := event.Args.(types.NetPacketDNSBaseArgs) 157 if !ok { 158 return FilterPass 159 } 160 161 if dnsEventArgs.Payload == nil { 162 l.Warn("received invalid event for event type dns") 163 return FilterPass 164 } 165 166 cacheKey := dnsEventArgs.Payload.DNSQuestionDomain 167 if cache.Contains(cacheKey) { 168 if l.IsEnabled(slog.LevelDebug) { 169 l.WithField("cachekey", cacheKey).Debug("dropping DNS event") 170 } 171 return FilterErrDNSDuplicateDetected 172 } 173 174 cache.Add(cacheKey, cacheValue{}) 175 176 return FilterPass 177 } 178 } 179 }