outside_test.go 29 KB

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