github.com/cilium/cilium@v1.16.2/pkg/maps/ctmap/types.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ctmap 5 6 import ( 7 "fmt" 8 "strings" 9 "unsafe" 10 11 "github.com/cilium/cilium/pkg/bpf" 12 "github.com/cilium/cilium/pkg/byteorder" 13 "github.com/cilium/cilium/pkg/option" 14 "github.com/cilium/cilium/pkg/tuple" 15 ) 16 17 // mapType is a type of connection tracking map. 18 type mapType int 19 20 const ( 21 // mapTypeIPv4TCPLocal and friends are map types which correspond to a 22 // combination of the following attributes: 23 // * IPv4 or IPv6; 24 // * TCP or non-TCP (shortened to Any) 25 // * Local (endpoint-specific) or global (endpoint-oblivious). 26 mapTypeIPv4TCPLocal mapType = iota 27 mapTypeIPv6TCPLocal 28 mapTypeIPv4TCPGlobal 29 mapTypeIPv6TCPGlobal 30 mapTypeIPv4AnyLocal 31 mapTypeIPv6AnyLocal 32 mapTypeIPv4AnyGlobal 33 mapTypeIPv6AnyGlobal 34 mapTypeMax 35 ) 36 37 // String renders the map type into a user-readable string. 38 func (m mapType) String() string { 39 switch m { 40 case mapTypeIPv4TCPLocal: 41 return "Local IPv4 TCP CT map" 42 case mapTypeIPv6TCPLocal: 43 return "Local IPv6 TCP CT map" 44 case mapTypeIPv4TCPGlobal: 45 return "Global IPv4 TCP CT map" 46 case mapTypeIPv6TCPGlobal: 47 return "Global IPv6 TCP CT map" 48 case mapTypeIPv4AnyLocal: 49 return "Local IPv4 non-TCP CT map" 50 case mapTypeIPv6AnyLocal: 51 return "Local IPv6 non-TCP CT map" 52 case mapTypeIPv4AnyGlobal: 53 return "Global IPv4 non-TCP CT map" 54 case mapTypeIPv6AnyGlobal: 55 return "Global IPv6 non-TCP CT map" 56 } 57 return fmt.Sprintf("Unknown (%d)", int(m)) 58 } 59 60 func (m mapType) name() string { 61 switch m { 62 case mapTypeIPv4TCPLocal, mapTypeIPv4TCPGlobal: 63 return "tcp4" 64 case mapTypeIPv6TCPLocal, mapTypeIPv6TCPGlobal: 65 return "tcp6" 66 case mapTypeIPv4AnyLocal, mapTypeIPv4AnyGlobal: 67 return "any4" 68 case mapTypeIPv6AnyLocal, mapTypeIPv6AnyGlobal: 69 return "any6" 70 default: 71 panic("Unexpected map type " + m.String()) 72 } 73 } 74 75 func (m mapType) isIPv4() bool { 76 switch m { 77 case mapTypeIPv4TCPLocal, mapTypeIPv4TCPGlobal, mapTypeIPv4AnyLocal, mapTypeIPv4AnyGlobal: 78 return true 79 } 80 return false 81 } 82 83 func (m mapType) isIPv6() bool { 84 switch m { 85 case mapTypeIPv6TCPLocal, mapTypeIPv6TCPGlobal, mapTypeIPv6AnyLocal, mapTypeIPv6AnyGlobal: 86 return true 87 } 88 return false 89 } 90 91 func (m mapType) isLocal() bool { 92 switch m { 93 case mapTypeIPv4TCPLocal, mapTypeIPv6TCPLocal, mapTypeIPv4AnyLocal, mapTypeIPv6AnyLocal: 94 return true 95 } 96 return false 97 } 98 99 func (m mapType) isGlobal() bool { 100 switch m { 101 case mapTypeIPv4TCPGlobal, mapTypeIPv6TCPGlobal, mapTypeIPv4AnyGlobal, mapTypeIPv6AnyGlobal: 102 return true 103 } 104 return false 105 } 106 107 func (m mapType) isTCP() bool { 108 switch m { 109 case mapTypeIPv4TCPLocal, mapTypeIPv6TCPLocal, mapTypeIPv4TCPGlobal, mapTypeIPv6TCPGlobal: 110 return true 111 } 112 return false 113 } 114 115 func (m mapType) key() bpf.MapKey { 116 switch m { 117 case mapTypeIPv4TCPLocal, mapTypeIPv4AnyLocal: 118 return &CtKey4{} 119 case mapTypeIPv6TCPLocal, mapTypeIPv6AnyLocal: 120 return &CtKey6{} 121 case mapTypeIPv4TCPGlobal, mapTypeIPv4AnyGlobal: 122 return &CtKey4Global{} 123 case mapTypeIPv6TCPGlobal, mapTypeIPv6AnyGlobal: 124 return &CtKey6Global{} 125 default: 126 panic("Unexpected map type " + m.String()) 127 } 128 } 129 130 func (m mapType) value() bpf.MapValue { 131 return &CtEntry{} 132 } 133 134 func (m mapType) maxEntries() int { 135 switch m { 136 case mapTypeIPv4TCPGlobal, mapTypeIPv6TCPGlobal: 137 if option.Config.CTMapEntriesGlobalTCP != 0 { 138 return option.Config.CTMapEntriesGlobalTCP 139 } 140 return option.CTMapEntriesGlobalTCPDefault 141 142 case mapTypeIPv4AnyGlobal, mapTypeIPv6AnyGlobal: 143 if option.Config.CTMapEntriesGlobalAny != 0 { 144 return option.Config.CTMapEntriesGlobalAny 145 } 146 return option.CTMapEntriesGlobalAnyDefault 147 148 case mapTypeIPv4TCPLocal, mapTypeIPv6TCPLocal, mapTypeIPv4AnyLocal, mapTypeIPv6AnyLocal: 149 return mapNumEntriesLocal 150 151 default: 152 panic("Unexpected map type " + m.String()) 153 } 154 } 155 156 func (m mapType) bpfDefine() string { 157 switch m { 158 case mapTypeIPv4TCPLocal, mapTypeIPv4TCPGlobal: 159 return "CT_MAP_TCP4" 160 case mapTypeIPv6TCPLocal, mapTypeIPv6TCPGlobal: 161 return "CT_MAP_TCP6" 162 case mapTypeIPv4AnyLocal, mapTypeIPv4AnyGlobal: 163 return "CT_MAP_ANY4" 164 case mapTypeIPv6AnyLocal, mapTypeIPv6AnyGlobal: 165 return "CT_MAP_ANY6" 166 default: 167 panic("Unexpected map type " + m.String()) 168 } 169 } 170 171 type CTMapIPVersion int 172 173 const ( 174 CTMapIPv4 CTMapIPVersion = iota 175 CTMapIPv6 176 ) 177 178 // FilterMapsByProto filters the given CT maps by the given IP version, and 179 // returns two maps - one for TCP and one for any protocol. 180 func FilterMapsByProto(maps []*Map, ipVsn CTMapIPVersion) (ctMapTCP *Map, ctMapAny *Map) { 181 for _, m := range maps { 182 switch ipVsn { 183 case CTMapIPv4: 184 switch m.mapType { 185 case mapTypeIPv4TCPLocal, mapTypeIPv4TCPGlobal: 186 ctMapTCP = m 187 case mapTypeIPv4AnyLocal, mapTypeIPv4AnyGlobal: 188 ctMapAny = m 189 } 190 case CTMapIPv6: 191 switch m.mapType { 192 case mapTypeIPv6TCPLocal, mapTypeIPv6TCPGlobal: 193 ctMapTCP = m 194 case mapTypeIPv6AnyLocal, mapTypeIPv6AnyGlobal: 195 ctMapAny = m 196 } 197 } 198 } 199 return 200 } 201 202 type CtKey interface { 203 bpf.MapKey 204 205 // ToNetwork converts fields to network byte order. 206 ToNetwork() CtKey 207 208 // ToHost converts fields to host byte order. 209 ToHost() CtKey 210 211 // Dump contents of key to sb. Returns true if successful. 212 Dump(sb *strings.Builder, reverse bool) bool 213 214 // GetFlags flags containing the direction of the CtKey. 215 GetFlags() uint8 216 217 GetTupleKey() tuple.TupleKey 218 } 219 220 type CtKey4 struct { 221 tuple.TupleKey4 222 } 223 224 // ToNetwork converts CtKey4 ports to network byte order. 225 func (k *CtKey4) ToNetwork() CtKey { 226 n := *k 227 n.SourcePort = byteorder.HostToNetwork16(n.SourcePort) 228 n.DestPort = byteorder.HostToNetwork16(n.DestPort) 229 return &n 230 } 231 232 // ToHost converts CtKey ports to host byte order. 233 func (k *CtKey4) ToHost() CtKey { 234 n := *k 235 n.SourcePort = byteorder.NetworkToHost16(n.SourcePort) 236 n.DestPort = byteorder.NetworkToHost16(n.DestPort) 237 return &n 238 } 239 240 // GetFlags returns the tuple's flags. 241 func (k *CtKey4) GetFlags() uint8 { 242 return k.Flags 243 } 244 245 func (k *CtKey4) String() string { 246 return fmt.Sprintf("%s:%d, %d, %d, %d", k.DestAddr, k.SourcePort, k.DestPort, k.NextHeader, k.Flags) 247 } 248 249 func (k *CtKey4) New() bpf.MapKey { return &CtKey4{} } 250 251 // Dump writes the contents of key to sb and returns true if the value for next 252 // header in the key is nonzero. 253 func (k *CtKey4) Dump(sb *strings.Builder, reverse bool) bool { 254 var addrDest string 255 256 if k.NextHeader == 0 { 257 return false 258 } 259 260 // Addresses swapped, see issue #5848 261 if reverse { 262 addrDest = k.SourceAddr.String() 263 } else { 264 addrDest = k.DestAddr.String() 265 } 266 267 if k.Flags&TUPLE_F_SERVICE != 0 { 268 sb.WriteString(fmt.Sprintf("%s SVC %s %d:%d ", 269 k.NextHeader.String(), k.DestAddr.String(), k.DestPort, 270 k.SourcePort), 271 ) 272 } else if k.Flags&TUPLE_F_IN != 0 { 273 sb.WriteString(fmt.Sprintf("%s IN %s %d:%d ", 274 k.NextHeader.String(), addrDest, k.SourcePort, 275 k.DestPort), 276 ) 277 } else { 278 sb.WriteString(fmt.Sprintf("%s OUT %s %d:%d ", 279 k.NextHeader.String(), addrDest, k.DestPort, 280 k.SourcePort), 281 ) 282 } 283 284 if k.Flags&TUPLE_F_RELATED != 0 { 285 sb.WriteString("related ") 286 } 287 288 return true 289 } 290 291 func (k *CtKey4) GetTupleKey() tuple.TupleKey { 292 return &k.TupleKey4 293 } 294 295 type CtKey4Global struct { 296 tuple.TupleKey4Global 297 } 298 299 // ToNetwork converts ports to network byte order. 300 // 301 // This is necessary to prevent callers from implicitly converting 302 // the CtKey4Global type here into a local key type in the nested 303 // TupleKey4Global field. 304 func (k *CtKey4Global) ToNetwork() CtKey { 305 return &CtKey4Global{ 306 TupleKey4Global: *k.TupleKey4Global.ToNetwork().(*tuple.TupleKey4Global), 307 } 308 } 309 310 // ToHost converts ports to host byte order. 311 // 312 // This is necessary to prevent callers from implicitly converting 313 // the CtKey4Global type here into a local key type in the nested 314 // TupleKey4Global field. 315 func (k *CtKey4Global) ToHost() CtKey { 316 return &CtKey4Global{ 317 TupleKey4Global: *k.TupleKey4Global.ToHost().(*tuple.TupleKey4Global), 318 } 319 } 320 321 // GetFlags returns the tuple's flags. 322 func (k *CtKey4Global) GetFlags() uint8 { 323 return k.Flags 324 } 325 326 func (k *CtKey4Global) String() string { 327 return fmt.Sprintf("%s:%d --> %s:%d, %d, %d", k.SourceAddr, k.SourcePort, k.DestAddr, k.DestPort, k.NextHeader, k.Flags) 328 } 329 330 func (k *CtKey4Global) New() bpf.MapKey { return &CtKey4Global{} } 331 332 // Dump writes the contents of key to sb and returns true if the value for next 333 // header in the key is nonzero. 334 func (k *CtKey4Global) Dump(sb *strings.Builder, reverse bool) bool { 335 var addrSource, addrDest string 336 337 if k.NextHeader == 0 { 338 return false 339 } 340 341 // Addresses swapped, see issue #5848 342 if reverse { 343 addrSource = k.DestAddr.String() 344 addrDest = k.SourceAddr.String() 345 } else { 346 addrSource = k.SourceAddr.String() 347 addrDest = k.DestAddr.String() 348 } 349 350 if k.Flags&TUPLE_F_SERVICE != 0 { 351 sb.WriteString(fmt.Sprintf("%s SVC %s:%d -> %s:%d ", 352 k.NextHeader.String(), k.SourceAddr.String(), k.DestPort, 353 k.DestAddr.String(), k.SourcePort), 354 ) 355 } else if k.Flags&TUPLE_F_IN != 0 { 356 sb.WriteString(fmt.Sprintf("%s IN %s:%d -> %s:%d ", 357 k.NextHeader.String(), addrSource, k.SourcePort, 358 addrDest, k.DestPort), 359 ) 360 } else { 361 sb.WriteString(fmt.Sprintf("%s OUT %s:%d -> %s:%d ", 362 k.NextHeader.String(), addrSource, k.SourcePort, 363 addrDest, k.DestPort), 364 ) 365 } 366 367 if k.Flags&TUPLE_F_RELATED != 0 { 368 sb.WriteString("related ") 369 } 370 371 return true 372 } 373 374 func (k *CtKey4Global) GetTupleKey() tuple.TupleKey { 375 return &k.TupleKey4Global 376 } 377 378 // CtKey6 is needed to provide CtEntry type to Lookup values 379 type CtKey6 struct { 380 tuple.TupleKey6 381 } 382 383 // ToNetwork converts CtKey6 ports to network byte order. 384 func (k *CtKey6) ToNetwork() CtKey { 385 return &CtKey6{ 386 TupleKey6: *k.TupleKey6.ToNetwork().(*tuple.TupleKey6), 387 } 388 } 389 390 // ToHost converts CtKey ports to host byte order. 391 func (k *CtKey6) ToHost() CtKey { 392 return &CtKey6{ 393 TupleKey6: *k.TupleKey6.ToHost().(*tuple.TupleKey6), 394 } 395 } 396 397 // GetFlags returns the tuple's flags. 398 func (k *CtKey6) GetFlags() uint8 { 399 return k.Flags 400 } 401 402 func (k *CtKey6) String() string { 403 return fmt.Sprintf("[%s]:%d, %d, %d, %d", k.DestAddr, k.SourcePort, k.DestPort, k.NextHeader, k.Flags) 404 } 405 406 func (k *CtKey6) New() bpf.MapKey { return &CtKey6{} } 407 408 // Dump writes the contents of key to sb and returns true if the value for next 409 // header in the key is nonzero. 410 func (k *CtKey6) Dump(sb *strings.Builder, reverse bool) bool { 411 var addrDest string 412 413 if k.NextHeader == 0 { 414 return false 415 } 416 417 // Addresses swapped, see issue #5848 418 if reverse { 419 addrDest = k.SourceAddr.String() 420 } else { 421 addrDest = k.DestAddr.String() 422 } 423 424 if k.Flags&TUPLE_F_SERVICE != 0 { 425 sb.WriteString(fmt.Sprintf("%s SVC %s %d:%d ", 426 k.NextHeader.String(), k.DestAddr.String(), k.DestPort, 427 k.SourcePort), 428 ) 429 } else if k.Flags&TUPLE_F_IN != 0 { 430 sb.WriteString(fmt.Sprintf("%s IN %s %d:%d ", 431 k.NextHeader.String(), addrDest, k.SourcePort, 432 k.DestPort), 433 ) 434 } else { 435 sb.WriteString(fmt.Sprintf("%s OUT %s %d:%d ", 436 k.NextHeader.String(), addrDest, k.DestPort, 437 k.SourcePort), 438 ) 439 } 440 441 if k.Flags&TUPLE_F_RELATED != 0 { 442 sb.WriteString("related ") 443 } 444 445 return true 446 } 447 448 func (k *CtKey6) GetTupleKey() tuple.TupleKey { 449 return &k.TupleKey6 450 } 451 452 // CtKey6Global is needed to provide CtEntry type to Lookup values 453 type CtKey6Global struct { 454 tuple.TupleKey6Global 455 } 456 457 const SizeofCtKey6Global = int(unsafe.Sizeof(CtKey6Global{})) 458 459 // ToNetwork converts ports to network byte order. 460 // 461 // This is necessary to prevent callers from implicitly converting 462 // the CtKey6Global type here into a local key type in the nested 463 // TupleKey6Global field. 464 func (k *CtKey6Global) ToNetwork() CtKey { 465 return &CtKey6Global{ 466 TupleKey6Global: *k.TupleKey6Global.ToNetwork().(*tuple.TupleKey6Global), 467 } 468 } 469 470 // ToHost converts ports to host byte order. 471 // 472 // This is necessary to prevent callers from implicitly converting 473 // the CtKey6Global type here into a local key type in the nested 474 // TupleKey6Global field. 475 func (k *CtKey6Global) ToHost() CtKey { 476 return &CtKey6Global{ 477 TupleKey6Global: *k.TupleKey6Global.ToHost().(*tuple.TupleKey6Global), 478 } 479 } 480 481 // GetFlags returns the tuple's flags. 482 func (k *CtKey6Global) GetFlags() uint8 { 483 return k.Flags 484 } 485 486 func (k *CtKey6Global) String() string { 487 return fmt.Sprintf("[%s]:%d --> [%s]:%d, %d, %d", k.SourceAddr, k.SourcePort, k.DestAddr, k.DestPort, k.NextHeader, k.Flags) 488 } 489 490 func (k *CtKey6Global) New() bpf.MapKey { return &CtKey6Global{} } 491 492 // Dump writes the contents of key to sb and returns true if the value for next 493 // header in the key is nonzero. 494 func (k *CtKey6Global) Dump(sb *strings.Builder, reverse bool) bool { 495 var addrSource, addrDest string 496 497 if k.NextHeader == 0 { 498 return false 499 } 500 501 // Addresses swapped, see issue #5848 502 if reverse { 503 addrSource = k.DestAddr.String() 504 addrDest = k.SourceAddr.String() 505 } else { 506 addrSource = k.SourceAddr.String() 507 addrDest = k.DestAddr.String() 508 } 509 510 if k.Flags&TUPLE_F_SERVICE != 0 { 511 sb.WriteString(fmt.Sprintf("%s SVC %s:%d -> %s:%d ", 512 k.NextHeader.String(), k.SourceAddr.String(), k.DestPort, 513 k.DestAddr.String(), k.SourcePort), 514 ) 515 } else if k.Flags&TUPLE_F_IN != 0 { 516 sb.WriteString(fmt.Sprintf("%s IN %s:%d -> %s:%d ", 517 k.NextHeader.String(), addrSource, k.SourcePort, 518 addrDest, k.DestPort), 519 ) 520 } else { 521 sb.WriteString(fmt.Sprintf("%s OUT %s:%d -> %s:%d ", 522 k.NextHeader.String(), addrSource, k.SourcePort, 523 addrDest, k.DestPort), 524 ) 525 } 526 527 if k.Flags&TUPLE_F_RELATED != 0 { 528 sb.WriteString("related ") 529 } 530 531 return true 532 } 533 534 func (k *CtKey6Global) GetTupleKey() tuple.TupleKey { 535 return &k.TupleKey6Global 536 } 537 538 // CtEntry represents an entry in the connection tracking table. 539 type CtEntry struct { 540 Reserved0 uint64 `align:"reserved0"` 541 BackendID uint64 `align:"backend_id"` 542 Packets uint64 `align:"packets"` 543 Bytes uint64 `align:"bytes"` 544 Lifetime uint32 `align:"lifetime"` 545 Flags uint16 `align:"rx_closing"` 546 // RevNAT is in network byte order 547 RevNAT uint16 `align:"rev_nat_index"` 548 IfIndex uint16 `align:"ifindex"` 549 TxFlagsSeen uint8 `align:"tx_flags_seen"` 550 RxFlagsSeen uint8 `align:"rx_flags_seen"` 551 SourceSecurityID uint32 `align:"src_sec_id"` 552 LastTxReport uint32 `align:"last_tx_report"` 553 LastRxReport uint32 `align:"last_rx_report"` 554 } 555 556 const SizeofCtEntry = int(unsafe.Sizeof(CtEntry{})) 557 558 const ( 559 RxClosing = 1 << iota 560 TxClosing 561 Nat64 562 LBLoopback 563 SeenNonSyn 564 NodePort 565 ProxyRedirect 566 DSRInternal 567 FromL7LB 568 Reserved1 569 FromTunnel 570 MaxFlags 571 ) 572 573 func (c *CtEntry) isDsrInternalEntry() bool { 574 return c.Flags&DSRInternal != 0 575 } 576 577 func (c *CtEntry) flagsString() string { 578 var sb strings.Builder 579 580 sb.WriteString(fmt.Sprintf("Flags=%#04x [ ", c.Flags)) 581 if (c.Flags & RxClosing) != 0 { 582 sb.WriteString("RxClosing ") 583 } 584 if (c.Flags & TxClosing) != 0 { 585 sb.WriteString("TxClosing ") 586 } 587 if (c.Flags & Nat64) != 0 { 588 sb.WriteString("Nat64 ") 589 } 590 if (c.Flags & LBLoopback) != 0 { 591 sb.WriteString("LBLoopback ") 592 } 593 if (c.Flags & SeenNonSyn) != 0 { 594 sb.WriteString("SeenNonSyn ") 595 } 596 if (c.Flags & NodePort) != 0 { 597 sb.WriteString("NodePort ") 598 } 599 if (c.Flags & ProxyRedirect) != 0 { 600 sb.WriteString("ProxyRedirect ") 601 } 602 if (c.Flags & DSRInternal) != 0 { 603 sb.WriteString("DSRInternal ") 604 } 605 if (c.Flags & FromL7LB) != 0 { 606 sb.WriteString("FromL7LB ") 607 } 608 if (c.Flags & FromTunnel) != 0 { 609 sb.WriteString("FromTunnel ") 610 } 611 612 unknownFlags := c.Flags 613 unknownFlags &^= MaxFlags - 1 614 if unknownFlags != 0 { 615 sb.WriteString(fmt.Sprintf("Unknown=%#04x ", unknownFlags)) 616 } 617 sb.WriteString("]") 618 return sb.String() 619 } 620 621 func (c *CtEntry) StringWithTimeDiff(toRemSecs func(uint32) string) string { 622 623 var timeDiff string 624 if toRemSecs != nil { 625 timeDiff = fmt.Sprintf(" (%s)", toRemSecs(c.Lifetime)) 626 } else { 627 timeDiff = "" 628 } 629 630 return fmt.Sprintf("expires=%d%s Packets=%d Bytes=%d RxFlagsSeen=%#02x LastRxReport=%d TxFlagsSeen=%#02x LastTxReport=%d %s RevNAT=%d SourceSecurityID=%d IfIndex=%d BackendID=%d \n", 631 c.Lifetime, 632 timeDiff, 633 c.Packets, 634 c.Bytes, 635 c.RxFlagsSeen, 636 c.LastRxReport, 637 c.TxFlagsSeen, 638 c.LastTxReport, 639 c.flagsString(), 640 byteorder.NetworkToHost16(c.RevNAT), 641 c.SourceSecurityID, 642 c.IfIndex, 643 c.BackendID) 644 } 645 646 // String returns the readable format 647 func (c *CtEntry) String() string { 648 return c.StringWithTimeDiff(nil) 649 } 650 651 func (c *CtEntry) New() bpf.MapValue { return &CtEntry{} }