Skip to content
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

feat(producer): refactor encapsulation and configuration #342

Merged
merged 64 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
9be9b25
feat(producer): refactor encap and packet decoding
lspgn Jul 21, 2024
20bf55a
prepare parsers
lspgn Jul 21, 2024
a9b28fc
initial parser
lspgn Jul 21, 2024
d396006
wip
lspgn Jul 21, 2024
373a783
tests, 802.1q
lspgn Jul 21, 2024
081cb45
ipv6
lspgn Jul 22, 2024
1a23c71
icmp
lspgn Jul 22, 2024
2fce326
various encap
lspgn Jul 22, 2024
cc664d8
gre
lspgn Jul 22, 2024
ffc9888
bugfix
lspgn Jul 22, 2024
13b3200
ipv6 fragment header
lspgn Jul 22, 2024
74e4eea
srv6
lspgn Jul 22, 2024
2e9c7a1
function args
lspgn Jul 22, 2024
f58f999
wip
lspgn Jul 22, 2024
5589349
improve stack mapping
lspgn Jul 23, 2024
c9efd85
wip
lspgn Jul 24, 2024
bddf3b3
wip
lspgn Jul 24, 2024
c96a149
new getbytes function
lspgn Jul 28, 2024
78a1a91
fixes
lspgn Jul 28, 2024
49f1ea9
skip delimiter
lspgn Jul 28, 2024
4e10e26
more tests
lspgn Jul 28, 2024
a5a1099
refactor config
lspgn Jul 31, 2024
2ec66e4
add bench
lspgn Jul 31, 2024
492b573
interface-ification
lspgn Jul 31, 2024
a9860bd
nit config
lspgn Aug 1, 2024
60b6bc9
nit
lspgn Aug 1, 2024
75d45b2
extract into legacy files
lspgn Aug 1, 2024
c0dcb96
make it safe and interface-y
lspgn Aug 1, 2024
edce6ba
legacy things
lspgn Aug 1, 2024
2c7e21b
wiup
lspgn Aug 1, 2024
329a676
less legacy
lspgn Aug 1, 2024
621d51f
more refactoring
lspgn Aug 1, 2024
020479a
finalize
lspgn Aug 1, 2024
4219dbe
wip
lspgn Aug 2, 2024
16c4a92
formatter
lspgn Aug 2, 2024
1693ae4
nits
lspgn Aug 2, 2024
325e343
srv6
lspgn Aug 2, 2024
4e2c51b
docs
lspgn Aug 3, 2024
5569085
map by etype and proto
lspgn Aug 4, 2024
09de264
add etype and proto dynamically for mapping
lspgn Aug 4, 2024
81f219f
layer iterator
lspgn Aug 4, 2024
aefe768
nit test
lspgn Aug 4, 2024
92daba6
fix checks, rendering, encapsulation
lspgn Aug 4, 2024
f129dd7
fix mapping
lspgn Aug 4, 2024
02f5d1b
mpls and dot1q have skipencap
lspgn Aug 4, 2024
f79ff61
comments
lspgn Aug 4, 2024
e342ce2
wip
lspgn Aug 4, 2024
3f6cf0e
more docs
lspgn Aug 4, 2024
741ab0e
fix panic
lspgn Aug 4, 2024
4520ac5
bugfix
lspgn Aug 4, 2024
9eaf510
rename ipv6eh, layer-size
lspgn Aug 5, 2024
6bfc106
add iana names, allows lowercase
lspgn Aug 11, 2024
344f6da
remove legacy files
lspgn Aug 11, 2024
63f7aa9
swap legacy naming
lspgn Aug 11, 2024
fae08b2
relocate files
lspgn Aug 11, 2024
097e0de
optimize
lspgn Aug 11, 2024
385d6fc
renames
lspgn Aug 11, 2024
c5b2737
prepare custom port mapping
lspgn Aug 12, 2024
4d6e0fd
add port parsing
lspgn Aug 12, 2024
08c553c
nits
lspgn Aug 12, 2024
6c33467
add geneve test
lspgn Aug 12, 2024
dc7fb5b
add parser environments
lspgn Aug 14, 2024
5fd5e44
config has environment
lspgn Aug 16, 2024
81a7c56
quick linting
lspgn Aug 18, 2024
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
9 changes: 7 additions & 2 deletions cmd/goflow2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ func main() {
}
}

