
nfacct is netfilter's accounting subsystem. This utility allows interactions with the subsystem using lower level netlink API. Signed-off-by: Daman Arora <aroradaman@gmail.com>
629 lines
19 KiB
Go
629 lines
19 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
/*
|
|
Copyright 2024 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package nfacct
|
|
|
|
import (
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/vishvananda/netlink/nl"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// fakeHandler is a mock implementation of the handler interface, designed for testing.
|
|
type fakeHandler struct {
|
|
// requests stores instances of fakeRequest, capturing new requests.
|
|
requests []*fakeRequest
|
|
// responses holds responses for the subsequent fakeRequest.Execute calls.
|
|
responses [][][]byte
|
|
// errs holds errors for the subsequent fakeRequest.Execute calls.
|
|
errs []error
|
|
}
|
|
|
|
// newRequest creates a request object with the given cmd, flags, predefined response and error.
|
|
// It additionally records the created request object.
|
|
func (fh *fakeHandler) newRequest(cmd int, flags uint16) request {
|
|
var response [][]byte
|
|
if fh.responses != nil && len(fh.responses) > 0 {
|
|
response = fh.responses[0]
|
|
// remove the response from the list of predefined responses and add it to request object for mocking.
|
|
fh.responses = fh.responses[1:]
|
|
}
|
|
|
|
var err error
|
|
if fh.errs != nil && len(fh.errs) > 0 {
|
|
err = fh.errs[0]
|
|
// remove the error from the list of predefined errors and add it to request object for mocking.
|
|
fh.errs = fh.errs[1:]
|
|
}
|
|
|
|
req := &fakeRequest{cmd: cmd, flags: flags, response: response, err: err}
|
|
fh.requests = append(fh.requests, req)
|
|
return req
|
|
}
|
|
|
|
// fakeRequest records information about the cmd and flags used when creating a new request,
|
|
// maintains a list for netlink attributes, and stores a predefined response and an optional
|
|
// error for subsequent execution.
|
|
type fakeRequest struct {
|
|
// cmd and flags which were used to create the request.
|
|
cmd int
|
|
flags uint16
|
|
|
|
// data holds netlink attributes.
|
|
data []nl.NetlinkRequestData
|
|
|
|
// response and err are the predefined output of execution.
|
|
response [][]byte
|
|
err error
|
|
}
|
|
|
|
// Serialize is part of request interface.
|
|
func (fr *fakeRequest) Serialize() []byte { return nil }
|
|
|
|
// AddData is part of request interface.
|
|
func (fr *fakeRequest) AddData(data nl.NetlinkRequestData) {
|
|
fr.data = append(fr.data, data)
|
|
}
|
|
|
|
// AddRawData is part of request interface.
|
|
func (fr *fakeRequest) AddRawData(_ []byte) {}
|
|
|
|
// Execute is part of request interface.
|
|
func (fr *fakeRequest) Execute(_ int, _ uint16) ([][]byte, error) {
|
|
return fr.response, fr.err
|
|
}
|
|
|
|
func TestRunner_Add(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
counterName string
|
|
handler *fakeHandler
|
|
err error
|
|
netlinkCalls int
|
|
}{
|
|
{
|
|
name: "valid",
|
|
counterName: "metric-1",
|
|
handler: &fakeHandler{},
|
|
// expected calls: NFNL_MSG_ACCT_NEW
|
|
netlinkCalls: 1,
|
|
},
|
|
{
|
|
name: "add duplicate counter",
|
|
counterName: "metric-2",
|
|
handler: &fakeHandler{
|
|
errs: []error{syscall.EBUSY},
|
|
},
|
|
err: ErrObjectAlreadyExists,
|
|
// expected calls: NFNL_MSG_ACCT_NEW
|
|
netlinkCalls: 1,
|
|
},
|
|
{
|
|
name: "insufficient privilege",
|
|
counterName: "metric-2",
|
|
handler: &fakeHandler{
|
|
errs: []error{syscall.EPERM},
|
|
},
|
|
err: ErrUnexpected,
|
|
// expected calls: NFNL_MSG_ACCT_NEW
|
|
netlinkCalls: 1,
|
|
},
|
|
{
|
|
name: "exceeds max length",
|
|
counterName: "this-is-a-string-with-more-than-32-characters",
|
|
handler: &fakeHandler{},
|
|
err: ErrNameExceedsMaxLength,
|
|
// expected calls: zero (the error should be returned by this library)
|
|
netlinkCalls: 0,
|
|
},
|
|
{
|
|
name: "falls below min length",
|
|
counterName: "",
|
|
handler: &fakeHandler{},
|
|
err: ErrEmptyName,
|
|
// expected calls: zero (the error should be returned by this library)
|
|
netlinkCalls: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rnr, err := newInternal(tc.handler)
|
|
assert.NoError(t, err)
|
|
|
|
err = rnr.Add(tc.counterName)
|
|
if tc.err != nil {
|
|
assert.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// validate number of requests
|
|
assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
|
|
|
|
if tc.netlinkCalls > 0 {
|
|
// validate request
|
|
assert.Equal(t, cmdNew, tc.handler.requests[0].cmd)
|
|
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_CREATE|unix.NLM_F_ACK), tc.handler.requests[0].flags)
|
|
|
|
// validate attribute(NFACCT_NAME)
|
|
assert.Equal(t, 1, len(tc.handler.requests[0].data))
|
|
assert.Equal(t,
|
|
tc.handler.requests[0].data[0].Serialize(),
|
|
nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize(),
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestRunner_Get(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
counterName string
|
|
counter *Counter
|
|
handler *fakeHandler
|
|
netlinkCalls int
|
|
err error
|
|
}{
|
|
{
|
|
name: "valid with padding",
|
|
counterName: "metric-1",
|
|
counter: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
|
|
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
},
|
|
{
|
|
name: "valid without padding",
|
|
counterName: "metrics",
|
|
counter: &Counter{Name: "metrics", Packets: 12, Bytes: 503},
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x00,
|
|
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
},
|
|
{
|
|
name: "missing netfilter generic header",
|
|
counterName: "metrics",
|
|
counter: nil,
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x01, 0x00, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
|
|
0x73, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00,
|
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x01, 0xf7, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "incorrect padding",
|
|
counterName: "metric-1",
|
|
counter: nil,
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
|
0x0a, 0x0f, 0xca, 0xf6, 0x63, 0x0c, 0x00, 0x03,
|
|
0x00, 0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd,
|
|
0xd1, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
|
0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "missing bytes attribute",
|
|
counterName: "metric-1",
|
|
counter: nil,
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "missing packets attribute",
|
|
counterName: "metric-1",
|
|
counter: nil,
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd, 0xd1,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "only name attribute",
|
|
counterName: "metric-1",
|
|
counter: nil,
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
}}},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "get non-existent counter",
|
|
counterName: "metric-2",
|
|
handler: &fakeHandler{
|
|
errs: []error{syscall.ENOENT},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrObjectNotFound,
|
|
},
|
|
{
|
|
name: "unexpected error",
|
|
counterName: "metric-2",
|
|
handler: &fakeHandler{
|
|
errs: []error{syscall.EMFILE},
|
|
},
|
|
// expected calls: NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
err: ErrUnexpected,
|
|
},
|
|
{
|
|
name: "exceeds max length",
|
|
counterName: "this-is-a-string-with-more-than-32-characters",
|
|
handler: &fakeHandler{},
|
|
// expected calls: zero (the error should be returned by this library)
|
|
netlinkCalls: 0,
|
|
err: ErrNameExceedsMaxLength,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rnr, err := newInternal(tc.handler)
|
|
assert.NoError(t, err)
|
|
|
|
counter, err := rnr.Get(tc.counterName)
|
|
|
|
// validate number of requests
|
|
assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
|
|
if tc.netlinkCalls > 0 {
|
|
// validate request
|
|
assert.Equal(t, cmdGet, tc.handler.requests[0].cmd)
|
|
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_ACK), tc.handler.requests[0].flags)
|
|
|
|
// validate attribute(NFACCT_NAME)
|
|
assert.Equal(t, 1, len(tc.handler.requests[0].data))
|
|
assert.Equal(t,
|
|
tc.handler.requests[0].data[0].Serialize(),
|
|
nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize())
|
|
|
|
// validate response
|
|
if tc.err != nil {
|
|
assert.Nil(t, counter)
|
|
assert.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
assert.NotNil(t, counter)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.counter.Name, counter.Name)
|
|
assert.Equal(t, tc.counter.Packets, counter.Packets)
|
|
assert.Equal(t, tc.counter.Bytes, counter.Bytes)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRunner_Ensure(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
counterName string
|
|
netlinkCalls int
|
|
handler *fakeHandler
|
|
}{
|
|
{
|
|
name: "counter doesnt exist",
|
|
counterName: "ct_established_accepted_packets",
|
|
handler: &fakeHandler{
|
|
errs: []error{syscall.ENOENT},
|
|
},
|
|
// expected calls - NFNL_MSG_ACCT_GET + NFNL_MSG_ACCT_NEW
|
|
netlinkCalls: 2,
|
|
},
|
|
{
|
|
name: "counter already exists",
|
|
counterName: "ct_invalid_dropped_packets",
|
|
handler: &fakeHandler{
|
|
responses: [][][]byte{{{
|
|
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
|
|
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
|
|
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
|
|
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
|
|
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x02, 0x68, 0xf3, 0x16, 0x58, 0x0e, 0x63,
|
|
0x0c, 0x00, 0x03, 0x00, 0x12, 0xc5, 0x37, 0xdf,
|
|
0xe5, 0xa1, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
}}},
|
|
},
|
|
// expected calls - NFNL_MSG_ACCT_GET
|
|
netlinkCalls: 1,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rnr, err := newInternal(tc.handler)
|
|
assert.NoError(t, err)
|
|
|
|
err = rnr.Ensure(tc.counterName)
|
|
assert.NoError(t, err)
|
|
|
|
// validate number of netlink requests
|
|
assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestRunner_List(t *testing.T) {
|
|
hndlr := &fakeHandler{
|
|
responses: [][][]byte{{
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
|
|
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
|
|
0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
|
|
0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x08, 0x60, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00,
|
|
0x6e, 0x66, 0x61, 0x63, 0x63, 0x74, 0x2d, 0x6c,
|
|
0x69, 0x73, 0x74, 0x2d, 0x74, 0x65, 0x73, 0x74,
|
|
0x2d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x00,
|
|
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x02, 0x0b, 0x96, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xe6, 0xc5, 0x74,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
|
|
0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
|
|
0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x86, 0x8d,
|
|
0x44, 0xeb, 0xc7, 0x02, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x6e, 0x5f, 0xe2, 0x89, 0x69, 0x3f, 0x9e,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
|
|
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
|
|
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
|
|
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
|
|
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x01, 0x1e, 0x6e, 0xac, 0x20, 0xe9,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x6d,
|
|
0x30, 0x11, 0x8a, 0xec, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
}},
|
|
}
|
|
|
|
expected := []*Counter{
|
|
{Name: "random-test-metric", Packets: 134, Bytes: 2144},
|
|
{Name: "nfacct-list-test-metric", Packets: 134038, Bytes: 31901044},
|
|
{Name: "test", Packets: 147941304813314, Bytes: 31067674010795934},
|
|
{Name: "ct_invalid_dropped_packets", Packets: 1230217421033, Bytes: 14762609052396},
|
|
}
|
|
|
|
rnr, err := newInternal(hndlr)
|
|
assert.NoError(t, err)
|
|
|
|
counters, err := rnr.List()
|
|
|
|
// validate request(NFNL_MSG_ACCT_GET)
|
|
assert.Equal(t, 1, len(hndlr.requests))
|
|
assert.Equal(t, cmdGet, hndlr.requests[0].cmd)
|
|
assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_DUMP), hndlr.requests[0].flags)
|
|
|
|
// validate attributes
|
|
assert.Equal(t, 0, len(hndlr.requests[0].data))
|
|
|
|
// validate response
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, counters)
|
|
assert.Equal(t, len(expected), len(counters))
|
|
for i := 0; i < len(expected); i++ {
|
|
assert.Equal(t, expected[i].Name, counters[i].Name)
|
|
assert.Equal(t, expected[i].Packets, counters[i].Packets)
|
|
assert.Equal(t, expected[i].Bytes, counters[i].Bytes)
|
|
}
|
|
}
|
|
|
|
func TestDecode(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
msg []byte
|
|
expected *Counter
|
|
}{
|
|
{
|
|
name: "valid",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
|
|
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
|
|
},
|
|
{
|
|
name: "attribute name missing",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x01, 0xe6, 0xc5, 0x74, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
expected: &Counter{Packets: 134038, Bytes: 31901044},
|
|
},
|
|
{
|
|
name: "attribute packets missing",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
|
|
0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
|
|
0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
|
|
0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
|
|
0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60,
|
|
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
expected: &Counter{Name: "ct_invalid_dropped_packets", Bytes: 2144},
|
|
},
|
|
{
|
|
name: "attribute bytes missing",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
|
|
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
|
|
0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
|
|
0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
|
|
},
|
|
expected: &Counter{Name: "random-test-metric", Packets: 503},
|
|
},
|
|
{
|
|
name: "attribute packets and bytes missing",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
|
|
0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
|
|
},
|
|
expected: &Counter{Name: "test"},
|
|
},
|
|
{
|
|
name: "only netfilter generic header present",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00,
|
|
},
|
|
expected: &Counter{},
|
|
},
|
|
{
|
|
name: "only packets attribute",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
|
|
},
|
|
expected: &Counter{Packets: 134038},
|
|
},
|
|
{
|
|
name: "only bytes attribute",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
|
|
},
|
|
expected: &Counter{Bytes: 12},
|
|
},
|
|
{
|
|
name: "new attribute in the beginning",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
|
|
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
|
|
},
|
|
{
|
|
name: "new attribute in the end",
|
|
msg: []byte{
|
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
|
|
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
|
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
|
|
0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
|
|
0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01,
|
|
0x02, 0x03, 0x0e, 0x3f, 0xf6, 0xda, 0xcd, 0xd1,
|
|
},
|
|
expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
counter, err := decode(tc.msg, false)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, counter)
|
|
|
|
assert.Equal(t, tc.expected.Name, counter.Name)
|
|
assert.Equal(t, tc.expected.Packets, counter.Packets)
|
|
assert.Equal(t, tc.expected.Bytes, counter.Bytes)
|
|
})
|
|
}
|
|
}
|