1
0

outside_test.go 27 KB

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