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 }