flowProducer, err = protoproducer.CreateProtoProducer(cfgProducer, protoproducer.CreateSamplingSystem)
cfgm, err := cfgProducer.Compile() // converts configuration into a format that can be used by a protobuf producer
if err != nil {
log.Fatal(err)
}

flowProducer, err = protoproducer.CreateProtoProducer(cfgm, protoproducer.CreateSamplingSystem)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -308,7 +313,7 @@ func main() {
l.Info("closed receiver")
continue
} else if !errors.Is(err, netflow.ErrorTemplateNotFound) && !errors.Is(err, debug.PanicError) {
l.Error("error")
l.WithError(err).Error("error")
continue
}

Expand Down
13 changes: 13 additions & 0 deletions cmd/goflow2/mapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ netflowv9:
- field: 61
destination: flow_direction
sflow:
ports:
- proto: "udp"
dir: "dst"
port: 3544
parser: "teredo-dst"
- proto: "udp"
dir: "both"
port: 4754
parser: "gre"
- proto: "udp"
dir: "both"
port: 6081
parser: "geneve"
mapping:
- layer: "udp"
offset: 48
Expand Down
201 changes: 201 additions & 0 deletions docs/mapping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Mapping and Configuration

GoFlow2 allows users to collect and represent non-standard fields
without having to rely on `-produce=raw` setting.

By default, commonly used types are collected into the protobuf.
For instance source and destination IP addresses, TCP/UDP ports, etc.
When suggesting a new field to collect, preference should be given to fields
that are both widely adopted and supported by multiple protocols (sFlow, IPFIX).

