k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/proxy/util/nfacct/nfacct_linux_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2024 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package nfacct
    21  
    22  import (
    23  	"syscall"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/vishvananda/netlink/nl"
    28  	"golang.org/x/sys/unix"
    29  )
    30  
    31  // fakeHandler is a mock implementation of the handler interface, designed for testing.
    32  type fakeHandler struct {
    33  	// requests stores instances of fakeRequest, capturing new requests.
    34  	requests []*fakeRequest
    35  	// responses holds responses for the subsequent fakeRequest.Execute calls.
    36  	responses [][][]byte
    37  	// errs holds errors for the subsequent fakeRequest.Execute calls.
    38  	errs []error
    39  }
    40  
    41  // newRequest creates a request object with the given cmd, flags, predefined response and error.
    42  // It additionally records the created request object.
    43  func (fh *fakeHandler) newRequest(cmd int, flags uint16) request {
    44  	var response [][]byte
    45  	if fh.responses != nil && len(fh.responses) > 0 {
    46  		response = fh.responses[0]
    47  		// remove the response from the list of predefined responses and add it to request object for mocking.
    48  		fh.responses = fh.responses[1:]
    49  	}
    50  
    51  	var err error
    52  	if fh.errs != nil && len(fh.errs) > 0 {
    53  		err = fh.errs[0]
    54  		// remove the error from the list of predefined errors and add it to request object for mocking.
    55  		fh.errs = fh.errs[1:]
    56  	}
    57  
    58  	req := &fakeRequest{cmd: cmd, flags: flags, response: response, err: err}
    59  	fh.requests = append(fh.requests, req)
    60  	return req
    61  }
    62  
    63  // fakeRequest records information about the cmd and flags used when creating a new request,
    64  // maintains a list for netlink attributes, and stores a predefined response and an optional
    65  // error for subsequent execution.
    66  type fakeRequest struct {
    67  	// cmd and flags which were used to create the request.
    68  	cmd   int
    69  	flags uint16
    70  
    71  	// data holds netlink attributes.
    72  	data []nl.NetlinkRequestData
    73  
    74  	// response and err are the predefined output of execution.
    75  	response [][]byte
    76  	err      error
    77  }
    78  
    79  // Serialize is part of request interface.
    80  func (fr *fakeRequest) Serialize() []byte { return nil }
    81  
    82  // AddData is part of request interface.
    83  func (fr *fakeRequest) AddData(data nl.NetlinkRequestData) {
    84  	fr.data = append(fr.data, data)
    85  }
    86  
    87  // AddRawData is part of request interface.
    88  func (fr *fakeRequest) AddRawData(_ []byte) {}
    89  
    90  // Execute is part of request interface.
    91  func (fr *fakeRequest) Execute(_ int, _ uint16) ([][]byte, error) {
    92  	return fr.response, fr.err
    93  }
    94  
    95  func TestRunner_Add(t *testing.T) {
    96  	testCases := []struct {
    97  		name         string
    98  		counterName  string
    99  		handler      *fakeHandler
   100  		err          error
   101  		netlinkCalls int
   102  	}{
   103  		{
   104  			name:        "valid",
   105  			counterName: "metric-1",
   106  			handler:     &fakeHandler{},
   107  			// expected calls: NFNL_MSG_ACCT_NEW
   108  			netlinkCalls: 1,
   109  		},
   110  		{
   111  			name:        "add duplicate counter",
   112  			counterName: "metric-2",
   113  			handler: &fakeHandler{
   114  				errs: []error{syscall.EBUSY},
   115  			},
   116  			err: ErrObjectAlreadyExists,
   117  			// expected calls: NFNL_MSG_ACCT_NEW
   118  			netlinkCalls: 1,
   119  		},
   120  		{
   121  			name:        "insufficient privilege",
   122  			counterName: "metric-2",
   123  			handler: &fakeHandler{
   124  				errs: []error{syscall.EPERM},
   125  			},
   126  			err: ErrUnexpected,
   127  			// expected calls: NFNL_MSG_ACCT_NEW
   128  			netlinkCalls: 1,
   129  		},
   130  		{
   131  			name:        "exceeds max length",
   132  			counterName: "this-is-a-string-with-more-than-32-characters",
   133  			handler:     &fakeHandler{},
   134  			err:         ErrNameExceedsMaxLength,
   135  			// expected calls: zero (the error should be returned by this library)
   136  			netlinkCalls: 0,
   137  		},
   138  		{
   139  			name:        "falls below min length",
   140  			counterName: "",
   141  			handler:     &fakeHandler{},
   142  			err:         ErrEmptyName,
   143  			// expected calls: zero (the error should be returned by this library)
   144  			netlinkCalls: 0,
   145  		},
   146  	}
   147  
   148  	for _, tc := range testCases {
   149  		t.Run(tc.name, func(t *testing.T) {
   150  			rnr, err := newInternal(tc.handler)
   151  			assert.NoError(t, err)
   152  
   153  			err = rnr.Add(tc.counterName)
   154  			if tc.err != nil {
   155  				assert.ErrorContains(t, err, tc.err.Error())
   156  			} else {
   157  				assert.NoError(t, err)
   158  			}
   159  
   160  			// validate number of requests
   161  			assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
   162  
   163  			if tc.netlinkCalls > 0 {
   164  				// validate request
   165  				assert.Equal(t, cmdNew, tc.handler.requests[0].cmd)
   166  				assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_CREATE|unix.NLM_F_ACK), tc.handler.requests[0].flags)
   167  
   168  				// validate attribute(NFACCT_NAME)
   169  				assert.Equal(t, 1, len(tc.handler.requests[0].data))
   170  				assert.Equal(t,
   171  					tc.handler.requests[0].data[0].Serialize(),
   172  					nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize(),
   173  				)
   174  			}
   175  		})
   176  	}
   177  }
   178  func TestRunner_Get(t *testing.T) {
   179  	testCases := []struct {
   180  		name         string
   181  		counterName  string
   182  		counter      *Counter
   183  		handler      *fakeHandler
   184  		netlinkCalls int
   185  		err          error
   186  	}{
   187  		{
   188  			name:        "valid with padding",
   189  			counterName: "metric-1",
   190  			counter:     &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
   191  			handler: &fakeHandler{
   192  				responses: [][][]byte{{{
   193  					0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   194  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   195  					0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   196  					0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
   197  					0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
   198  					0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
   199  					0x00, 0x00, 0x00, 0x01,
   200  				}}},
   201  			},
   202  			// expected calls: NFNL_MSG_ACCT_GET
   203  			netlinkCalls: 1,
   204  		},
   205  		{
   206  			name:        "valid without padding",
   207  			counterName: "metrics",
   208  			counter:     &Counter{Name: "metrics", Packets: 12, Bytes: 503},
   209  			handler: &fakeHandler{
   210  				responses: [][][]byte{{{
   211  					0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
   212  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x00,
   213  					0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
   214  					0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x03, 0x00,
   215  					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
   216  					0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   217  				}}},
   218  			},
   219  			// expected calls: NFNL_MSG_ACCT_GET
   220  			netlinkCalls: 1,
   221  		},
   222  		{
   223  			name:        "missing netfilter generic header",
   224  			counterName: "metrics",
   225  			counter:     nil,
   226  			handler: &fakeHandler{
   227  				responses: [][][]byte{{{
   228  					0x01, 0x00, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
   229  					0x73, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00,
   230  					0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00,
   231  					0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   232  					0x01, 0xf7, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
   233  					0x00, 0x01,
   234  				}}},
   235  			},
   236  			// expected calls: NFNL_MSG_ACCT_GET
   237  			netlinkCalls: 1,
   238  			err:          ErrUnexpected,
   239  		},
   240  		{
   241  			name:        "incorrect padding",
   242  			counterName: "metric-1",
   243  			counter:     nil,
   244  			handler: &fakeHandler{
   245  				responses: [][][]byte{{{
   246  					0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   247  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   248  					0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
   249  					0x0a, 0x0f, 0xca, 0xf6, 0x63, 0x0c, 0x00, 0x03,
   250  					0x00, 0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd,
   251  					0xd1, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
   252  					0x01,
   253  				}}},
   254  			},
   255  			// expected calls: NFNL_MSG_ACCT_GET
   256  			netlinkCalls: 1,
   257  			err:          ErrUnexpected,
   258  		},
   259  		{
   260  			name:        "missing bytes attribute",
   261  			counterName: "metric-1",
   262  			counter:     nil,
   263  			handler: &fakeHandler{
   264  				responses: [][][]byte{{{
   265  					0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   266  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   267  					0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   268  					0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
   269  					0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   270  				}}},
   271  			},
   272  			// expected calls: NFNL_MSG_ACCT_GET
   273  			netlinkCalls: 1,
   274  			err:          ErrUnexpected,
   275  		},
   276  		{
   277  			name:        "missing packets attribute",
   278  			counterName: "metric-1",
   279  			counter:     nil,
   280  			handler: &fakeHandler{
   281  				responses: [][][]byte{{{
   282  					0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   283  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   284  					0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
   285  					0x00, 0x09, 0x0e, 0x06, 0xf6, 0xda, 0xcd, 0xd1,
   286  					0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   287  				}}},
   288  			},
   289  			// expected calls: NFNL_MSG_ACCT_GET
   290  			netlinkCalls: 1,
   291  			err:          ErrUnexpected,
   292  		},
   293  		{
   294  			name:        "only name attribute",
   295  			counterName: "metric-1",
   296  			counter:     nil,
   297  			handler: &fakeHandler{
   298  				responses: [][][]byte{{{
   299  					0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   300  					0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   301  					0x00, 0x00, 0x00, 0x00,
   302  				}}},
   303  			},
   304  			// expected calls: NFNL_MSG_ACCT_GET
   305  			netlinkCalls: 1,
   306  			err:          ErrUnexpected,
   307  		},
   308  		{
   309  			name:        "get non-existent counter",
   310  			counterName: "metric-2",
   311  			handler: &fakeHandler{
   312  				errs: []error{syscall.ENOENT},
   313  			},
   314  			// expected calls: NFNL_MSG_ACCT_GET
   315  			netlinkCalls: 1,
   316  			err:          ErrObjectNotFound,
   317  		},
   318  		{
   319  			name:        "unexpected error",
   320  			counterName: "metric-2",
   321  			handler: &fakeHandler{
   322  				errs: []error{syscall.EMFILE},
   323  			},
   324  			// expected calls: NFNL_MSG_ACCT_GET
   325  			netlinkCalls: 1,
   326  			err:          ErrUnexpected,
   327  		},
   328  		{
   329  			name:        "exceeds max length",
   330  			counterName: "this-is-a-string-with-more-than-32-characters",
   331  			handler:     &fakeHandler{},
   332  			// expected calls: zero (the error should be returned by this library)
   333  			netlinkCalls: 0,
   334  			err:          ErrNameExceedsMaxLength,
   335  		},
   336  	}
   337  
   338  	for _, tc := range testCases {
   339  		t.Run(tc.name, func(t *testing.T) {
   340  			rnr, err := newInternal(tc.handler)
   341  			assert.NoError(t, err)
   342  
   343  			counter, err := rnr.Get(tc.counterName)
   344  
   345  			// validate number of requests
   346  			assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
   347  			if tc.netlinkCalls > 0 {
   348  				// validate request
   349  				assert.Equal(t, cmdGet, tc.handler.requests[0].cmd)
   350  				assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_ACK), tc.handler.requests[0].flags)
   351  
   352  				// validate attribute(NFACCT_NAME)
   353  				assert.Equal(t, 1, len(tc.handler.requests[0].data))
   354  				assert.Equal(t,
   355  					tc.handler.requests[0].data[0].Serialize(),
   356  					nl.NewRtAttr(attrName, nl.ZeroTerminated(tc.counterName)).Serialize())
   357  
   358  				// validate response
   359  				if tc.err != nil {
   360  					assert.Nil(t, counter)
   361  					assert.ErrorContains(t, err, tc.err.Error())
   362  				} else {
   363  					assert.NotNil(t, counter)
   364  					assert.NoError(t, err)
   365  					assert.Equal(t, tc.counter.Name, counter.Name)
   366  					assert.Equal(t, tc.counter.Packets, counter.Packets)
   367  					assert.Equal(t, tc.counter.Bytes, counter.Bytes)
   368  				}
   369  			}
   370  		})
   371  	}
   372  }
   373  
   374  func TestRunner_Ensure(t *testing.T) {
   375  	testCases := []struct {
   376  		name         string
   377  		counterName  string
   378  		netlinkCalls int
   379  		handler      *fakeHandler
   380  	}{
   381  		{
   382  			name:        "counter doesnt exist",
   383  			counterName: "ct_established_accepted_packets",
   384  			handler: &fakeHandler{
   385  				errs: []error{syscall.ENOENT},
   386  			},
   387  			// expected calls - NFNL_MSG_ACCT_GET + NFNL_MSG_ACCT_NEW
   388  			netlinkCalls: 2,
   389  		},
   390  		{
   391  			name:        "counter already exists",
   392  			counterName: "ct_invalid_dropped_packets",
   393  			handler: &fakeHandler{
   394  				responses: [][][]byte{{{
   395  					0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
   396  					0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
   397  					0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
   398  					0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
   399  					0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   400  					0x00, 0x02, 0x68, 0xf3, 0x16, 0x58, 0x0e, 0x63,
   401  					0x0c, 0x00, 0x03, 0x00, 0x12, 0xc5, 0x37, 0xdf,
   402  					0xe5, 0xa1, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
   403  					0x00, 0x00, 0x00, 0x01,
   404  				}}},
   405  			},
   406  			// expected calls - NFNL_MSG_ACCT_GET
   407  			netlinkCalls: 1,
   408  		},
   409  	}
   410  
   411  	for _, tc := range testCases {
   412  		t.Run(tc.name, func(t *testing.T) {
   413  			rnr, err := newInternal(tc.handler)
   414  			assert.NoError(t, err)
   415  
   416  			err = rnr.Ensure(tc.counterName)
   417  			assert.NoError(t, err)
   418  
   419  			// validate number of netlink requests
   420  			assert.Equal(t, tc.netlinkCalls, len(tc.handler.requests))
   421  		})
   422  	}
   423  
   424  }
   425  
   426  func TestRunner_List(t *testing.T) {
   427  	hndlr := &fakeHandler{
   428  		responses: [][][]byte{{
   429  			{
   430  				0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
   431  				0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
   432  				0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
   433  				0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   434  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,
   435  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
   436  				0x00, 0x00, 0x08, 0x60, 0x08, 0x00, 0x04, 0x00,
   437  				0x00, 0x00, 0x00, 0x01,
   438  			},
   439  			{
   440  				0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00,
   441  				0x6e, 0x66, 0x61, 0x63, 0x63, 0x74, 0x2d, 0x6c,
   442  				0x69, 0x73, 0x74, 0x2d, 0x74, 0x65, 0x73, 0x74,
   443  				0x2d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x00,
   444  				0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
   445  				0x00, 0x02, 0x0b, 0x96, 0x0c, 0x00, 0x03, 0x00,
   446  				0x00, 0x00, 0x00, 0x00, 0x01, 0xe6, 0xc5, 0x74,
   447  				0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   448  			},
   449  			{
   450  				0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
   451  				0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
   452  				0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x86, 0x8d,
   453  				0x44, 0xeb, 0xc7, 0x02, 0x0c, 0x00, 0x03, 0x00,
   454  				0x00, 0x6e, 0x5f, 0xe2, 0x89, 0x69, 0x3f, 0x9e,
   455  				0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   456  			},
   457  			{
   458  				0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
   459  				0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
   460  				0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
   461  				0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
   462  				0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   463  				0x00, 0x00, 0x01, 0x1e, 0x6e, 0xac, 0x20, 0xe9,
   464  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x6d,
   465  				0x30, 0x11, 0x8a, 0xec, 0x08, 0x00, 0x04, 0x00,
   466  				0x00, 0x00, 0x00, 0x01,
   467  			},
   468  		}},
   469  	}
   470  
   471  	expected := []*Counter{
   472  		{Name: "random-test-metric", Packets: 134, Bytes: 2144},
   473  		{Name: "nfacct-list-test-metric", Packets: 134038, Bytes: 31901044},
   474  		{Name: "test", Packets: 147941304813314, Bytes: 31067674010795934},
   475  		{Name: "ct_invalid_dropped_packets", Packets: 1230217421033, Bytes: 14762609052396},
   476  	}
   477  
   478  	rnr, err := newInternal(hndlr)
   479  	assert.NoError(t, err)
   480  
   481  	counters, err := rnr.List()
   482  
   483  	// validate request(NFNL_MSG_ACCT_GET)
   484  	assert.Equal(t, 1, len(hndlr.requests))
   485  	assert.Equal(t, cmdGet, hndlr.requests[0].cmd)
   486  	assert.Equal(t, uint16(unix.NLM_F_REQUEST|unix.NLM_F_DUMP), hndlr.requests[0].flags)
   487  
   488  	// validate attributes
   489  	assert.Equal(t, 0, len(hndlr.requests[0].data))
   490  
   491  	// validate response
   492  	assert.NoError(t, err)
   493  	assert.NotNil(t, counters)
   494  	assert.Equal(t, len(expected), len(counters))
   495  	for i := 0; i < len(expected); i++ {
   496  		assert.Equal(t, expected[i].Name, counters[i].Name)
   497  		assert.Equal(t, expected[i].Packets, counters[i].Packets)
   498  		assert.Equal(t, expected[i].Bytes, counters[i].Bytes)
   499  	}
   500  }
   501  
   502  func TestDecode(t *testing.T) {
   503  	testCases := []struct {
   504  		name     string
   505  		msg      []byte
   506  		expected *Counter
   507  	}{
   508  		{
   509  			name: "valid",
   510  			msg: []byte{
   511  				0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00,
   512  				0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   513  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   514  				0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
   515  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
   516  				0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
   517  				0x00, 0x00, 0x00, 0x01,
   518  			},
   519  			expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
   520  		},
   521  		{
   522  			name: "attribute name missing",
   523  			msg: []byte{
   524  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   525  				0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
   526  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
   527  				0x01, 0xe6, 0xc5, 0x74, 0x08, 0x00, 0x04, 0x00,
   528  				0x00, 0x00, 0x00, 0x01,
   529  			},
   530  			expected: &Counter{Packets: 134038, Bytes: 31901044},
   531  		},
   532  		{
   533  			name: "attribute packets missing",
   534  			msg: []byte{
   535  				0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
   536  				0x63, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c,
   537  				0x69, 0x64, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70,
   538  				0x65, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
   539  				0x74, 0x73, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
   540  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60,
   541  				0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01,
   542  			},
   543  			expected: &Counter{Name: "ct_invalid_dropped_packets", Bytes: 2144},
   544  		},
   545  		{
   546  			name: "attribute bytes missing",
   547  			msg: []byte{
   548  				0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
   549  				0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2d, 0x74,
   550  				0x65, 0x73, 0x74, 0x2d, 0x6d, 0x65, 0x74, 0x72,
   551  				0x69, 0x63, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   552  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf7,
   553  			},
   554  			expected: &Counter{Name: "random-test-metric", Packets: 503},
   555  		},
   556  		{
   557  			name: "attribute packets and bytes missing",
   558  			msg: []byte{
   559  				0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00,
   560  				0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00,
   561  			},
   562  			expected: &Counter{Name: "test"},
   563  		},
   564  		{
   565  			name: "only netfilter generic header present",
   566  			msg: []byte{
   567  				0x00, 0x00, 0x00, 0x00,
   568  			},
   569  			expected: &Counter{},
   570  		},
   571  		{
   572  			name: "only packets attribute",
   573  			msg: []byte{
   574  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   575  				0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x96,
   576  			},
   577  			expected: &Counter{Packets: 134038},
   578  		},
   579  		{
   580  			name: "only bytes attribute",
   581  			msg: []byte{
   582  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
   583  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
   584  			},
   585  			expected: &Counter{Bytes: 12},
   586  		},
   587  		{
   588  			name: "new attribute in the beginning",
   589  			msg: []byte{
   590  				0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
   591  				0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
   592  				0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   593  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   594  				0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
   595  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
   596  				0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
   597  				0x00, 0x00, 0x00, 0x01,
   598  			},
   599  			expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
   600  		},
   601  		{
   602  			name: "new attribute in the end",
   603  			msg: []byte{
   604  				0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
   605  				0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x01, 0x00,
   606  				0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2d, 0x31,
   607  				0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00,
   608  				0x00, 0x00, 0x00, 0x0a, 0x0f, 0xca, 0xf6, 0x63,
   609  				0x0c, 0x00, 0x03, 0x00, 0x00, 0x09, 0x0e, 0x06,
   610  				0xf6, 0xda, 0xcd, 0xd1, 0x08, 0x00, 0x04, 0x00,
   611  				0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01,
   612  				0x02, 0x03, 0x0e, 0x3f, 0xf6, 0xda, 0xcd, 0xd1,
   613  			},
   614  			expected: &Counter{Name: "metric-1", Packets: 43214632547, Bytes: 2548697864523217},
   615  		},
   616  	}
   617  	for _, tc := range testCases {
   618  		t.Run(tc.name, func(t *testing.T) {
   619  			counter, err := decode(tc.msg, false)
   620  			assert.NoError(t, err)
   621  			assert.NotNil(t, counter)
   622  
   623  			assert.Equal(t, tc.expected.Name, counter.Name)
   624  			assert.Equal(t, tc.expected.Packets, counter.Packets)
   625  			assert.Equal(t, tc.expected.Bytes, counter.Bytes)
   626  		})
   627  	}
   628  }