8000 feat(transformer): add configurable unique fields to traffic reducer by dmachard · Pull Request #905 · dmachard/DNS-collector · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(transformer): add configurable unique fields to traffic reducer #905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<p align="center">
<img src="https://goreportcard.com/badge/github.com/dmachard/go-dns-collector" alt="Go Report"/>
<img src="https://img.shields.io/badge/go%20version-min%201.21-green" alt="Go version"/>
<img src="https://img.shields.io/badge/go%20tests-516-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20tests-517-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20bench-21-green" alt="Go bench"/>
<img src="https://img.shields.io/badge/go%20lines-32515-green" alt="Go lines"/>
<img src="https://img.shields.io/badge/go%20lines-32537-green" alt="Go lines"/>
</p>

<p align="center">
Expand Down
10 changes: 5 additions & 5 deletions dnsutils/dnsmessage_matching.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (dm *DNSMessage) Matching(matching map[string]interface{}) (error, bool) {
var isMatch = true

for nestedKeys, value := range matching {
realValue, found := getFieldByJSONTag(dmValue, nestedKeys)
realValue, found := GetFieldByJSONTag(dmValue, nestedKeys)
if !found {
return nil, false
}
Expand Down Expand Up @@ -429,7 +429,7 @@ func matchUserPattern(realValue, expectedValue reflect.Value) (bool, error) {
}

// getFieldByJSONTag retrieves a field value from a struct based on JSON tags.
func getFieldByJSONTag(value reflect.Value, nestedKeys string) (reflect.Value, bool) {
func GetFieldByJSONTag(value reflect.Value, nestedKeys string) (reflect.Value, bool) {
listKeys := strings.SplitN(nestedKeys, ".", 2)
jsonKey := listKeys[0]
var remainingKeys string
Expand Down Expand Up @@ -464,18 +464,18 @@ func getFieldByJSONTag(value reflect.Value, nestedKeys string) (reflect.Value, b
// Recurse into structs or handle slices
switch fieldValue.Kind() {
case reflect.Struct:
return getFieldByJSONTag(fieldValue, remainingKeys)
return GetFieldByJSONTag(fieldValue, remainingKeys)
case reflect.Slice:
if sliceElem, leftKey, found := getSliceElement(fieldValue, remainingKeys); found {
// Handle the slice element based on its kind
switch sliceElem.Kind() {
case reflect.Struct:
return getFieldByJSONTag(sliceElem, leftKey)
return GetFieldByJSONTag(sliceElem, leftKey)
case reflect.Slice, reflect.Array:
var result []interface{}
for i := 0; i < sliceElem.Len(); i++ {
if subElem := sliceElem.Index(i); subElem.Kind() == reflect.Struct {
if nestedValue, found := getFieldByJSONTag(subElem, leftKey); found {
if nestedValue, found := GetFieldByJSONTag(subElem, leftKey); found {
result = append(result, nestedValue.Interface())
}
} else {
Expand Down
2 changes: 2 additions & 0 deletions docs/transformers/transform_newdomaintracker.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ The **New Domain Tracker** transformer identifies domains that are newly observe
* `white-domains-file` (string)
> path file to domain white list, domains list can be a partial domain name with regexp expression

* `persistence-file` (string)
> enable the persistence feature by specifying a file path

```yaml
transforms:
Expand Down
38 changes: 25 additions & 13 deletions docs/transformers/transform_trafficreducer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@
# Transformer: Traffic Reducer

Use this transformer to detect repetitive traffic.
A query or reply is repeated when the following criterias are the same.
A query or reply is considered repeated when the specified criteria match.

The following criterias are used:
The following criteria can be configured for detecting repetitions (default one):

- server identity
- operation
- qname or qname+1
- query ip
- qtype
- Server identity
- Operation
- Qname or Qname+1
- Query IP and Response IP
- Qtype

Options:

* `repetitive-traffic-detector` (boolean)
> detect repetitive traffic
> Detect repetitive traffic

* `qname-plus-one` (boolean)
> use qname+1 instead of the complete one
> Use qname+1 instead of the full Qname for matching.

* `watch-interval` (integer)
> watch interval in seconds
> Interval in seconds to aggregate and process the traffic.

* `unique-fields` (array of strings)
> Define custom fields for uniqueness matching (limited to string and integer values).
> This allows greater flexibility in detecting repetitive traffic.
> Complete list of [fields](../dnsconversions.md#json-encoding) available.

Default values:

Expand All @@ -30,13 +35,20 @@ transforms:
reducer:
repetitive-traffic-detector: true
qname-plus-one: false
watch-interval: 5
watch-interval: 2
unique-fields:
- dnstap.identity
- dnstap.operation
- network.query-ip
- network.response-ip
- dns.qname
- dns.qtype
```

Specific text directive(s) available for the text format:
Specific directives available for the text output format:

* `reducer-occurrences`: display the number of detected duplication
* `cumulative-length`: sum of the length of each occurrences
* `cumulative-length`: sums the lengths of all occurrences.

When the feature is enabled, the following json field are populated in your DNS message:

Expand Down
9 changes: 5 additions & 4 deletions pkgconfig/transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ type ConfigTransformers struct {
QueriesTimeout int `yaml:"queries-timeout" default:"2"`
} `yaml:"latency"`
Reducer struct {
Enable bool `yaml:"enable" default:"false"`
RepetitiveTrafficDetector bool `yaml:"repetitive-traffic-detector" default:"false"`
QnamePlusOne bool `yaml:"qname-plus-one" default:"false"`
WatchInterval int `yaml:"watch-interval" default:"5"`
Enable bool `yaml:"enable" default:"false"`
RepetitiveTrafficDetector bool `yaml:"repetitive-traffic-detector" default:"false"`
QnamePlusOne bool `yaml:"qname-plus-one" default:"false"`
WatchInterval int `yaml:"watch-interval" default:"2"`
UniqueFields []string `yaml:"unique-fields" default:"[\"dnstap.identity\", \"dnstap.operation\", \"network.query-ip\", \"network.response-ip\", \"dns.qname\", \"dns.qtype\"]"`
} `yaml:"reducer"`
Filtering struct {
Enable bool `yaml:"enable" default:"false"`
Expand Down
24 changes: 19 additions & 5 deletions transformers/reducer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package transformers

import (
"container/list"
"fmt"
"reflect"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -133,18 +135,30 @@ func (t *ReducerTransform) repetitiveTrafficDetector(dm *dnsutils.DNSMessage) (i
}

t.strBuilder.Reset()
t.strBuilder.WriteString(dm.DNSTap.Identity)
t.strBuilder.WriteString(dm.DNSTap.Operation)
t.strBuilder.WriteString(dm.NetworkInfo.QueryIP)

// update qname ?
if t.config.Reducer.QnamePlusOne {
qname := strings.ToLower(dm.DNS.Qname)
qname = strings.TrimSuffix(qname, ".")
if etld, err := publicsuffixlist.EffectiveTLDPlusOne(qname); err == nil {
dm.DNS.Qname = etld
}
}
t.strBuilder.WriteString(dm.DNS.Qname)
t.strBuilder.WriteString(dm.DNS.Qtype)

dmValue := reflect.ValueOf(dm).Elem() // Get the struct value of the DNSMessage
for _, field := range t.config.Reducer.UniqueFields {
if value, found := dnsutils.GetFieldByJSONTag(dmValue, field); found {
// Check if the field's kind is either int or string
switch value.Kind() {
case reflect.Int, reflect.String:
t.strBuilder.WriteString(fmt.Sprintf("%v", value.Interface())) // Append field value
default:
// Skip unsupported types
continue
}
}
}

dmTag := t.strBuilder.String()

dmCopy := *dm
Expand Down
35 changes: 29 additions & 6 deletions transformers/reducer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func TestReducer_RepetitiveTrafficDetector(t *testing.T) {
dnsMessagesIn []dnsutils.DNSMessage
}{
{
name: "norepeat",
name: "no_reduce",
dnsMessagesIn: []dnsutils.DNSMessage{
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY"},
Expand All @@ -104,27 +104,50 @@ func TestReducer_RepetitiveTrafficDetector(t *testing.T) {
},
},
{
name: "reduce",
name: "reduce_default_unique_fields",
dnsMessagesIn: []dnsutils.DNSMessage{
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY", Identity: "test"},
DNS: dnsutils.DNS{Qname: "hello.world", Qtype: "A"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1", ResponseIP: "8.8.8.8"},
},
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY", Identity: "test"},
DNS: dnsutils.DNS{Qname: "hello.world", Qtype: "A"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1", ResponseIP: "8.8.8.8"},
},
},
dnsMessagesOut: []dnsutils.DNSMessage{
{
Reducer: &dnsutils.TransformReducer{Occurrences: 2},
},
},
},
{
name: "no_reduce_responseip_different",
dnsMessagesIn: []dnsutils.DNSMessage{
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY"},
DNS: dnsutils.DNS{Qname: "hello.world", Qtype: "A"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1", ResponseIP: "1.1.1.1"},
},
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY"},
DNS: dnsutils.DNS{Qname: "hello.world", Qtype: "A"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1"},
NetworkInfo: dnsutils.DNSNetInfo{QueryIP: "127.0.0.1", ResponseIP: "8.8.8.8"},
},
},
dnsMessagesOut: []dnsutils.DNSMessage{
{
Reducer: &dnsutils.TransformReducer{Occurrences: 2},
Reducer: &dnsutils.TransformReducer{Occurrences: 1},
},
{
Reducer: &dnsutils.TransformReducer{Occurrences: 1},
},
},
},
{
name: "norepeat_qtype",
name: "no_reduce_qtype_different",
dnsMessagesIn: []dnsutils.DNSMessage{
{
DNSTap: dnsutils.DNSTap{Operation: "CLIENT_QUERY"},
Expand Down
Loading
0