github.com/tumi8/quic-go@v0.37.4-tum/sys_conn_oob_test.go (about)

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