packet.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package firewall
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/netip"
  6. )
  7. type m = map[string]any
  8. const (
  9. ProtoAny = 0 // When we want to handle HOPOPT (0) we can change this, if ever
  10. ProtoTCP = 6
  11. ProtoUDP = 17
  12. ProtoICMP = 1
  13. ProtoICMPv6 = 58
  14. PortAny = 0 // Special value for matching `port: any`
  15. PortFragment = -1 // Special value for matching `port: fragment`
  16. )
  17. // TransportTuple is the dense 5-tuple shape shared between the coalescer's
  18. // flowKey-equivalent and the firewall's PacketKey. Stored in Local/Remote
  19. // orientation so a flow's incoming and outgoing packets share the same
  20. // tuple identity. v4 addresses occupy the low 4 bytes of LocalAddr/
  21. // RemoteAddr (NOT v4-mapped form) so v4 vs v6 tuples never collide.
  22. type TransportTuple struct {
  23. LocalAddr [16]byte
  24. RemoteAddr [16]byte
  25. LocalPort uint16
  26. RemotePort uint16
  27. IsV6 bool
  28. }
  29. // PacketKey is the firewall's conntrack and ConntrackCache map key — the
  30. // dense form of the 5-tuple plus the protocol and fragment flag the
  31. // firewall actually discriminates flows on. Kept separate from Packet so
  32. // the conntrack-hit fast path doesn't pay for hashing the unique.Handle
  33. // each netip.Addr carries, and so the inbound parser can skip the
  34. // AddrFrom4/AddrFrom16 calls until rule matching actually needs them.
  35. //
  36. // Superset of the coalescer's flowKey shape (same 5-tuple, just in
  37. // Local/Remote orientation rather than wire src/dst).
  38. type PacketKey struct {
  39. TransportTuple
  40. Protocol uint8
  41. Fragment bool
  42. }
  43. type Packet struct {
  44. LocalAddr netip.Addr
  45. RemoteAddr netip.Addr
  46. // LocalPort is the destination port for incoming traffic, or the source port for outgoing. Zero for ICMP.
  47. LocalPort uint16
  48. // RemotePort is the source port for incoming traffic, or the destination port for outgoing.
  49. // For ICMP, it's the "identifier". This is only used for connection tracking, actual firewall rules will not filter on ICMP identifier
  50. RemotePort uint16
  51. Protocol uint8
  52. Fragment bool
  53. }
  54. // Key derives a PacketKey from a populated Packet. Used by the outgoing
  55. // path (inside.go) which still parses into a full Packet via newPacket
  56. // before the firewall check; the inbound path skips this hop entirely by
  57. // having its parser write straight into the PacketKey.
  58. func (fp *Packet) Key() PacketKey {
  59. k := PacketKey{
  60. Protocol: fp.Protocol,
  61. Fragment: fp.Fragment,
  62. }
  63. k.LocalPort = fp.LocalPort
  64. k.RemotePort = fp.RemotePort
  65. k.IsV6 = !fp.LocalAddr.Is4()
  66. if k.IsV6 {
  67. k.LocalAddr = fp.LocalAddr.As16()
  68. k.RemoteAddr = fp.RemoteAddr.As16()
  69. } else {
  70. v4 := fp.LocalAddr.As4()
  71. copy(k.LocalAddr[:4], v4[:])
  72. v4 = fp.RemoteAddr.As4()
  73. copy(k.RemoteAddr[:4], v4[:])
  74. }
  75. return k
  76. }
  77. // Hydrate fills fp's netip.Addr fields and copies the rest from k. Called
  78. // by the firewall slow path when conntrack misses and rule matching needs
  79. // the rich Packet form (CIDR lookups, family checks). The fast path skips
  80. // this entirely.
  81. func (k *PacketKey) Hydrate(fp *Packet) {
  82. fp.LocalPort = k.LocalPort
  83. fp.RemotePort = k.RemotePort
  84. fp.Protocol = k.Protocol
  85. fp.Fragment = k.Fragment
  86. if k.IsV6 {
  87. fp.LocalAddr = netip.AddrFrom16(k.LocalAddr)
  88. fp.RemoteAddr = netip.AddrFrom16(k.RemoteAddr)
  89. } else {
  90. var v4 [4]byte
  91. copy(v4[:], k.LocalAddr[:4])
  92. fp.LocalAddr = netip.AddrFrom4(v4)
  93. copy(v4[:], k.RemoteAddr[:4])
  94. fp.RemoteAddr = netip.AddrFrom4(v4)
  95. }
  96. }
  97. func (fp *Packet) Copy() *Packet {
  98. return &Packet{
  99. LocalAddr: fp.LocalAddr,
  100. RemoteAddr: fp.RemoteAddr,
  101. LocalPort: fp.LocalPort,
  102. RemotePort: fp.RemotePort,
  103. Protocol: fp.Protocol,
  104. Fragment: fp.Fragment,
  105. }
  106. }
  107. func (fp Packet) MarshalJSON() ([]byte, error) {
  108. var proto string
  109. switch fp.Protocol {
  110. case ProtoTCP:
  111. proto = "tcp"
  112. case ProtoICMP:
  113. proto = "icmp"
  114. case ProtoICMPv6:
  115. proto = "icmpv6"
  116. case ProtoUDP:
  117. proto = "udp"
  118. default:
  119. proto = fmt.Sprintf("unknown %v", fp.Protocol)
  120. }
  121. return json.Marshal(m{
  122. "LocalAddr": fp.LocalAddr.String(),
  123. "RemoteAddr": fp.RemoteAddr.String(),
  124. "LocalPort": fp.LocalPort,
  125. "RemotePort": fp.RemotePort,
  126. "Protocol": proto,
  127. "Fragment": fp.Fragment,
  128. })
  129. }