Some scenarios require more flexibility.
In fact, IPFIX allows Private Enterprise Numbers ([PEN](https://www.iana.org/assignments/enterprise-numbers/))
and entire datagrams (IPFIX, sFlow) can contain bytes of interest.

A mapping configuration file empowers GoFlow2 users to collect
extra data without changing the code and recompiling.
The feature is available for both protobuf binary and JSON formatting.

A configuration file can be invoked the following way:

```bash
goflow2 -mapping=config.yaml -format=json -produce=sample
```

An example configuration file that collects NetFlow/IPFIX flow direction information:

```yaml
formatter:
fields: # list of fields to format in JSON
- flow_direction
protobuf: # manual protobuf fields addition
- name: flow_direction
index: 42
type: varint
# Decoder mappings
ipfix:
mapping:
- field: 61
destination: flow_direction
netflowv9:
mapping:
- field: 61
destination: flow_direction
```

In JSON, the field `flow_direction` will now be added.
In binary protobuf, when consumed by another tool,
the latter can access the new field at index 42.
A custom proto file can be compiled with the following:

```proto
message FlowMessage {

...
uint32 flow_direction = 42;

```

## Formatting and rendering

This section of the configuration is used for textual representations.
Both fields from [`flow.proto`](../pb/flow.proto) and custom ones inside `formatter.protobuf`
can be available in the textual output (JSON for instance).

The items inside `formatter.fields` are the fields present in the output.

The render section picks the representation.
For instance a 4/16 bytes field can be represented as an IP address, time can be represented as RFC3339 or epoch.

```yaml
formatter:
fields:
- time_received_ns
- my_new_field
- my_other_field
protobuf:
- name: my_new_field
index: 1000
type: varint
- name: my_other_field
index: 2000
type: string
render:
time_received_ns: datetimenano
my_other_field: ip
```

## Encapsulation

Custom mapping can be used with encapsulation.

By default, GoFlow2 will expect a packet with the following layers:

* Ethernet
* 802.1q and/or MPLS
* IP
* TCP or UDP

A more complex packet could be in the form:

* **Ethernet**
* **MPLS**
* **IP**
* *GRE*
* *Ethernet*
* *IP*
* *UDP*

Only the layers in **bold** will have the information collected.
The perimeter that is considered encapsulation here is the GRE protocol (note: it could be started if a second Ethernet layer was above 802.1q).
Rather than having duplicates of the existing fields with encapsulation, a configuration file can be used to collect
the encapsulated fields.

An additional consideration is that protobuf fields can be array (or `repeated`).
Due to the way the mapping works, the arrays are not [packed](https://protobuf.dev/programming-guides/encoding/#packed)
(equivalent to a `repeated myfield = 123 [packed=false]` in the definition).
Each item is encoded in the order they are parsed alongside other fields
whereas packed would require a second pass to combine all the items together.

### Inner UDP/TCP ports

```yaml
formatter:
fields:
- src_port_encap
- dst_port_encap
protobuf:
- name: src_port_encap
index: 1021
type: string
array: true
- name: dst_port_encap
index: 1022
type: string
array: true
sflow:
mapping:
- layer: "udp"
encap: true
offset: 0
length: 16
destination: src_port_encap
- layer: "udp"
encap: true
offset: 16
length: 16
destination: dst_port_encap
- layer: "tcp"
encap: true
offset: 0
length: 16
destination: src_port_encap
- layer: "tcp"
encap: true
offset: 16
length: 16
destination: dst_port_encap
```

### Inner IP addresses

```yaml
formatter:
fields:
- src_ip_encap
- dst_ip_encap
protobuf:
- name: src_ip_encap
index: 1006
type: string
array: true
- name: dst_ip_encap
index: 1007
type: string
array: true
render:
src_ip_encap: ip
dst_ip_encap: ip
sflow:
mapping:
- layer: "ipv6"
encap: true
offset: 64
length: 128
destination: src_ip_encap
- layer: "ipv6"
encap: true
offset: 192
length: 128
destination: dst_ip_encap
- layer: "ipv4"
encap: true
offset: 96
length: 32
destination: src_ip_encap
- layer: "ipv4"
encap: true
offset: 128
length: 32
destination: dst_ip_encap
```
2 changes: 1 addition & 1 deletion format/binary/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (d *BinaryDriver) Format(data interface{}) ([]byte, []byte, error) {
text, err := dataIf.MarshalBinary()
return key, text, err
}
return key, nil, format.ErrorNoSerializer
return key, nil, format.ErrNoSerializer
}

func init() {
Expand Down
8 changes: 4 additions & 4 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ var (
formatDrivers = make(map[string]FormatDriver)
lock = &sync.RWMutex{}

ErrorFormat = fmt.Errorf("format error")
ErrorNoSerializer = fmt.Errorf("message is not serializable")
ErrFormat = fmt.Errorf("format error")
ErrNoSerializer = fmt.Errorf("message is not serializable")
)

type DriverFormatError struct {
Expand All @@ -23,7 +23,7 @@ func (e *DriverFormatError) Error() string {
}

func (e *DriverFormatError) Unwrap() []error {
return []error{ErrorFormat, e.Err}
return []error{ErrFormat, e.Err}
}

type FormatDriver interface {
Expand Down Expand Up @@ -67,7 +67,7 @@ func FindFormat(name string) (*Format, error) {
t, ok := formatDrivers[name]
lock.RUnlock()
if !ok {
return nil, fmt.Errorf("%w %s not found", ErrorFormat, name)
return nil, fmt.Errorf("%w %s not found", ErrFormat, name)
}

err := t.Init()
Expand Down
2 changes: 1 addition & 1 deletion format/text/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (d *TextDriver) Format(data interface{}) ([]byte, []byte, error) {
if dataIf, ok := data.(interface{ String() string }); ok {
return key, []byte(dataIf.String()), nil
}
return key, nil, format.ErrorNoSerializer
return key, nil, format.ErrNoSerializer
}

func init() {
Expand Down
Loading
Loading