packet.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package firewall
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/netip"
  6. "github.com/slackhq/nebula/iputil"
  7. )
  8. type m = map[string]any
  9. const (
  10. ProtoAny = 0 // When we want to handle HOPOPT (0) we can change this, if ever
  11. PortAny = 0 // Special value for matching `port: any`
  12. PortFragment = -1 // Special value for matching `port: fragment`
  13. )
  14. type Packet struct {
  15. LocalAddr netip.Addr
  16. RemoteAddr netip.Addr
  17. // LocalPort is the destination port for incoming traffic, or the source port for outgoing. Zero for ICMP.
  18. LocalPort uint16
  19. // RemotePort is the source port for incoming traffic, or the destination port for outgoing.
  20. // For ICMP, it's the "identifier". This is only used for connection tracking, actual firewall rules will not filter on ICMP identifier
  21. RemotePort uint16
  22. Protocol uint8
  23. Fragment bool
  24. }
  25. func (fp *Packet) Copy() *Packet {
  26. return &Packet{
  27. LocalAddr: fp.LocalAddr,
  28. RemoteAddr: fp.RemoteAddr,
  29. LocalPort: fp.LocalPort,
  30. RemotePort: fp.RemotePort,
  31. Protocol: fp.Protocol,
  32. Fragment: fp.Fragment,
  33. }
  34. }
  35. func (fp Packet) MarshalJSON() ([]byte, error) {
  36. return json.Marshal(m{
  37. "LocalAddr": fp.LocalAddr.String(),
  38. "RemoteAddr": fp.RemoteAddr.String(),
  39. "LocalPort": fp.LocalPort,
  40. "RemotePort": fp.RemotePort,
  41. "Protocol": protoName(fp.Protocol),
  42. "Fragment": fp.Fragment,
  43. })
  44. }
  45. // protoNames are the protocol names that rules accept and packets report, and their numbers. Any other protocol
  46. // is given by number.
  47. var protoNames = []struct {
  48. name string
  49. proto uint8
  50. }{
  51. {"any", ProtoAny},
  52. {"tcp", iputil.IPProtocolTCP},
  53. {"udp", iputil.IPProtocolUDP},
  54. {"udplite", iputil.IPProtocolUDPLite},
  55. {"dccp", iputil.IPProtocolDCCP},
  56. {"sctp", iputil.IPProtocolSCTP},
  57. {"icmp", iputil.IPProtocolICMP},
  58. {"icmpv6", iputil.IPProtocolICMPv6},
  59. }
  60. // protoByName returns the number of a protocol named in protoNames.
  61. func protoByName(name string) (uint8, bool) {
  62. for _, p := range protoNames {
  63. if p.name == name {
  64. return p.proto, true
  65. }
  66. }
  67. return 0, false
  68. }
  69. // protoName returns the name in protoNames of a packet's protocol, or "unknown" and its number. `any` is only
  70. // a rule's protocol, so a packet's protocol 0 is unknown.
  71. func protoName(proto uint8) string {
  72. if proto != ProtoAny {
  73. for _, p := range protoNames {
  74. if p.proto == proto {
  75. return p.name
  76. }
  77. }
  78. }
  79. return fmt.Sprintf("unknown %v", proto)
  80. }
  81. // ParsedPacket is a Packet plus the parse byproducts the RX path reuses
  82. type ParsedPacket struct {
  83. Packet
  84. IPHdrLen int
  85. // FragAny reports any fragmentation at all: MF flag or nonzero offset for IPv4, a fragment extension header for IPv6.
  86. // Distinct from Packet.Fragment, which is true only for NON-FIRST fragments
  87. FragAny bool
  88. }