outside_test.go 29 KB

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