inside_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package nebula
  2. import (
  3. "encoding/binary"
  4. "io"
  5. "net/netip"
  6. "testing"
  7. "github.com/gaissmai/bart"
  8. "github.com/slackhq/nebula/firewall"
  9. "github.com/slackhq/nebula/iputil"
  10. "github.com/slackhq/nebula/overlay/tio"
  11. "github.com/slackhq/nebula/test"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. )
  15. const (
  16. ipv4HeaderLen = 20
  17. ipv6HeaderLen = 40
  18. )
  19. // capturingTun is a tio.Queue that records what is written to it. A queue that
  20. // discards writes is indistinguishable from a packet that was never forwarded.
  21. type capturingTun struct {
  22. writes [][]byte
  23. }
  24. func (c *capturingTun) Read() ([]tio.Packet, error) { return nil, io.EOF }
  25. func (c *capturingTun) Close() error { return nil }
  26. func (c *capturingTun) Write(b []byte) (int, error) {
  27. c.writes = append(c.writes, append([]byte(nil), b...))
  28. return len(b), nil
  29. }
  30. func newSelfForwardInterface(myAddrs ...netip.Addr) (*Interface, *capturingTun) {
  31. vpnAddrs := &bart.Lite{}
  32. for _, a := range myAddrs {
  33. vpnAddrs.Insert(netip.PrefixFrom(a, a.BitLen()))
  34. }
  35. tun := &capturingTun{}
  36. return &Interface{
  37. l: test.NewLogger(),
  38. myVpnAddrsTable: vpnAddrs,
  39. myBroadcastAddrsTable: &bart.Lite{},
  40. queues: []tio.Queue{tun},
  41. }, tun
  42. }
  43. func consumeInside(f *Interface, packet []byte) {
  44. f.consumeInsidePacket(tio.Packet{Bytes: packet}, &firewall.ParsedPacket{}, make([]byte, 12), nil, make([]byte, mtu), 0, nil)
  45. }
  46. // l4Proto describes one upper-layer header for these tests: its IP next-header
  47. // value, where its checksum field sits within the header, and how to build a
  48. // minimal instance of it.
  49. type l4Proto struct {
  50. name string
  51. nextHdr uint8
  52. cksumAt int
  53. build func() []byte
  54. }
  55. var (
  56. tcpSyn = l4Proto{"tcp", iputil.IPProtocolTCP, 16, func() []byte {
  57. h := make([]byte, 20)
  58. binary.BigEndian.PutUint16(h[0:2], 49152)
  59. binary.BigEndian.PutUint16(h[2:4], 443)
  60. binary.BigEndian.PutUint32(h[4:8], 0x11223344) // sequence
  61. h[12] = 5 << 4 // data offset, no options
  62. h[13] = 0x02 // SYN
  63. binary.BigEndian.PutUint16(h[14:16], 65535) // window
  64. return h
  65. }}
  66. udpDatagram = l4Proto{"udp", iputil.IPProtocolUDP, 6, func() []byte {
  67. h := make([]byte, 8+4)
  68. binary.BigEndian.PutUint16(h[0:2], 49152)
  69. binary.BigEndian.PutUint16(h[2:4], 53)
  70. binary.BigEndian.PutUint16(h[4:6], uint16(len(h)))
  71. copy(h[8:], "ping")
  72. return h
  73. }}
  74. icmpEcho = l4Proto{"icmp", iputil.IPProtocolICMP, 2, func() []byte { return echoRequest(8) }}
  75. icmpv6Echo = l4Proto{"icmpv6", iputil.IPProtocolICMPv6, 2, func() []byte { return echoRequest(128) }}
  76. )
  77. // echoRequest builds an echo request body. The type differs between ICMP and
  78. // ICMPv6, the rest of the header does not.
  79. func echoRequest(typ uint8) []byte {
  80. h := make([]byte, 8)
  81. h[0] = typ
  82. binary.BigEndian.PutUint16(h[4:6], 0xbeef) // identifier
  83. binary.BigEndian.PutUint16(h[6:8], 1) // sequence
  84. return h
  85. }
  86. func buildIPv6(src, dst netip.Addr, p l4Proto) []byte {
  87. l4 := p.build()
  88. pkt := make([]byte, ipv6HeaderLen+len(l4))
  89. pkt[0] = 0x60
  90. binary.BigEndian.PutUint16(pkt[4:6], uint16(len(l4)))
  91. pkt[6] = p.nextHdr
  92. pkt[7] = 64
  93. copy(pkt[8:24], src.AsSlice())
  94. copy(pkt[24:40], dst.AsSlice())
  95. copy(pkt[ipv6HeaderLen:], l4)
  96. if l4 := pkt[ipv6HeaderLen:]; p.nextHdr == iputil.IPProtocolTCP || p.nextHdr == iputil.IPProtocolUDP {
  97. sum := ipv6PseudoheaderSum(src, dst, uint32(p.nextHdr), uint32(len(l4)))
  98. binary.BigEndian.PutUint16(l4[p.cksumAt:], ^fold(sumBytes(l4, sum)))
  99. }
  100. return pkt
  101. }
  102. func buildIPv4(src, dst netip.Addr, p l4Proto) []byte {
  103. l4 := p.build()
  104. pkt := make([]byte, ipv4HeaderLen+len(l4))
  105. pkt[0] = 0x45
  106. binary.BigEndian.PutUint16(pkt[2:4], uint16(len(pkt)))
  107. pkt[8] = 64
  108. pkt[9] = p.nextHdr
  109. copy(pkt[12:16], src.AsSlice())
  110. copy(pkt[16:20], dst.AsSlice())
  111. copy(pkt[ipv4HeaderLen:], l4)
  112. if l4 := pkt[ipv4HeaderLen:]; p.nextHdr == iputil.IPProtocolTCP || p.nextHdr == iputil.IPProtocolUDP {
  113. sum := sumBytes(pkt[12:20], uint32(p.nextHdr)+uint32(len(l4)))
  114. binary.BigEndian.PutUint16(l4[p.cksumAt:], ^fold(sumBytes(l4, sum)))
  115. }
  116. return pkt
  117. }
  118. // ipv6PseudoheaderSum is the RFC 2460 section 8.1 pseudo-header sum: source,
  119. // destination, a 32 bit upper-layer packet length and a 32 bit zero-padded next
  120. // header. Kept local to the test so these assertions do not check nebula's
  121. // checksum code against itself.
  122. func ipv6PseudoheaderSum(src, dst netip.Addr, nextHeader, length uint32) uint32 {
  123. var csum uint32
  124. s, d := src.AsSlice(), dst.AsSlice()
  125. for i := 0; i < 16; i += 2 {
  126. csum += uint32(s[i])<<8 | uint32(s[i+1])
  127. csum += uint32(d[i])<<8 | uint32(d[i+1])
  128. }
  129. return csum + length + nextHeader
  130. }
  131. func sumBytes(b []byte, csum uint32) uint32 {
  132. for i := 0; i+1 < len(b); i += 2 {
  133. csum += uint32(b[i])<<8 | uint32(b[i+1])
  134. }
  135. if len(b)%2 == 1 {
  136. csum += uint32(b[len(b)-1]) << 8
  137. }
  138. return csum
  139. }
  140. func fold(csum uint32) uint16 {
  141. for csum > 0xffff {
  142. csum = (csum >> 16) + (csum & 0xffff)
  143. }
  144. return uint16(csum)
  145. }
  146. // l4ChecksumValid6 verifies an IPv6 upper-layer checksum the way a receiver
  147. // does: the pseudo-header plus the whole upper-layer segment, checksum field
  148. // included, folds to 0xffff. The next header field is the upper-layer protocol
  149. // only while there are no extension headers, which is all this file builds.
  150. func l4ChecksumValid6(pkt []byte) bool {
  151. src, _ := netip.AddrFromSlice(pkt[8:24])
  152. dst, _ := netip.AddrFromSlice(pkt[24:40])
  153. l4 := pkt[ipv6HeaderLen:]
  154. return fold(sumBytes(l4, ipv6PseudoheaderSum(src, dst, uint32(pkt[6]), uint32(len(l4))))) == 0xffff
  155. }
  156. // l4ChecksumValid4 is the IPv4 counterpart: the RFC 793/768 pseudo-header is
  157. // source, destination, a zero byte, the protocol and the upper-layer length.
  158. func l4ChecksumValid4(pkt []byte) bool {
  159. ihl := int(pkt[0]&0x0f) << 2
  160. l4 := pkt[ihl:]
  161. return fold(sumBytes(l4, sumBytes(pkt[12:20], uint32(pkt[9])+uint32(len(l4))))) == 0xffff
  162. }
  163. // TestConsumeInsidePacketSelfTraffic covers the self-addressed branch of
  164. // consumeInsidePacket, taken where immediatelyForwardToSelf is set (see
  165. // inside_bsd.go): the packet goes straight back to the tun, ahead of the
  166. // firewall and the handshake.
  167. func TestConsumeInsidePacketSelfTraffic(t *testing.T) {
  168. v4 := netip.MustParseAddr("100.100.1.42")
  169. v6 := netip.MustParseAddr("fd00::42")
  170. tests := []struct {
  171. name string
  172. addr netip.Addr
  173. pkt []byte
  174. }{
  175. {"ipv4/tcp", v4, buildIPv4(v4, v4, tcpSyn)},
  176. {"ipv4/udp", v4, buildIPv4(v4, v4, udpDatagram)},
  177. {"ipv4/icmp", v4, buildIPv4(v4, v4, icmpEcho)},
  178. {"ipv6/tcp", v6, buildIPv6(v6, v6, tcpSyn)},
  179. {"ipv6/udp", v6, buildIPv6(v6, v6, udpDatagram)},
  180. {"ipv6/icmpv6", v6, buildIPv6(v6, v6, icmpv6Echo)},
  181. }
  182. for _, tt := range tests {
  183. t.Run(tt.name, func(t *testing.T) {
  184. f, tun := newSelfForwardInterface(tt.addr)
  185. // consumeInsidePacket writes through the slice it is handed, so a
  186. // packet that arrived with a valid checksum must come back out of
  187. // bytes taken before the call, unchanged.
  188. want := append([]byte(nil), tt.pkt...)
  189. consumeInside(f, tt.pkt)
  190. if immediatelyForwardToSelf {
  191. require.Len(t, tun.writes, 1)
  192. assert.Equal(t, want, tun.writes[0])
  193. } else {
  194. assert.Empty(t, tun.writes, "self traffic reaches the tun over loopback here and must be dropped")
  195. }
  196. })
  197. }
  198. }
  199. // TestConsumeInsidePacketSelfTrafficChecksum shows that the self-forward
  200. // returns the bytes it was handed, so a packet that arrived with a wrong
  201. // upper-layer checksum is written back with that same wrong checksum and the
  202. // kernel drops it on re-entry.
  203. //
  204. // This is how a macOS host loses TCP and UDP to its own IPv6 overlay address:
  205. // the kernel writes only the pseudo-header sum into the checksum field and
  206. // defers completion to hardware offload, state that does not survive the
  207. // crossing into userspace. Which kernels do this, for which protocols and IP
  208. // versions, is a property of the kernel and belongs to a test against a live
  209. // one; here the checksum is simply wrong, and the forward must make it right.
  210. func TestConsumeInsidePacketSelfTrafficChecksum(t *testing.T) {
  211. if !immediatelyForwardToSelf {
  212. t.Skip("self traffic never reaches the tun on this platform")
  213. }
  214. versions := []struct {
  215. name string
  216. addr netip.Addr
  217. build func(src, dst netip.Addr, p l4Proto) []byte
  218. l4At int
  219. valid func(pkt []byte) bool
  220. }{
  221. {"v4", netip.MustParseAddr("100.100.1.42"), buildIPv4, ipv4HeaderLen, l4ChecksumValid4},
  222. {"v6", netip.MustParseAddr("fd00::42"), buildIPv6, ipv6HeaderLen, l4ChecksumValid6},
  223. }
  224. for _, v := range versions {
  225. for _, p := range []l4Proto{tcpSyn, udpDatagram} {
  226. t.Run(v.name+"/"+p.name, func(t *testing.T) {
  227. pkt := v.build(v.addr, v.addr, p)
  228. binary.BigEndian.PutUint16(pkt[v.l4At+p.cksumAt:], 0x1234)
  229. require.False(t, v.valid(pkt), "the packet under test must start with a wrong checksum")
  230. f, tun := newSelfForwardInterface(v.addr)
  231. consumeInside(f, pkt)
  232. require.Len(t, tun.writes, 1)
  233. assert.True(t, v.valid(tun.writes[0]),
  234. "a forwarded %s packet must carry a valid checksum, got 0x%04x",
  235. p.name, binary.BigEndian.Uint16(tun.writes[0][v.l4At+p.cksumAt:]))
  236. })
  237. }
  238. }
  239. }