1// +build !windows
2
3package quic
4
5import (
6	"fmt"
7	"net"
8	"time"
9
10	"golang.org/x/net/ipv4"
11	"golang.org/x/sys/unix"
12
13	"github.com/golang/mock/gomock"
14	"github.com/lucas-clemente/quic-go/internal/protocol"
15	"github.com/lucas-clemente/quic-go/internal/utils"
16
17	. "github.com/onsi/ginkgo"
18	. "github.com/onsi/gomega"
19)
20
21var _ = Describe("OOB Conn Test", func() {
22	runServer := func(network, address string) (*net.UDPConn, <-chan *receivedPacket) {
23		addr, err := net.ResolveUDPAddr(network, address)
24		Expect(err).ToNot(HaveOccurred())
25		udpConn, err := net.ListenUDP(network, addr)
26		Expect(err).ToNot(HaveOccurred())
27		oobConn, err := newConn(udpConn)
28		Expect(err).ToNot(HaveOccurred())
29
30		packetChan := make(chan *receivedPacket)
31		go func() {
32			defer GinkgoRecover()
33			for {
34				p, err := oobConn.ReadPacket()
35				if err != nil {
36					return
37				}
38				packetChan <- p
39			}
40		}()
41
42		return udpConn, packetChan
43	}
44
45	Context("ECN conn", func() {
46		sendPacketWithECN := func(network string, addr *net.UDPAddr, setECN func(uintptr)) net.Addr {
47			conn, err := net.DialUDP(network, nil, addr)
48			ExpectWithOffset(1, err).ToNot(HaveOccurred())
49			rawConn, err := conn.SyscallConn()
50			ExpectWithOffset(1, err).ToNot(HaveOccurred())
51			ExpectWithOffset(1, rawConn.Control(func(fd uintptr) {
52				setECN(fd)
53			})).To(Succeed())
54			_, err = conn.Write([]byte("foobar"))
55			ExpectWithOffset(1, err).ToNot(HaveOccurred())
56			return conn.LocalAddr()
57		}
58
59		It("reads ECN flags on IPv4", func() {
60			conn, packetChan := runServer("udp4", "localhost:0")
61			defer conn.Close()
62
63			sentFrom := sendPacketWithECN(
64				"udp4",
65				conn.LocalAddr().(*net.UDPAddr),
66				func(fd uintptr) {
67					Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 2)).To(Succeed())
68				},
69			)
70
71			var p *receivedPacket
72			Eventually(packetChan).Should(Receive(&p))
73			Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
74			Expect(p.data).To(Equal([]byte("foobar")))
75			Expect(p.remoteAddr).To(Equal(sentFrom))
76			Expect(p.ecn).To(Equal(protocol.ECT0))
77		})
78
79		It("reads ECN flags on IPv6", func() {
80			conn, packetChan := runServer("udp6", "[::]:0")
81			defer conn.Close()
82
83			sentFrom := sendPacketWithECN(
84				"udp6",
85				conn.LocalAddr().(*net.UDPAddr),
86				func(fd uintptr) {
87					Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 3)).To(Succeed())
88				},
89			)
90
91			var p *receivedPacket
92			Eventually(packetChan).Should(Receive(&p))
93			Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
94			Expect(p.data).To(Equal([]byte("foobar")))
95			Expect(p.remoteAddr).To(Equal(sentFrom))
96			Expect(p.ecn).To(Equal(protocol.ECNCE))
97		})
98
99		It("reads ECN flags on a connection that supports both IPv4 and IPv6", func() {
100			conn, packetChan := runServer("udp", "0.0.0.0:0")
101			defer conn.Close()
102			port := conn.LocalAddr().(*net.UDPAddr).Port
103
104			// IPv4
105			sendPacketWithECN(
106				"udp4",
107				&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port},
108				func(fd uintptr) {
109					Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 3)).To(Succeed())
110				},
111			)
112
113			var p *receivedPacket
114			Eventually(packetChan).Should(Receive(&p))
115			Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue())
116			Expect(p.ecn).To(Equal(protocol.ECNCE))
117
118			// IPv6
119			sendPacketWithECN(
120				"udp6",
121				&net.UDPAddr{IP: net.IPv6loopback, Port: port},
122				func(fd uintptr) {
123					Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 1)).To(Succeed())
124				},
125			)
126
127			Eventually(packetChan).Should(Receive(&p))
128			Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse())
129			Expect(p.ecn).To(Equal(protocol.ECT1))
130		})
131	})
132
133	Context("Packet Info conn", func() {
134		sendPacket := func(network string, addr *net.UDPAddr) net.Addr {
135			conn, err := net.DialUDP(network, nil, addr)
136			ExpectWithOffset(1, err).ToNot(HaveOccurred())
137			_, err = conn.Write([]byte("foobar"))
138			ExpectWithOffset(1, err).ToNot(HaveOccurred())
139			return conn.LocalAddr()
140		}
141
142		It("reads packet info on IPv4", func() {
143			conn, packetChan := runServer("udp4", ":0")
144			defer conn.Close()
145
146			addr := conn.LocalAddr().(*net.UDPAddr)
147			ip := net.ParseIP("127.0.0.1").To4()
148			addr.IP = ip
149			sentFrom := sendPacket("udp4", addr)
150
151			var p *receivedPacket
152			Eventually(packetChan).Should(Receive(&p))
153			Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
154			Expect(p.data).To(Equal([]byte("foobar")))
155			Expect(p.remoteAddr).To(Equal(sentFrom))
156			Expect(p.info).To(Not(BeNil()))
157			Expect(p.info.addr.To4()).To(Equal(ip))
158		})
159
160		It("reads packet info on IPv6", func() {
161			conn, packetChan := runServer("udp6", ":0")
162			defer conn.Close()
163
164			addr := conn.LocalAddr().(*net.UDPAddr)
165			ip := net.ParseIP("::1")
166			addr.IP = ip
167			sentFrom := sendPacket("udp6", addr)
168
169			var p *receivedPacket
170			Eventually(packetChan).Should(Receive(&p))
171			Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
172			Expect(p.data).To(Equal([]byte("foobar")))
173			Expect(p.remoteAddr).To(Equal(sentFrom))
174			Expect(p.info).To(Not(BeNil()))
175			Expect(p.info.addr).To(Equal(ip))
176		})
177
178		It("reads packet info on a connection that supports both IPv4 and IPv6", func() {
179			conn, packetChan := runServer("udp", ":0")
180			defer conn.Close()
181			port := conn.LocalAddr().(*net.UDPAddr).Port
182
183			// IPv4
184			ip4 := net.ParseIP("127.0.0.1").To4()
185			sendPacket("udp4", &net.UDPAddr{IP: ip4, Port: port})
186
187			var p *receivedPacket
188			Eventually(packetChan).Should(Receive(&p))
189			Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue())
190			Expect(p.info).To(Not(BeNil()))
191			Expect(p.info.addr.To4()).To(Equal(ip4))
192
193			// IPv6
194			ip6 := net.ParseIP("::1")
195			sendPacket("udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: port})
196
197			Eventually(packetChan).Should(Receive(&p))
198			Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse())
199			Expect(p.info).To(Not(BeNil()))
200			Expect(p.info.addr).To(Equal(ip6))
201		})
202	})
203
204	Context("Batch Reading", func() {
205		var batchConn *MockBatchConn
206
207		BeforeEach(func() {
208			batchConn = NewMockBatchConn(mockCtrl)
209		})
210
211		It("reads multiple messages in one batch", func() {
212			const numMsgRead = batchSize/2 + 1
213			var counter int
214			batchConn.EXPECT().ReadBatch(gomock.Any(), gomock.Any()).DoAndReturn(func(ms []ipv4.Message, flags int) (int, error) {
215				Expect(ms).To(HaveLen(batchSize))
216				for i := 0; i < numMsgRead; i++ {
217					Expect(ms[i].Buffers).To(HaveLen(1))
218					Expect(ms[i].Buffers[0]).To(HaveLen(int(protocol.MaxPacketBufferSize)))
219					data := []byte(fmt.Sprintf("message %d", counter))
220					counter++
221					ms[i].Buffers[0] = data
222					ms[i].N = len(data)
223				}
224				return numMsgRead, nil
225			}).Times(2)
226
227			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
228			Expect(err).ToNot(HaveOccurred())
229			udpConn, err := net.ListenUDP("udp", addr)
230			Expect(err).ToNot(HaveOccurred())
231			oobConn, err := newConn(udpConn)
232			Expect(err).ToNot(HaveOccurred())
233			oobConn.batchConn = batchConn
234
235			for i := 0; i < batchSize+1; i++ {
236				p, err := oobConn.ReadPacket()
237				Expect(err).ToNot(HaveOccurred())
238				Expect(string(p.data)).To(Equal(fmt.Sprintf("message %d", i)))
239			}
240		})
241	})
242})
243