outside_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. package nebula
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "net"
  6. "net/netip"
  7. "testing"
  8. "github.com/google/gopacket"
  9. "github.com/google/gopacket/layers"
  10. "github.com/slackhq/nebula/iputil"
  11. "github.com/slackhq/nebula/firewall"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. "golang.org/x/net/ipv4"
  15. "golang.org/x/net/ipv6"
  16. )
  17. func Test_newPacket(t *testing.T) {
  18. p := &firewall.ParsedPacket{}
  19. // length fails
  20. err := newPacket([]byte{}, true, p)
  21. require.ErrorIs(t, err, ErrPacketTooShort)
  22. err = newPacket([]byte{0x40}, true, p)
  23. require.ErrorIs(t, err, ErrIPv4PacketTooShort)
  24. err = newPacket([]byte{0x60}, true, p)
  25. require.ErrorIs(t, err, ErrIPv6PacketTooShort)
  26. // length fail with ip options
  27. h := ipv4.Header{
  28. Version: 1,
  29. Len: 100,
  30. Src: net.IPv4(10, 0, 0, 1),
  31. Dst: net.IPv4(10, 0, 0, 2),
  32. Options: []byte{0, 1, 0, 2},
  33. }
  34. b, _ := h.Marshal()
  35. err = newPacket(b, true, p)
  36. require.ErrorIs(t, err, ErrIPv4InvalidHeaderLength)
  37. // not an ipv4 packet
  38. err = newPacket([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true, p)
  39. require.ErrorIs(t, err, ErrUnknownIPVersion)
  40. // invalid ihl
  41. err = newPacket([]byte{4<<4 | (8 >> 2 & 0x0f), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true, p)
  42. require.ErrorIs(t, err, ErrIPv4InvalidHeaderLength)
  43. // account for variable ip header length - incoming
  44. h = ipv4.Header{
  45. Version: 1,
  46. Len: 100,
  47. Src: net.IPv4(10, 0, 0, 1),
  48. Dst: net.IPv4(10, 0, 0, 2),
  49. Options: []byte{0, 1, 0, 2},
  50. Protocol: iputil.IPProtocolTCP,
  51. }
  52. b, _ = h.Marshal()
  53. b = append(b, []byte{0, 3, 0, 4}...)
  54. err = newPacket(b, true, p)
  55. require.NoError(t, err)
  56. assert.Equal(t, uint8(iputil.IPProtocolTCP), p.Protocol)
  57. assert.Equal(t, netip.MustParseAddr("10.0.0.2"), p.LocalAddr)
  58. assert.Equal(t, netip.MustParseAddr("10.0.0.1"), p.RemoteAddr)
  59. assert.Equal(t, uint16(3), p.RemotePort)
  60. assert.Equal(t, uint16(4), p.LocalPort)
  61. assert.False(t, p.Fragment)
  62. // account for variable ip header length - outgoing
  63. h = ipv4.Header{
  64. Version: 1,
  65. Protocol: iputil.IPProtocolUDP,
  66. Len: 100,
  67. Src: net.IPv4(10, 0, 0, 1),
  68. Dst: net.IPv4(10, 0, 0, 2),
  69. Options: []byte{0, 1, 0, 2},
  70. }
  71. b, _ = h.Marshal()
  72. b = append(b, []byte{0, 5, 0, 6}...)
  73. err = newPacket(b, false, p)
  74. require.NoError(t, err)
  75. assert.Equal(t, uint8(iputil.IPProtocolUDP), p.Protocol)
  76. assert.Equal(t, netip.MustParseAddr("10.0.0.1"), p.LocalAddr)
  77. assert.Equal(t, netip.MustParseAddr("10.0.0.2"), p.RemoteAddr)
  78. assert.Equal(t, uint16(6), p.RemotePort)
  79. assert.Equal(t, uint16(5), p.LocalPort)
  80. assert.False(t, p.Fragment)
  81. }
  82. // Test_newPacket_v4NoPorts ensures only protocols that lead with src/dst ports report them. The first 4 bytes of other protocols are not ports,
  83. // and are often chosen by the sender: a GRE header's protocol type, an IPIP inner total length, a 6in4 flow label.
  84. // Reading them as ports would let a `proto: any` rule with a port match tunneled traffic.
  85. func Test_newPacket_v4NoPorts(t *testing.T) {
  86. p := &firewall.ParsedPacket{}
  87. for _, proto := range []uint8{4, 41, 47, 50, 51} { // IPIP, 6in4, GRE, ESP, AH
  88. h := ipv4.Header{
  89. Version: 4,
  90. Len: ipv4.HeaderLen,
  91. Protocol: int(proto),
  92. Src: net.IPv4(10, 0, 0, 1),
  93. Dst: net.IPv4(10, 0, 0, 2),
  94. }
  95. b, err := h.Marshal()
  96. require.NoError(t, err)
  97. b = append(b, []byte{0x00, 0x00, 0x86, 0xdd}...) // a GRE header carrying IPv6 would read as port 34525
  98. for _, incoming := range []bool{true, false} {
  99. require.NoError(t, newPacket(b, incoming, p))
  100. assert.Equal(t, proto, p.Protocol)
  101. assert.Equal(t, uint16(0), p.RemotePort)
  102. assert.Equal(t, uint16(0), p.LocalPort)
  103. assert.False(t, p.Fragment)
  104. }
  105. }
  106. }
  107. // Test_newPacket_portedProtocols ensures UDP-Lite, DCCP, and SCTP report ports from the same leading
  108. // src/dst port words as TCP and UDP, oriented the same way.
  109. func Test_newPacket_portedProtocols(t *testing.T) {
  110. p := &firewall.ParsedPacket{}
  111. ports := []byte{0x13, 0x88, 0x00, 0x50} // src 5000, dst 80
  112. for _, proto := range []uint8{iputil.IPProtocolUDPLite, iputil.IPProtocolDCCP, iputil.IPProtocolSCTP} {
  113. h := ipv4.Header{
  114. Version: 4,
  115. Len: ipv4.HeaderLen,
  116. Protocol: int(proto),
  117. Src: net.IPv4(10, 0, 0, 1),
  118. Dst: net.IPv4(10, 0, 0, 2),
  119. }
  120. v4, err := h.Marshal()
  121. require.NoError(t, err)
  122. v4 = append(v4, ports...)
  123. v6 := make([]byte, 40, 44)
  124. v6[0] = 0x60
  125. v6[6] = proto
  126. v6[7] = 64
  127. v6 = append(v6, ports...)
  128. for _, b := range [][]byte{v4, v6} {
  129. require.NoError(t, newPacket(b, true, p))
  130. assert.Equal(t, proto, p.Protocol)
  131. assert.Equal(t, uint16(5000), p.RemotePort)
  132. assert.Equal(t, uint16(80), p.LocalPort)
  133. require.NoError(t, newPacket(b, false, p))
  134. assert.Equal(t, uint16(5000), p.LocalPort)
  135. assert.Equal(t, uint16(80), p.RemotePort)
  136. }
  137. }
  138. }
  139. func Test_newPacket_v6(t *testing.T) {
  140. p := &firewall.ParsedPacket{}
  141. // invalid ipv6
  142. ip := layers.IPv6{
  143. Version: 6,
  144. HopLimit: 128,
  145. SrcIP: net.IPv6linklocalallrouters,
  146. DstIP: net.IPv6linklocalallnodes,
  147. }
  148. buffer := gopacket.NewSerializeBuffer()
  149. opt := gopacket.SerializeOptions{
  150. ComputeChecksums: false,
  151. FixLengths: false,
  152. }
  153. err := gopacket.SerializeLayers(buffer, opt, &ip)
  154. require.NoError(t, err)
  155. err = newPacket(buffer.Bytes(), true, p)
  156. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  157. // A v6 packet with a hop-by-hop extension
  158. // ICMPv6 Payload (Echo Request)
  159. icmpLayer := layers.ICMPv6{
  160. TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0),
  161. }
  162. // Hop-by-Hop Extension Header
  163. hopOption := layers.IPv6HopByHopOption{}
  164. hopOption.OptionData = []byte{0, 0, 0, 0}
  165. hopByHop := layers.IPv6HopByHop{}
  166. hopByHop.Options = append(hopByHop.Options, &hopOption)
  167. ip = layers.IPv6{
  168. Version: 6,
  169. HopLimit: 128,
  170. NextHeader: layers.IPProtocolIPv6Destination,
  171. SrcIP: net.IPv6linklocalallrouters,
  172. DstIP: net.IPv6linklocalallnodes,
  173. }
  174. buffer.Clear()
  175. err = gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{
  176. ComputeChecksums: false,
  177. FixLengths: true,
  178. }, &ip, &hopByHop, &icmpLayer)
  179. if err != nil {
  180. panic(err)
  181. }
  182. // Ensure buffer length checks during parsing with the next 2 tests.
  183. // A full IPv6 header and 1 byte in the first extension, but missing
  184. // the length byte.
  185. err = newPacket(buffer.Bytes()[:41], true, p)
  186. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  187. // A full IPv6 header plus 1 full extension, but only 1 byte of the
  188. // next layer, missing length byte
  189. err = newPacket(buffer.Bytes()[:49], true, p)
  190. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  191. err = nil
  192. // A good ICMP packet
  193. ip = layers.IPv6{
  194. Version: 6,
  195. NextHeader: layers.IPProtocolICMPv6,
  196. HopLimit: 128,
  197. SrcIP: net.IPv6linklocalallrouters,
  198. DstIP: net.IPv6linklocalallnodes,
  199. }
  200. icmp := layers.ICMPv6{
  201. TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0),
  202. Checksum: 0x1234,
  203. }
  204. buffer.Clear()
  205. require.NoError(t, gopacket.SerializeLayers(buffer, opt, &ip, &icmp))
  206. require.Error(t, newPacket(buffer.Bytes(), true, p))
  207. buffer.Clear()
  208. echo := layers.ICMPv6Echo{
  209. Identifier: 0xabcd,
  210. SeqNumber: 1234,
  211. }
  212. require.NoError(t, gopacket.SerializeLayers(buffer, opt, &ip, &icmp, &echo))
  213. require.NoError(t, newPacket(buffer.Bytes(), true, p))
  214. assert.Equal(t, uint8(layers.IPProtocolICMPv6), p.Protocol)
  215. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  216. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  217. assert.Equal(t, uint16(0xabcd), p.RemotePort)
  218. assert.Equal(t, uint16(0), p.LocalPort)
  219. assert.False(t, p.Fragment)
  220. // A minimal 4 byte non-echo ICMPv6 message (type, code, checksum), no identifier to read
  221. icmpMin := make([]byte, ipv6.HeaderLen+4)
  222. copy(icmpMin, buffer.Bytes()[:ipv6.HeaderLen])
  223. icmpMin[6] = byte(layers.IPProtocolICMPv6)
  224. icmpMin[ipv6.HeaderLen] = 1 // type 1, destination unreachable, not echo
  225. err = newPacket(icmpMin, true, p)
  226. require.NoError(t, err)
  227. assert.Equal(t, uint8(layers.IPProtocolICMPv6), p.Protocol)
  228. assert.Equal(t, uint16(0), p.RemotePort)
  229. assert.Equal(t, uint16(0), p.LocalPort)
  230. assert.False(t, p.Fragment)
  231. // A good ESP packet
  232. b := buffer.Bytes()
  233. b[6] = byte(layers.IPProtocolESP)
  234. err = newPacket(b, true, p)
  235. require.NoError(t, err)
  236. assert.Equal(t, uint8(layers.IPProtocolESP), p.Protocol)
  237. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  238. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  239. assert.Equal(t, uint16(0), p.RemotePort)
  240. assert.Equal(t, uint16(0), p.LocalPort)
  241. assert.False(t, p.Fragment)
  242. // A good None packet
  243. b = buffer.Bytes()
  244. b[6] = byte(layers.IPProtocolNoNextHeader)
  245. err = newPacket(b, true, p)
  246. require.NoError(t, err)
  247. assert.Equal(t, uint8(layers.IPProtocolNoNextHeader), p.Protocol)
  248. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  249. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  250. assert.Equal(t, uint16(0), p.RemotePort)
  251. assert.Equal(t, uint16(0), p.LocalPort)
  252. assert.False(t, p.Fragment)
  253. // An unknown protocol packet, we don't dissect it so we fail closed on its true protocol with no ports
  254. b = buffer.Bytes()
  255. b[6] = 255 // 255 is a reserved protocol number
  256. err = newPacket(b, true, p)
  257. require.NoError(t, err)
  258. assert.Equal(t, uint8(255), p.Protocol)
  259. assert.Equal(t, uint16(0), p.RemotePort)
  260. assert.Equal(t, uint16(0), p.LocalPort)
  261. assert.False(t, p.Fragment)
  262. // A good UDP packet
  263. ip = layers.IPv6{
  264. Version: 6,
  265. NextHeader: iputil.IPProtocolUDP,
  266. HopLimit: 128,
  267. SrcIP: net.IPv6linklocalallrouters,
  268. DstIP: net.IPv6linklocalallnodes,
  269. }
  270. udp := layers.UDP{
  271. SrcPort: layers.UDPPort(36123),
  272. DstPort: layers.UDPPort(22),
  273. }
  274. err = udp.SetNetworkLayerForChecksum(&ip)
  275. require.NoError(t, err)
  276. buffer.Clear()
  277. err = gopacket.SerializeLayers(buffer, opt, &ip, &udp, gopacket.Payload([]byte{0xde, 0xad, 0xbe, 0xef}))
  278. if err != nil {
  279. panic(err)
  280. }
  281. b = buffer.Bytes()
  282. // incoming
  283. err = newPacket(b, true, p)
  284. require.NoError(t, err)
  285. assert.Equal(t, uint8(iputil.IPProtocolUDP), p.Protocol)
  286. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  287. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  288. assert.Equal(t, uint16(36123), p.RemotePort)
  289. assert.Equal(t, uint16(22), p.LocalPort)
  290. assert.False(t, p.Fragment)
  291. // outgoing
  292. err = newPacket(b, false, p)
  293. require.NoError(t, err)
  294. assert.Equal(t, uint8(iputil.IPProtocolUDP), p.Protocol)
  295. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.LocalAddr)
  296. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.RemoteAddr)
  297. assert.Equal(t, uint16(36123), p.LocalPort)
  298. assert.Equal(t, uint16(22), p.RemotePort)
  299. assert.False(t, p.Fragment)
  300. // Too short UDP packet
  301. err = newPacket(b[:len(b)-10], false, p) // pull off the last 10 bytes
  302. require.ErrorIs(t, err, ErrIPv6PacketTooShort)
  303. // A good TCP packet
  304. b[6] = byte(layers.IPProtocolTCP)
  305. // incoming
  306. err = newPacket(b, true, p)
  307. require.NoError(t, err)
  308. assert.Equal(t, uint8(iputil.IPProtocolTCP), p.Protocol)
  309. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  310. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  311. assert.Equal(t, uint16(36123), p.RemotePort)
  312. assert.Equal(t, uint16(22), p.LocalPort)
  313. assert.False(t, p.Fragment)
  314. // outgoing
  315. err = newPacket(b, false, p)
  316. require.NoError(t, err)
  317. assert.Equal(t, uint8(iputil.IPProtocolTCP), p.Protocol)
  318. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.LocalAddr)
  319. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.RemoteAddr)
  320. assert.Equal(t, uint16(36123), p.LocalPort)
  321. assert.Equal(t, uint16(22), p.RemotePort)
  322. assert.False(t, p.Fragment)
  323. // Too short TCP packet
  324. err = newPacket(b[:len(b)-10], false, p) // pull off the last 10 bytes
  325. require.ErrorIs(t, err, ErrIPv6PacketTooShort)
  326. // A good UDP packet with an AH header
  327. ip = layers.IPv6{
  328. Version: 6,
  329. NextHeader: layers.IPProtocolAH,
  330. HopLimit: 128,
  331. SrcIP: net.IPv6linklocalallrouters,
  332. DstIP: net.IPv6linklocalallnodes,
  333. }
  334. ah := layers.IPSecAH{
  335. AuthenticationData: []byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef},
  336. }
  337. ah.NextHeader = layers.IPProtocolUDP
  338. udpHeader := []byte{
  339. 0x8d, 0x1b, // Source port 36123
  340. 0x00, 0x16, // Destination port 22
  341. 0x00, 0x00, // Length
  342. 0x00, 0x00, // Checksum
  343. }
  344. buffer.Clear()
  345. err = ip.SerializeTo(buffer, opt)
  346. if err != nil {
  347. panic(err)
  348. }
  349. b = buffer.Bytes()
  350. ahb := serializeAH(&ah)
  351. b = append(b, ahb...)
  352. b = append(b, udpHeader...)
  353. err = newPacket(b, true, p)
  354. require.NoError(t, err)
  355. assert.Equal(t, uint8(iputil.IPProtocolUDP), p.Protocol)
  356. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  357. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  358. assert.Equal(t, uint16(36123), p.RemotePort)
  359. assert.Equal(t, uint16(22), p.LocalPort)
  360. assert.False(t, p.Fragment)
  361. // Ensure buffer bounds checking during processing, a truncated AH header can't reach the payload
  362. err = newPacket(b[:41], true, p)
  363. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  364. // Invalid AH header
  365. b = buffer.Bytes()
  366. err = newPacket(b, true, p)
  367. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  368. }
  369. func Test_newPacket_ipv6Fragment(t *testing.T) {
  370. p := &firewall.ParsedPacket{}
  371. ip := &layers.IPv6{
  372. Version: 6,
  373. NextHeader: layers.IPProtocolIPv6Fragment,
  374. HopLimit: 64,
  375. SrcIP: net.IPv6linklocalallrouters,
  376. DstIP: net.IPv6linklocalallnodes,
  377. }
  378. // First fragment
  379. fragHeader1 := []byte{
  380. uint8(layers.IPProtocolUDP), // Next Header (UDP)
  381. 0x00, // Reserved
  382. 0x00, // Fragment Offset high byte (0)
  383. 0x01, // Fragment Offset low byte & flags (M=1)
  384. 0x00, 0x00, 0x00, 0x01, // Identification
  385. }
  386. udpHeader := []byte{
  387. 0x8d, 0x1b, // Source port 36123
  388. 0x00, 0x16, // Destination port 22
  389. 0x00, 0x00, // Length
  390. 0x00, 0x00, // Checksum
  391. }
  392. buffer := gopacket.NewSerializeBuffer()
  393. opts := gopacket.SerializeOptions{
  394. ComputeChecksums: true,
  395. FixLengths: true,
  396. }
  397. err := ip.SerializeTo(buffer, opts)
  398. if err != nil {
  399. t.Fatal(err)
  400. }
  401. firstFrag := buffer.Bytes()
  402. firstFrag = append(firstFrag, fragHeader1...)
  403. firstFrag = append(firstFrag, udpHeader...)
  404. firstFrag = append(firstFrag, []byte{0xde, 0xad, 0xbe, 0xef}...)
  405. // Test first fragment incoming
  406. err = newPacket(firstFrag, true, p)
  407. require.NoError(t, err)
  408. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  409. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  410. assert.Equal(t, uint8(layers.IPProtocolUDP), p.Protocol)
  411. assert.Equal(t, uint16(36123), p.RemotePort)
  412. assert.Equal(t, uint16(22), p.LocalPort)
  413. assert.False(t, p.Fragment)
  414. // Test first fragment outgoing
  415. err = newPacket(firstFrag, false, p)
  416. require.NoError(t, err)
  417. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.LocalAddr)
  418. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.RemoteAddr)
  419. assert.Equal(t, uint8(layers.IPProtocolUDP), p.Protocol)
  420. assert.Equal(t, uint16(36123), p.LocalPort)
  421. assert.Equal(t, uint16(22), p.RemotePort)
  422. assert.False(t, p.Fragment)
  423. // Second fragment
  424. fragHeader2 := []byte{
  425. uint8(layers.IPProtocolUDP), // Next Header (UDP)
  426. 0x00, // Reserved
  427. 0xb9, // Fragment Offset high byte (185)
  428. 0x01, // Fragment Offset low byte & flags (M=1)
  429. 0x00, 0x00, 0x00, 0x01, // Identification
  430. }
  431. buffer.Clear()
  432. err = ip.SerializeTo(buffer, opts)
  433. if err != nil {
  434. t.Fatal(err)
  435. }
  436. secondFrag := buffer.Bytes()
  437. secondFrag = append(secondFrag, fragHeader2...)
  438. secondFrag = append(secondFrag, []byte{0xde, 0xad, 0xbe, 0xef}...)
  439. // Test second fragment incoming
  440. err = newPacket(secondFrag, true, p)
  441. require.NoError(t, err)
  442. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.RemoteAddr)
  443. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.LocalAddr)
  444. assert.Equal(t, uint8(layers.IPProtocolUDP), p.Protocol)
  445. assert.Equal(t, uint16(0), p.RemotePort)
  446. assert.Equal(t, uint16(0), p.LocalPort)
  447. assert.True(t, p.Fragment)
  448. // Test second fragment outgoing
  449. err = newPacket(secondFrag, false, p)
  450. require.NoError(t, err)
  451. assert.Equal(t, netip.MustParseAddr("ff02::2"), p.LocalAddr)
  452. assert.Equal(t, netip.MustParseAddr("ff02::1"), p.RemoteAddr)
  453. assert.Equal(t, uint8(layers.IPProtocolUDP), p.Protocol)
  454. assert.Equal(t, uint16(0), p.LocalPort)
  455. assert.Equal(t, uint16(0), p.RemotePort)
  456. assert.True(t, p.Fragment)
  457. // Too short of a fragment packet
  458. err = newPacket(secondFrag[:len(secondFrag)-10], false, p)
  459. require.ErrorIs(t, err, iputil.ErrIPv6CouldNotFindPayload)
  460. }
  461. func BenchmarkParseV6(b *testing.B) {
  462. // Regular UDP packet
  463. ip := &layers.IPv6{
  464. Version: 6,
  465. NextHeader: layers.IPProtocolUDP,
  466. HopLimit: 64,
  467. SrcIP: net.IPv6linklocalallrouters,
  468. DstIP: net.IPv6linklocalallnodes,
  469. }
  470. udp := &layers.UDP{
  471. SrcPort: layers.UDPPort(36123),
  472. DstPort: layers.UDPPort(22),
  473. }
  474. buffer := gopacket.NewSerializeBuffer()
  475. opts := gopacket.SerializeOptions{
  476. ComputeChecksums: false,
  477. FixLengths: true,
  478. }
  479. err := gopacket.SerializeLayers(buffer, opts, ip, udp)
  480. if err != nil {
  481. b.Fatal(err)
  482. }
  483. normalPacket := buffer.Bytes()
  484. // First Fragment packet
  485. ipFrag := &layers.IPv6{
  486. Version: 6,
  487. NextHeader: layers.IPProtocolIPv6Fragment,
  488. HopLimit: 64,
  489. SrcIP: net.IPv6linklocalallrouters,
  490. DstIP: net.IPv6linklocalallnodes,
  491. }
  492. fragHeader := []byte{
  493. uint8(layers.IPProtocolUDP), // Next Header (UDP)
  494. 0x00, // Reserved
  495. 0x00, // Fragment Offset high byte (0)
  496. 0x01, // Fragment Offset low byte & flags (M=1)
  497. 0x00, 0x00, 0x00, 0x01, // Identification
  498. }
  499. udpHeader := []byte{
  500. 0x8d, 0x7b, // Source port 36123
  501. 0x00, 0x16, // Destination port 22
  502. 0x00, 0x00, // Length
  503. 0x00, 0x00, // Checksum
  504. }
  505. buffer.Clear()
  506. err = ipFrag.SerializeTo(buffer, opts)
  507. if err != nil {
  508. b.Fatal(err)
  509. }
  510. firstFrag := buffer.Bytes()
  511. firstFrag = append(firstFrag, fragHeader...)
  512. firstFrag = append(firstFrag, udpHeader...)
  513. firstFrag = append(firstFrag, []byte{0xde, 0xad, 0xbe, 0xef}...)
  514. // Second Fragment packet
  515. fragHeader[2] = 0xb9 // offset 185
  516. buffer.Clear()
  517. err = ipFrag.SerializeTo(buffer, opts)
  518. if err != nil {
  519. b.Fatal(err)
  520. }
  521. secondFrag := buffer.Bytes()
  522. secondFrag = append(secondFrag, fragHeader...)
  523. secondFrag = append(secondFrag, []byte{0xde, 0xad, 0xbe, 0xef}...)
  524. fp := &firewall.ParsedPacket{}
  525. b.Run("Normal", func(b *testing.B) {
  526. for i := 0; i < b.N; i++ {
  527. if err = parseV6(normalPacket, true, fp); err != nil {
  528. b.Fatal(err)
  529. }
  530. }
  531. })
  532. b.Run("FirstFragment", func(b *testing.B) {
  533. for i := 0; i < b.N; i++ {
  534. if err = parseV6(firstFrag, true, fp); err != nil {
  535. b.Fatal(err)
  536. }
  537. }
  538. })
  539. b.Run("SecondFragment", func(b *testing.B) {
  540. for i := 0; i < b.N; i++ {
  541. if err = parseV6(secondFrag, true, fp); err != nil {
  542. b.Fatal(err)
  543. }
  544. }
  545. })
  546. // Evil packet
  547. evilPacket := &layers.IPv6{
  548. Version: 6,
  549. NextHeader: layers.IPProtocolIPv6HopByHop,
  550. HopLimit: 64,
  551. SrcIP: net.IPv6linklocalallrouters,
  552. DstIP: net.IPv6linklocalallnodes,
  553. }
  554. hopHeader := []byte{
  555. uint8(layers.IPProtocolIPv6HopByHop), // Next Header (HopByHop)
  556. 0x00, // Length
  557. 0x00, 0x00, // Options and padding
  558. 0x00, 0x00, 0x00, 0x00, // More options and padding
  559. }
  560. lastHopHeader := []byte{
  561. uint8(layers.IPProtocolUDP), // Next Header (UDP)
  562. 0x00, // Length
  563. 0x00, 0x00, // Options and padding
  564. 0x00, 0x00, 0x00, 0x00, // More options and padding
  565. }
  566. buffer.Clear()
  567. err = evilPacket.SerializeTo(buffer, opts)
  568. if err != nil {
  569. b.Fatal(err)
  570. }
  571. evilBytes := buffer.Bytes()
  572. for range 200 {
  573. evilBytes = append(evilBytes, hopHeader...)
  574. }
  575. evilBytes = append(evilBytes, lastHopHeader...)
  576. evilBytes = append(evilBytes, udpHeader...)
  577. evilBytes = append(evilBytes, []byte{0xde, 0xad, 0xbe, 0xef}...)
  578. b.Run("200 HopByHop headers", func(b *testing.B) {
  579. for i := 0; i < b.N; i++ {
  580. if err = parseV6(evilBytes, false, fp); err != nil {
  581. b.Fatal(err)
  582. }
  583. }
  584. })
  585. }
  586. // Ensure authentication data is a multiple of 8 bytes by padding if necessary
  587. func padAuthData(authData []byte) []byte {
  588. // Length of Authentication Data must be a multiple of 8 bytes
  589. paddingLength := (8 - (len(authData) % 8)) % 8 // Only pad if necessary
  590. if paddingLength > 0 {
  591. authData = append(authData, make([]byte, paddingLength)...)
  592. }
  593. return authData
  594. }
  595. // Custom function to manually serialize IPSecAH for both IPv4 and IPv6
  596. func serializeAH(ah *layers.IPSecAH) []byte {
  597. buf := new(bytes.Buffer)
  598. // Ensure Authentication Data is a multiple of 8 bytes
  599. ah.AuthenticationData = padAuthData(ah.AuthenticationData)
  600. // Calculate Payload Length (in 32-bit words, minus 2)
  601. payloadLen := uint8((12+len(ah.AuthenticationData))/4) - 2
  602. // Serialize fields
  603. if err := binary.Write(buf, binary.BigEndian, ah.NextHeader); err != nil {
  604. panic(err)
  605. }
  606. if err := binary.Write(buf, binary.BigEndian, payloadLen); err != nil {
  607. panic(err)
  608. }
  609. if err := binary.Write(buf, binary.BigEndian, ah.Reserved); err != nil {
  610. panic(err)
  611. }
  612. if err := binary.Write(buf, binary.BigEndian, ah.SPI); err != nil {
  613. panic(err)
  614. }
  615. if err := binary.Write(buf, binary.BigEndian, ah.Seq); err != nil {
  616. panic(err)
  617. }
  618. if len(ah.AuthenticationData) > 0 {
  619. if err := binary.Write(buf, binary.BigEndian, ah.AuthenticationData); err != nil {
  620. panic(err)
  621. }
  622. }
  623. return buf.Bytes()
  624. }
  625. // Test_newPacket_v6ExtHeaderOverflow is a regression test for the IPv6 extension-header
  626. // length uint8 overflow in parseV6. A Destination-Options header with HdrExtLen=255 spans
  627. // (255+1)*8 = 2048 bytes, so the real transport header sits at offset 2088. Before the fix
  628. // the advance was computed in uint8 and wrapped to 0 (then clamped to 8), so the firewall
  629. // read the transport header ~2KB too early from attacker-controlled option bytes while the
  630. // host OS parses the real header, a firewall port/proto bypass. The fix makes parseV6 land
  631. // on the same offset the host does.
  632. func Test_newPacket_v6ExtHeaderOverflow(t *testing.T) {
  633. p := &firewall.ParsedPacket{}
  634. const (
  635. hdrLen = 40 // IPv6 header
  636. extLen = 2048 // (255+1)*8, the true Destination-Options header size
  637. realTCPAt = hdrLen + extLen // 2088, where the host reads the transport header
  638. forgedTCPAt = hdrLen + 8 // 48, where the pre-fix wrapped+clamped walk landed
  639. )
  640. pkt := make([]byte, realTCPAt+4)
  641. pkt[0] = 0x60 // version 6
  642. pkt[6] = byte(layers.IPProtocolIPv6Destination) // NextHeader -> Destination Options
  643. pkt[40] = byte(iputil.IPProtocolTCP) // Dest-Options NextHeader -> TCP
  644. pkt[41] = 255 // HdrExtLen = 255
  645. // Forged transport header at the pre-fix (wrong) offset: dst port 443.
  646. binary.BigEndian.PutUint16(pkt[forgedTCPAt+2:forgedTCPAt+4], 443)
  647. // Real transport header at the offset the host actually uses: dst port 22.
  648. binary.BigEndian.PutUint16(pkt[realTCPAt+2:realTCPAt+4], 22)
  649. require.NoError(t, newPacket(pkt, true, p))
  650. assert.Equal(t, uint8(iputil.IPProtocolTCP), p.Protocol)
  651. // LocalPort is the destination port for incoming traffic. It must be the real port (22)
  652. // the host delivers to, not the forged 443 at the overflowed offset.
  653. assert.Equal(t, uint16(22), p.LocalPort, "firewall must parse the real transport header, not the overflowed offset")
  654. }
  655. // Test_newPacket_v6ExtHeaderPastBuffer is a regression test for an extension header whose declared length
  656. // advances the walk past the end of the packet. The upper layer protocol's header isn't actually present,
  657. // so parseV6 must drop the packet rather than classify it as the terminal protocol with no ports.
  658. func Test_newPacket_v6ExtHeaderPastBuffer(t *testing.T) {
  659. p := &firewall.ParsedPacket{}
  660. pkt := make([]byte, 48)
  661. pkt[0] = 0x60
  662. pkt[6] = byte(layers.IPProtocolIPv6Destination) // Destination Options
  663. pkt[7] = 64 // hop limit
  664. pkt[40] = byte(layers.IPProtocolSCTP) // Dest Options next header = SCTP
  665. pkt[41] = 255 // declared length (255+1)*8 = 2048, past the 48 byte buffer
  666. require.ErrorIs(t, newPacket(pkt, true, p), iputil.ErrIPv6CouldNotFindPayload)
  667. }
  668. // Test_newPacket_v6ExtHeaderConfusion is a regression test for parseV6 walking any unrecognized
  669. // Next Header as if it were an ipv6 extension header. A real upper layer protocol Nebula doesn't
  670. // dissect (GRE here) is not walkable, so applying the (len+1)*8 formula marched into the GRE
  671. // payload and landed on a byte that looked like UDP, forging a protocol/port pair the firewall
  672. // would trust while the host delivered the real GRE packet. The fix fails closed: the packet
  673. // is classified as its true protocol with no ports, so it only matches an `any` rule.
  674. func Test_newPacket_v6ExtHeaderConfusion(t *testing.T) {
  675. p := &firewall.ParsedPacket{}
  676. pkt := make([]byte, 52)
  677. pkt[0] = 0x60 // version 6
  678. pkt[6] = byte(layers.IPProtocolGRE) // NextHeader = GRE, a real protocol, not an extension header
  679. pkt[7] = 64 // hop limit
  680. // Real GRE header at offset 40. Pre-fix parseV6 walked GRE as an extension header: byte 41 (0x00, the
  681. // low byte of the flags/version word below) was read as the header length, giving next=(0+1)*8=8, which
  682. // landed the walk on byte 40 (0x11), misread as NextHeader=UDP, then bytes 48-51 as ports.
  683. binary.BigEndian.PutUint16(pkt[40:42], 0x1100) // GRE flags/version; byte 40=0x11, byte 41=0x00
  684. binary.BigEndian.PutUint16(pkt[42:44], 0x86dd) // GRE protocol type, never read by parseV6
  685. binary.BigEndian.PutUint16(pkt[48:50], 53) // GRE payload, pre-fix forged RemotePort
  686. binary.BigEndian.PutUint16(pkt[50:52], 53) // pre-fix forged LocalPort
  687. require.NoError(t, newPacket(pkt, true, p))
  688. assert.Equal(t, uint8(layers.IPProtocolGRE), p.Protocol, "must classify as the true protocol, not the forged UDP")
  689. assert.Equal(t, uint16(0), p.RemotePort)
  690. assert.Equal(t, uint16(0), p.LocalPort)
  691. assert.False(t, p.Fragment)
  692. // Same confusion, but the unknown protocol sits after a real extension header. The HopByHop is walked
  693. // correctly, then GRE must still fail closed instead of being walked into its own payload. Protocol is
  694. // the only assertion that discriminates the fix here, a regression that walked GRE would misclassify it.
  695. chained := make([]byte, 60)
  696. chained[0] = 0x60 // version 6
  697. chained[6] = byte(layers.IPProtocolIPv6HopByHop) // NextHeader = HopByHop extension
  698. chained[7] = 64 // hop limit
  699. chained[40] = byte(layers.IPProtocolGRE) // HopByHop NextHeader = GRE
  700. chained[41] = 0 // HopByHop length 0 -> 8 bytes, GRE begins at offset 48
  701. binary.BigEndian.PutUint16(chained[48:50], 0x1100) // GRE flags/version, pre-fix forged NextHeader/length bait
  702. binary.BigEndian.PutUint16(chained[50:52], 0x86dd) // GRE protocol type, never read by parseV6
  703. require.NoError(t, newPacket(chained, true, p))
  704. assert.Equal(t, uint8(layers.IPProtocolGRE), p.Protocol, "must fail closed on the unknown protocol after the extension header")
  705. assert.Equal(t, uint16(0), p.RemotePort)
  706. assert.Equal(t, uint16(0), p.LocalPort)
  707. assert.False(t, p.Fragment)
  708. }
  709. // Test_newPacket_parsedFields pins the ParsedPacket byproducts the RX
  710. // batcher consumes: IPHdrLen (the true L4 offset) and FragAny (any fragment
  711. // shape at all — unlike Packet.Fragment, which is port-oriented and true
  712. // only for non-first fragments).
  713. func Test_newPacket_parsedFields(t *testing.T) {
  714. p := &firewall.ParsedPacket{}
  715. // Plain IPv4 TCP, IHL 20: L4 offset 20, no fragment shape.
  716. v4 := make([]byte, 28)
  717. v4[0] = 0x45
  718. v4[9] = iputil.IPProtocolTCP
  719. binary.BigEndian.PutUint16(v4[6:8], 0x4000) // DF only
  720. require.NoError(t, newPacket(v4, true, p))
  721. assert.Equal(t, 20, p.IPHdrLen)
  722. assert.False(t, p.FragAny)
  723. assert.False(t, p.Fragment)
  724. // IPv4 first fragment (MF set, offset 0): the firewall can read ports
  725. // (Fragment false) but the coalescer must not touch it (FragAny true).
  726. ff := make([]byte, 28)
  727. ff[0] = 0x45
  728. ff[9] = iputil.IPProtocolUDP
  729. binary.BigEndian.PutUint16(ff[6:8], 0x2000) // MF, offset 0
  730. require.NoError(t, newPacket(ff, true, p))
  731. assert.False(t, p.Fragment)
  732. assert.True(t, p.FragAny)
  733. assert.Equal(t, 20, p.IPHdrLen)
  734. // IPv4 non-first fragment (nonzero offset): both flags set.
  735. nf := make([]byte, 28)
  736. nf[0] = 0x45
  737. nf[9] = iputil.IPProtocolUDP
  738. binary.BigEndian.PutUint16(nf[6:8], 0x00b9)
  739. require.NoError(t, newPacket(nf, true, p))
  740. assert.True(t, p.Fragment)
  741. assert.True(t, p.FragAny)
  742. // IPv4 with options (IHL 24): IPHdrLen tracks the real L4 offset.
  743. opts := make([]byte, 32)
  744. opts[0] = 0x46
  745. opts[9] = iputil.IPProtocolTCP
  746. binary.BigEndian.PutUint16(opts[6:8], 0x4000)
  747. require.NoError(t, newPacket(opts, true, p))
  748. assert.Equal(t, 24, p.IPHdrLen)
  749. assert.False(t, p.FragAny)
  750. // Plain IPv6 TCP: L4 at 40.
  751. v6 := make([]byte, 60)
  752. v6[0] = 0x60
  753. v6[6] = iputil.IPProtocolTCP
  754. require.NoError(t, newPacket(v6, true, p))
  755. assert.Equal(t, 40, p.IPHdrLen)
  756. assert.False(t, p.FragAny)
  757. // IPv6 hop-by-hop then TCP: IPHdrLen lands past the extension header.
  758. hbh := make([]byte, 60)
  759. hbh[0] = 0x60
  760. hbh[6] = 0 // hop-by-hop
  761. hbh[40] = iputil.IPProtocolTCP
  762. hbh[41] = 0 // HdrExtLen 0 -> 8-byte header
  763. require.NoError(t, newPacket(hbh, true, p))
  764. assert.Equal(t, 48, p.IPHdrLen)
  765. assert.False(t, p.FragAny)
  766. // IPv6 first fragment: terminal proto resolved, FragAny set, Fragment not.
  767. f6 := make([]byte, 60)
  768. f6[0] = 0x60
  769. f6[6] = 44 // fragment extension header
  770. f6[40] = iputil.IPProtocolUDP
  771. require.NoError(t, newPacket(f6, true, p))
  772. assert.True(t, p.FragAny)
  773. assert.False(t, p.Fragment)
  774. assert.Equal(t, uint8(iputil.IPProtocolUDP), p.Protocol)
  775. // IPv6 non-first fragment: both set, walk stops at the fragment header.
  776. f6n := make([]byte, 60)
  777. f6n[0] = 0x60
  778. f6n[6] = 44
  779. f6n[40] = iputil.IPProtocolUDP
  780. binary.BigEndian.PutUint16(f6n[42:44], 0x0008)
  781. require.NoError(t, newPacket(f6n, true, p))
  782. assert.True(t, p.Fragment)
  783. assert.True(t, p.FragAny)
  784. }