golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv6/readwrite_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ipv6_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"net"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	"golang.org/x/net/internal/iana"
    18  	"golang.org/x/net/ipv6"
    19  	"golang.org/x/net/nettest"
    20  )
    21  
    22  func BenchmarkReadWriteUnicast(b *testing.B) {
    23  	switch runtime.GOOS {
    24  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
    25  		b.Skipf("not supported on %s", runtime.GOOS)
    26  	}
    27  
    28  	c, err := nettest.NewLocalPacketListener("udp6")
    29  	if err != nil {
    30  		b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
    31  	}
    32  	defer c.Close()
    33  
    34  	dst := c.LocalAddr()
    35  	wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
    36  
    37  	b.Run("NetUDP", func(b *testing.B) {
    38  		for i := 0; i < b.N; i++ {
    39  			if _, err := c.WriteTo(wb, dst); err != nil {
    40  				b.Fatal(err)
    41  			}
    42  			if _, _, err := c.ReadFrom(rb); err != nil {
    43  				b.Fatal(err)
    44  			}
    45  		}
    46  	})
    47  	b.Run("IPv6UDP", func(b *testing.B) {
    48  		p := ipv6.NewPacketConn(c)
    49  		cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
    50  		if err := p.SetControlMessage(cf, true); err != nil {
    51  			b.Fatal(err)
    52  		}
    53  		cm := ipv6.ControlMessage{
    54  			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
    55  			HopLimit:     1,
    56  		}
    57  		ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
    58  		if ifi != nil {
    59  			cm.IfIndex = ifi.Index
    60  		}
    61  
    62  		for i := 0; i < b.N; i++ {
    63  			if _, err := p.WriteTo(wb, &cm, dst); err != nil {
    64  				b.Fatal(err)
    65  			}
    66  			if _, _, _, err := p.ReadFrom(rb); err != nil {
    67  				b.Fatal(err)
    68  			}
    69  		}
    70  	})
    71  }
    72  
    73  func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
    74  	switch runtime.GOOS {
    75  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
    76  		b.Skipf("not supported on %s", runtime.GOOS)
    77  	}
    78  
    79  	payload := []byte("HELLO-R-U-THERE")
    80  	iph := []byte{
    81  		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
    82  		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
    83  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    84  		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
    85  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    86  	}
    87  	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
    88  	datagram := append(greh, append(iph, payload...)...)
    89  	bb := make([]byte, 128)
    90  	cm := ipv6.ControlMessage{
    91  		TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
    92  		HopLimit:     1,
    93  		Src:          net.IPv6loopback,
    94  	}
    95  	ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
    96  	if ifi != nil {
    97  		cm.IfIndex = ifi.Index
    98  	}
    99  
   100  	b.Run("UDP", func(b *testing.B) {
   101  		c, err := nettest.NewLocalPacketListener("udp6")
   102  		if err != nil {
   103  			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   104  		}
   105  		defer c.Close()
   106  		p := ipv6.NewPacketConn(c)
   107  		dst := c.LocalAddr()
   108  		cf := ipv6.FlagHopLimit | ipv6.FlagInterface
   109  		if err := p.SetControlMessage(cf, true); err != nil {
   110  			b.Fatal(err)
   111  		}
   112  		wms := []ipv6.Message{
   113  			{
   114  				Buffers: [][]byte{payload},
   115  				Addr:    dst,
   116  				OOB:     cm.Marshal(),
   117  			},
   118  		}
   119  		rms := []ipv6.Message{
   120  			{
   121  				Buffers: [][]byte{bb},
   122  				OOB:     ipv6.NewControlMessage(cf),
   123  			},
   124  		}
   125  		b.Run("Net", func(b *testing.B) {
   126  			for i := 0; i < b.N; i++ {
   127  				if _, err := c.WriteTo(payload, dst); err != nil {
   128  					b.Fatal(err)
   129  				}
   130  				if _, _, err := c.ReadFrom(bb); err != nil {
   131  					b.Fatal(err)
   132  				}
   133  			}
   134  		})
   135  		b.Run("ToFrom", func(b *testing.B) {
   136  			for i := 0; i < b.N; i++ {
   137  				if _, err := p.WriteTo(payload, &cm, dst); err != nil {
   138  					b.Fatal(err)
   139  				}
   140  				if _, _, _, err := p.ReadFrom(bb); err != nil {
   141  					b.Fatal(err)
   142  				}
   143  			}
   144  		})
   145  		b.Run("Batch", func(b *testing.B) {
   146  			for i := 0; i < b.N; i++ {
   147  				if _, err := p.WriteBatch(wms, 0); err != nil {
   148  					b.Fatal(err)
   149  				}
   150  				if _, err := p.ReadBatch(rms, 0); err != nil {
   151  					b.Fatal(err)
   152  				}
   153  			}
   154  		})
   155  	})
   156  	b.Run("IP", func(b *testing.B) {
   157  		switch runtime.GOOS {
   158  		case "netbsd":
   159  			b.Skip("need to configure gre on netbsd")
   160  		case "openbsd":
   161  			b.Skip("net.inet.gre.allow=0 by default on openbsd")
   162  		}
   163  
   164  		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
   165  		if err != nil {
   166  			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   167  		}
   168  		defer c.Close()
   169  		p := ipv6.NewPacketConn(c)
   170  		dst := c.LocalAddr()
   171  		cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
   172  		if err := p.SetControlMessage(cf, true); err != nil {
   173  			b.Fatal(err)
   174  		}
   175  		wms := []ipv6.Message{
   176  			{
   177  				Buffers: [][]byte{datagram},
   178  				Addr:    dst,
   179  				OOB:     cm.Marshal(),
   180  			},
   181  		}
   182  		rms := []ipv6.Message{
   183  			{
   184  				Buffers: [][]byte{bb},
   185  				OOB:     ipv6.NewControlMessage(cf),
   186  			},
   187  		}
   188  		b.Run("Net", func(b *testing.B) {
   189  			for i := 0; i < b.N; i++ {
   190  				if _, err := c.WriteTo(datagram, dst); err != nil {
   191  					b.Fatal(err)
   192  				}
   193  				if _, _, err := c.ReadFrom(bb); err != nil {
   194  					b.Fatal(err)
   195  				}
   196  			}
   197  		})
   198  		b.Run("ToFrom", func(b *testing.B) {
   199  			for i := 0; i < b.N; i++ {
   200  				if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
   201  					b.Fatal(err)
   202  				}
   203  				if _, _, _, err := p.ReadFrom(bb); err != nil {
   204  					b.Fatal(err)
   205  				}
   206  			}
   207  		})
   208  		b.Run("Batch", func(b *testing.B) {
   209  			for i := 0; i < b.N; i++ {
   210  				if _, err := p.WriteBatch(wms, 0); err != nil {
   211  					b.Fatal(err)
   212  				}
   213  				if _, err := p.ReadBatch(rms, 0); err != nil {
   214  					b.Fatal(err)
   215  				}
   216  			}
   217  		})
   218  	})
   219  }
   220  
   221  func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
   222  	switch runtime.GOOS {
   223  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
   224  		t.Skipf("not supported on %s", runtime.GOOS)
   225  	}
   226  	ifi, err := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
   227  	if err != nil {
   228  		t.Skip("ipv6 is not enabled for loopback interface")
   229  	}
   230  	c, err := nettest.NewLocalPacketListener("udp6")
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	defer c.Close()
   235  	p := ipv6.NewPacketConn(c)
   236  	defer p.Close()
   237  
   238  	dst := c.LocalAddr()
   239  	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
   240  	wb := []byte("HELLO-R-U-THERE")
   241  
   242  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   243  		if protocolNotSupported(err) {
   244  			t.Skipf("not supported on %s", runtime.GOOS)
   245  		}
   246  		t.Fatal(err)
   247  	}
   248  
   249  	var firstError sync.Once
   250  	fatalf := func(format string, args ...interface{}) {
   251  		// On the first error, close the PacketConn to unblock the remaining
   252  		// goroutines. Suppress any further errors, which may occur simply due to
   253  		// closing the PacketConn.
   254  		first := false
   255  		firstError.Do(func() {
   256  			first = true
   257  			p.Close()
   258  		})
   259  		if first {
   260  			t.Helper()
   261  			t.Errorf(format, args...)
   262  		}
   263  		runtime.Goexit()
   264  	}
   265  
   266  	var wg sync.WaitGroup
   267  	reader := func() {
   268  		defer wg.Done()
   269  		rb := make([]byte, 128)
   270  		if n, cm, _, err := p.ReadFrom(rb); err != nil {
   271  			fatalf("%v", err)
   272  		} else if !bytes.Equal(rb[:n], wb) {
   273  			fatalf("got %v; want %v", rb[:n], wb)
   274  		} else {
   275  			s := cm.String()
   276  			if strings.Contains(s, ",") {
   277  				t.Errorf("should be space-separated values: %s", s)
   278  			}
   279  		}
   280  	}
   281  	writer := func(toggle bool) {
   282  		defer wg.Done()
   283  		cm := ipv6.ControlMessage{
   284  			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
   285  			Src:          net.IPv6loopback,
   286  		}
   287  		if ifi != nil {
   288  			cm.IfIndex = ifi.Index
   289  		}
   290  		if err := p.SetControlMessage(cf, toggle); err != nil {
   291  			fatalf("%v", err)
   292  		}
   293  
   294  		backoff := time.Millisecond
   295  		for {
   296  			n, err := p.WriteTo(wb, &cm, dst)
   297  			if err != nil {
   298  				if n == 0 && isENOBUFS(err) {
   299  					time.Sleep(backoff)
   300  					backoff *= 2
   301  					continue
   302  				}
   303  				fatalf("%v", err)
   304  			}
   305  			if n != len(wb) {
   306  				fatalf("got %d; want %d", n, len(wb))
   307  			}
   308  			break
   309  		}
   310  	}
   311  
   312  	const N = 10
   313  	wg.Add(N)
   314  	for i := 0; i < N; i++ {
   315  		go reader()
   316  	}
   317  	wg.Add(2 * N)
   318  	for i := 0; i < 2*N; i++ {
   319  		go writer(i%2 != 0)
   320  	}
   321  	wg.Add(N)
   322  	for i := 0; i < N; i++ {
   323  		go reader()
   324  	}
   325  	wg.Wait()
   326  }
   327  
   328  func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
   329  	switch runtime.GOOS {
   330  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
   331  		t.Skipf("not supported on %s", runtime.GOOS)
   332  	}
   333  
   334  	payload := []byte("HELLO-R-U-THERE")
   335  	iph := []byte{
   336  		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
   337  		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
   338  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   339  		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
   340  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   341  	}
   342  	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
   343  	datagram := append(greh, append(iph, payload...)...)
   344  
   345  	t.Run("UDP", func(t *testing.T) {
   346  		c, err := nettest.NewLocalPacketListener("udp6")
   347  		if err != nil {
   348  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   349  		}
   350  		defer c.Close()
   351  		p := ipv6.NewPacketConn(c)
   352  		t.Run("ToFrom", func(t *testing.T) {
   353  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
   354  		})
   355  		t.Run("Batch", func(t *testing.T) {
   356  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
   357  		})
   358  	})
   359  	t.Run("IP", func(t *testing.T) {
   360  		switch runtime.GOOS {
   361  		case "netbsd":
   362  			t.Skip("need to configure gre on netbsd")
   363  		case "openbsd":
   364  			t.Skip("net.inet.gre.allow=0 by default on openbsd")
   365  		}
   366  
   367  		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
   368  		if err != nil {
   369  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   370  		}
   371  		defer c.Close()
   372  		p := ipv6.NewPacketConn(c)
   373  		t.Run("ToFrom", func(t *testing.T) {
   374  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
   375  		})
   376  		t.Run("Batch", func(t *testing.T) {
   377  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
   378  		})
   379  	})
   380  }
   381  
   382  func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr, batch bool) {
   383  	ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
   384  	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
   385  
   386  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   387  		if protocolNotSupported(err) {
   388  			t.Skipf("not supported on %s", runtime.GOOS)
   389  		}
   390  		t.Fatal(err)
   391  	}
   392  
   393  	var firstError sync.Once
   394  	fatalf := func(format string, args ...interface{}) {
   395  		// On the first error, close the PacketConn to unblock the remaining
   396  		// goroutines. Suppress any further errors, which may occur simply due to
   397  		// closing the PacketConn.
   398  		first := false
   399  		firstError.Do(func() {
   400  			first = true
   401  			p.Close()
   402  		})
   403  		if first {
   404  			t.Helper()
   405  			t.Errorf(format, args...)
   406  		}
   407  		runtime.Goexit()
   408  	}
   409  
   410  	var wg sync.WaitGroup
   411  	reader := func() {
   412  		defer wg.Done()
   413  		b := make([]byte, 128)
   414  		n, cm, _, err := p.ReadFrom(b)
   415  		if err != nil {
   416  			fatalf("%v", err)
   417  		}
   418  		if !bytes.Equal(b[:n], data) {
   419  			fatalf("got %#v; want %#v", b[:n], data)
   420  		}
   421  		s := cm.String()
   422  		if strings.Contains(s, ",") {
   423  			fatalf("should be space-separated values: %s", s)
   424  		}
   425  	}
   426  	batchReader := func() {
   427  		defer wg.Done()
   428  		ms := []ipv6.Message{
   429  			{
   430  				Buffers: [][]byte{make([]byte, 128)},
   431  				OOB:     ipv6.NewControlMessage(cf),
   432  			},
   433  		}
   434  		n, err := p.ReadBatch(ms, 0)
   435  		if err != nil {
   436  			fatalf("%v", err)
   437  		}
   438  		if n != len(ms) {
   439  			fatalf("got %d; want %d", n, len(ms))
   440  		}
   441  		var cm ipv6.ControlMessage
   442  		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
   443  			fatalf("%v", err)
   444  		}
   445  		b := ms[0].Buffers[0][:ms[0].N]
   446  		if !bytes.Equal(b, data) {
   447  			fatalf("got %#v; want %#v", b, data)
   448  		}
   449  		s := cm.String()
   450  		if strings.Contains(s, ",") {
   451  			fatalf("should be space-separated values: %s", s)
   452  		}
   453  	}
   454  	writer := func(toggle bool) {
   455  		defer wg.Done()
   456  		cm := ipv6.ControlMessage{
   457  			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
   458  			HopLimit:     1,
   459  			Src:          net.IPv6loopback,
   460  		}
   461  		if ifi != nil {
   462  			cm.IfIndex = ifi.Index
   463  		}
   464  		if err := p.SetControlMessage(cf, toggle); err != nil {
   465  			fatalf("%v", err)
   466  		}
   467  
   468  		backoff := time.Millisecond
   469  		for {
   470  			n, err := p.WriteTo(data, &cm, dst)
   471  			if err != nil {
   472  				if n == 0 && isENOBUFS(err) {
   473  					time.Sleep(backoff)
   474  					backoff *= 2
   475  					continue
   476  				}
   477  				fatalf("%v", err)
   478  			}
   479  			if n != len(data) {
   480  				fatalf("got %d; want %d", n, len(data))
   481  			}
   482  			break
   483  		}
   484  	}
   485  	batchWriter := func(toggle bool) {
   486  		defer wg.Done()
   487  		cm := ipv6.ControlMessage{
   488  			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
   489  			HopLimit:     1,
   490  			Src:          net.IPv6loopback,
   491  		}
   492  		if ifi != nil {
   493  			cm.IfIndex = ifi.Index
   494  		}
   495  		if err := p.SetControlMessage(cf, toggle); err != nil {
   496  			fatalf("%v", err)
   497  		}
   498  		ms := []ipv6.Message{
   499  			{
   500  				Buffers: [][]byte{data},
   501  				OOB:     cm.Marshal(),
   502  				Addr:    dst,
   503  			},
   504  		}
   505  
   506  		backoff := time.Millisecond
   507  		for {
   508  			n, err := p.WriteBatch(ms, 0)
   509  			if err != nil {
   510  				if n == 0 && isENOBUFS(err) {
   511  					time.Sleep(backoff)
   512  					backoff *= 2
   513  					continue
   514  				}
   515  				fatalf("%v", err)
   516  			}
   517  			if n != len(ms) {
   518  				fatalf("got %d; want %d", n, len(ms))
   519  			}
   520  			if ms[0].N != len(data) {
   521  				fatalf("got %d; want %d", ms[0].N, len(data))
   522  			}
   523  			break
   524  		}
   525  	}
   526  
   527  	const N = 10
   528  	wg.Add(N)
   529  	for i := 0; i < N; i++ {
   530  		if batch {
   531  			go batchReader()
   532  		} else {
   533  			go reader()
   534  		}
   535  	}
   536  	wg.Add(2 * N)
   537  	for i := 0; i < 2*N; i++ {
   538  		if batch {
   539  			go batchWriter(i%2 != 0)
   540  		} else {
   541  			go writer(i%2 != 0)
   542  		}
   543  	}
   544  	wg.Add(N)
   545  	for i := 0; i < N; i++ {
   546  		if batch {
   547  			go batchReader()
   548  		} else {
   549  			go reader()
   550  		}
   551  	}
   552  	wg.Wait()
   553  }