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

     1  // Copyright 2012 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 ipv4_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/ipv4"
    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("udp4")
    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("IPv4UDP", func(b *testing.B) {
    48  		p := ipv4.NewPacketConn(c)
    49  		cf := ipv4.FlagTTL | ipv4.FlagInterface
    50  		if err := p.SetControlMessage(cf, true); err != nil {
    51  			b.Fatal(err)
    52  		}
    53  		cm := ipv4.ControlMessage{TTL: 1}
    54  		ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
    55  		if ifi != nil {
    56  			cm.IfIndex = ifi.Index
    57  		}
    58  
    59  		for i := 0; i < b.N; i++ {
    60  			if _, err := p.WriteTo(wb, &cm, dst); err != nil {
    61  				b.Fatal(err)
    62  			}
    63  			if _, _, _, err := p.ReadFrom(rb); err != nil {
    64  				b.Fatal(err)
    65  			}
    66  		}
    67  	})
    68  }
    69  
    70  func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
    71  	switch runtime.GOOS {
    72  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
    73  		b.Skipf("not supported on %s", runtime.GOOS)
    74  	}
    75  
    76  	payload := []byte("HELLO-R-U-THERE")
    77  	iph, err := (&ipv4.Header{
    78  		Version:  ipv4.Version,
    79  		Len:      ipv4.HeaderLen,
    80  		TotalLen: ipv4.HeaderLen + len(payload),
    81  		TTL:      1,
    82  		Protocol: iana.ProtocolReserved,
    83  		Src:      net.IPv4(192, 0, 2, 1),
    84  		Dst:      net.IPv4(192, 0, 2, 254),
    85  	}).Marshal()
    86  	if err != nil {
    87  		b.Fatal(err)
    88  	}
    89  	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
    90  	datagram := append(greh, append(iph, payload...)...)
    91  	bb := make([]byte, 128)
    92  	cm := ipv4.ControlMessage{
    93  		Src: net.IPv4(127, 0, 0, 1),
    94  	}
    95  	ifi, _ := nettest.RoutedInterface("ip4", 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("udp4")
   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 := ipv4.NewPacketConn(c)
   107  		dst := c.LocalAddr()
   108  		cf := ipv4.FlagTTL | ipv4.FlagInterface
   109  		if err := p.SetControlMessage(cf, true); err != nil {
   110  			b.Fatal(err)
   111  		}
   112  		wms := []ipv4.Message{
   113  			{
   114  				Buffers: [][]byte{payload},
   115  				Addr:    dst,
   116  				OOB:     cm.Marshal(),
   117  			},
   118  		}
   119  		rms := []ipv4.Message{
   120  			{
   121  				Buffers: [][]byte{bb},
   122  				OOB:     ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.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 := ipv4.NewPacketConn(c)
   170  		dst := c.LocalAddr()
   171  		cf := ipv4.FlagTTL | ipv4.FlagInterface
   172  		if err := p.SetControlMessage(cf, true); err != nil {
   173  			b.Fatal(err)
   174  		}
   175  		wms := []ipv4.Message{
   176  			{
   177  				Buffers: [][]byte{datagram},
   178  				Addr:    dst,
   179  				OOB:     cm.Marshal(),
   180  			},
   181  		}
   182  		rms := []ipv4.Message{
   183  			{
   184  				Buffers: [][]byte{bb},
   185  				OOB:     ipv4.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  
   227  	c, err := nettest.NewLocalPacketListener("udp4")
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	defer c.Close()
   232  	p := ipv4.NewPacketConn(c)
   233  	defer p.Close()
   234  
   235  	dst := c.LocalAddr()
   236  	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
   237  	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
   238  	wb := []byte("HELLO-R-U-THERE")
   239  
   240  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   241  		if protocolNotSupported(err) {
   242  			t.Skipf("not supported on %s", runtime.GOOS)
   243  		}
   244  		t.Fatal(err)
   245  	}
   246  
   247  	var firstError sync.Once
   248  	fatalf := func(format string, args ...interface{}) {
   249  		// On the first error, close the PacketConn to unblock the remaining
   250  		// goroutines. Suppress any further errors, which may occur simply due to
   251  		// closing the PacketConn.
   252  		first := false
   253  		firstError.Do(func() {
   254  			first = true
   255  			p.Close()
   256  		})
   257  		if first {
   258  			t.Helper()
   259  			t.Errorf(format, args...)
   260  		}
   261  		runtime.Goexit()
   262  	}
   263  
   264  	var wg sync.WaitGroup
   265  	reader := func() {
   266  		defer wg.Done()
   267  		rb := make([]byte, 128)
   268  		if n, cm, _, err := p.ReadFrom(rb); err != nil {
   269  			fatalf("%v", err)
   270  		} else if !bytes.Equal(rb[:n], wb) {
   271  			fatalf("got %v; want %v", rb[:n], wb)
   272  		} else {
   273  			s := cm.String()
   274  			if strings.Contains(s, ",") {
   275  				t.Errorf("should be space-separated values: %s", s)
   276  			}
   277  		}
   278  	}
   279  	writer := func(toggle bool) {
   280  		defer wg.Done()
   281  		cm := ipv4.ControlMessage{
   282  			Src: net.IPv4(127, 0, 0, 1),
   283  		}
   284  		if ifi != nil {
   285  			cm.IfIndex = ifi.Index
   286  		}
   287  		if err := p.SetControlMessage(cf, toggle); err != nil {
   288  			fatalf("%v", err)
   289  		}
   290  
   291  		backoff := time.Millisecond
   292  		for {
   293  			n, err := p.WriteTo(wb, &cm, dst)
   294  			if err != nil {
   295  				if n == 0 && isENOBUFS(err) {
   296  					time.Sleep(backoff)
   297  					backoff *= 2
   298  					continue
   299  				}
   300  				fatalf("%v", err)
   301  			}
   302  			if n != len(wb) {
   303  				fatalf("got %d; want %d", n, len(wb))
   304  			}
   305  			break
   306  		}
   307  	}
   308  
   309  	const N = 10
   310  	wg.Add(N)
   311  	for i := 0; i < N; i++ {
   312  		go reader()
   313  	}
   314  	wg.Add(2 * N)
   315  	for i := 0; i < 2*N; i++ {
   316  		go writer(i%2 != 0)
   317  	}
   318  	wg.Add(N)
   319  	for i := 0; i < N; i++ {
   320  		go reader()
   321  	}
   322  	wg.Wait()
   323  }
   324  
   325  func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
   326  	switch runtime.GOOS {
   327  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
   328  		t.Skipf("not supported on %s", runtime.GOOS)
   329  	}
   330  
   331  	payload := []byte("HELLO-R-U-THERE")
   332  	iph, err := (&ipv4.Header{
   333  		Version:  ipv4.Version,
   334  		Len:      ipv4.HeaderLen,
   335  		TotalLen: ipv4.HeaderLen + len(payload),
   336  		TTL:      1,
   337  		Protocol: iana.ProtocolReserved,
   338  		Src:      net.IPv4(192, 0, 2, 1),
   339  		Dst:      net.IPv4(192, 0, 2, 254),
   340  	}).Marshal()
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
   345  	datagram := append(greh, append(iph, payload...)...)
   346  
   347  	t.Run("UDP", func(t *testing.T) {
   348  		c, err := nettest.NewLocalPacketListener("udp4")
   349  		if err != nil {
   350  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   351  		}
   352  		defer c.Close()
   353  		p := ipv4.NewPacketConn(c)
   354  		t.Run("ToFrom", func(t *testing.T) {
   355  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
   356  		})
   357  		t.Run("Batch", func(t *testing.T) {
   358  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
   359  		})
   360  	})
   361  	t.Run("IP", func(t *testing.T) {
   362  		switch runtime.GOOS {
   363  		case "netbsd":
   364  			t.Skip("need to configure gre on netbsd")
   365  		case "openbsd":
   366  			t.Skip("net.inet.gre.allow=0 by default on openbsd")
   367  		}
   368  
   369  		c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
   370  		if err != nil {
   371  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   372  		}
   373  		defer c.Close()
   374  		p := ipv4.NewPacketConn(c)
   375  		t.Run("ToFrom", func(t *testing.T) {
   376  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
   377  		})
   378  		t.Run("Batch", func(t *testing.T) {
   379  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
   380  		})
   381  	})
   382  }
   383  
   384  func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) {
   385  	t.Helper()
   386  
   387  	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
   388  	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
   389  
   390  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   391  		if protocolNotSupported(err) {
   392  			t.Skipf("not supported on %s", runtime.GOOS)
   393  		}
   394  		t.Fatal(err)
   395  	}
   396  
   397  	var firstError sync.Once
   398  	fatalf := func(format string, args ...interface{}) {
   399  		// On the first error, close the PacketConn to unblock the remaining
   400  		// goroutines. Suppress any further errors, which may occur simply due to
   401  		// closing the PacketConn.
   402  		first := false
   403  		firstError.Do(func() {
   404  			first = true
   405  			p.Close()
   406  		})
   407  		if first {
   408  			t.Helper()
   409  			t.Errorf(format, args...)
   410  		}
   411  		runtime.Goexit()
   412  	}
   413  
   414  	var wg sync.WaitGroup
   415  	reader := func() {
   416  		defer wg.Done()
   417  		b := make([]byte, 128)
   418  		n, cm, _, err := p.ReadFrom(b)
   419  		if err != nil {
   420  			fatalf("%v", err)
   421  		}
   422  		if !bytes.Equal(b[:n], data) {
   423  			fatalf("got %#v; want %#v", b[:n], data)
   424  		}
   425  		s := cm.String()
   426  		if strings.Contains(s, ",") {
   427  			fatalf("should be space-separated values: %s", s)
   428  		}
   429  	}
   430  	batchReader := func() {
   431  		defer wg.Done()
   432  		ms := []ipv4.Message{
   433  			{
   434  				Buffers: [][]byte{make([]byte, 128)},
   435  				OOB:     ipv4.NewControlMessage(cf),
   436  			},
   437  		}
   438  		n, err := p.ReadBatch(ms, 0)
   439  		if err != nil {
   440  			fatalf("%v", err)
   441  		}
   442  		if n != len(ms) {
   443  			fatalf("got %d; want %d", n, len(ms))
   444  		}
   445  		var cm ipv4.ControlMessage
   446  		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
   447  			fatalf("%v", err)
   448  		}
   449  		var b []byte
   450  		if _, ok := dst.(*net.IPAddr); ok {
   451  			var h ipv4.Header
   452  			if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil {
   453  				fatalf("%v", err)
   454  			}
   455  			b = ms[0].Buffers[0][h.Len:ms[0].N]
   456  		} else {
   457  			b = ms[0].Buffers[0][:ms[0].N]
   458  		}
   459  		if !bytes.Equal(b, data) {
   460  			fatalf("got %#v; want %#v", b, data)
   461  		}
   462  		s := cm.String()
   463  		if strings.Contains(s, ",") {
   464  			fatalf("should be space-separated values: %s", s)
   465  		}
   466  	}
   467  	writer := func(toggle bool) {
   468  		defer wg.Done()
   469  		cm := ipv4.ControlMessage{
   470  			Src: net.IPv4(127, 0, 0, 1),
   471  		}
   472  		if ifi != nil {
   473  			cm.IfIndex = ifi.Index
   474  		}
   475  		if err := p.SetControlMessage(cf, toggle); err != nil {
   476  			fatalf("%v", err)
   477  		}
   478  
   479  		backoff := time.Millisecond
   480  		for {
   481  			n, err := p.WriteTo(data, &cm, dst)
   482  			if err != nil {
   483  				if n == 0 && isENOBUFS(err) {
   484  					time.Sleep(backoff)
   485  					backoff *= 2
   486  					continue
   487  				}
   488  				fatalf("%v", err)
   489  			}
   490  			if n != len(data) {
   491  				fatalf("got %d; want %d", n, len(data))
   492  			}
   493  			break
   494  		}
   495  	}
   496  	batchWriter := func(toggle bool) {
   497  		defer wg.Done()
   498  		cm := ipv4.ControlMessage{
   499  			Src: net.IPv4(127, 0, 0, 1),
   500  		}
   501  		if ifi != nil {
   502  			cm.IfIndex = ifi.Index
   503  		}
   504  		if err := p.SetControlMessage(cf, toggle); err != nil {
   505  			fatalf("%v", err)
   506  		}
   507  		ms := []ipv4.Message{
   508  			{
   509  				Buffers: [][]byte{data},
   510  				OOB:     cm.Marshal(),
   511  				Addr:    dst,
   512  			},
   513  		}
   514  
   515  		backoff := time.Millisecond
   516  		for {
   517  			n, err := p.WriteBatch(ms, 0)
   518  			if err != nil {
   519  				if n == 0 && isENOBUFS(err) {
   520  					time.Sleep(backoff)
   521  					backoff *= 2
   522  					continue
   523  				}
   524  				fatalf("%v", err)
   525  			}
   526  			if n != len(ms) {
   527  				fatalf("got %d; want %d", n, len(ms))
   528  			}
   529  			if ms[0].N != len(data) {
   530  				fatalf("got %d; want %d", ms[0].N, len(data))
   531  			}
   532  			break
   533  		}
   534  	}
   535  
   536  	const N = 10
   537  	wg.Add(N)
   538  	for i := 0; i < N; i++ {
   539  		if batch {
   540  			go batchReader()
   541  		} else {
   542  			go reader()
   543  		}
   544  	}
   545  	wg.Add(2 * N)
   546  	for i := 0; i < 2*N; i++ {
   547  		if batch {
   548  			go batchWriter(i%2 != 0)
   549  		} else {
   550  			go writer(i%2 != 0)
   551  		}
   552  
   553  	}
   554  	wg.Add(N)
   555  	for i := 0; i < N; i++ {
   556  		if batch {
   557  			go batchReader()
   558  		} else {
   559  			go reader()
   560  		}
   561  	}
   562  	wg.Wait()
   563  }