github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/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  
    16  	"github.com/Andyfoo/golang/x/net/internal/iana"
    17  	"github.com/Andyfoo/golang/x/net/ipv4"
    18  	"github.com/Andyfoo/golang/x/net/nettest"
    19  )
    20  
    21  func BenchmarkReadWriteUnicast(b *testing.B) {
    22  	switch runtime.GOOS {
    23  	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
    24  		b.Skipf("not supported on %s", runtime.GOOS)
    25  	}
    26  
    27  	c, err := nettest.NewLocalPacketListener("udp4")
    28  	if err != nil {
    29  		b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
    30  	}
    31  	defer c.Close()
    32  
    33  	dst := c.LocalAddr()
    34  	wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
    35  
    36  	b.Run("NetUDP", func(b *testing.B) {
    37  		for i := 0; i < b.N; i++ {
    38  			if _, err := c.WriteTo(wb, dst); err != nil {
    39  				b.Fatal(err)
    40  			}
    41  			if _, _, err := c.ReadFrom(rb); err != nil {
    42  				b.Fatal(err)
    43  			}
    44  		}
    45  	})
    46  	b.Run("IPv4UDP", func(b *testing.B) {
    47  		p := ipv4.NewPacketConn(c)
    48  		cf := ipv4.FlagTTL | ipv4.FlagInterface
    49  		if err := p.SetControlMessage(cf, true); err != nil {
    50  			b.Fatal(err)
    51  		}
    52  		cm := ipv4.ControlMessage{TTL: 1}
    53  		ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
    54  		if ifi != nil {
    55  			cm.IfIndex = ifi.Index
    56  		}
    57  
    58  		for i := 0; i < b.N; i++ {
    59  			if _, err := p.WriteTo(wb, &cm, dst); err != nil {
    60  				b.Fatal(err)
    61  			}
    62  			if _, _, _, err := p.ReadFrom(rb); err != nil {
    63  				b.Fatal(err)
    64  			}
    65  		}
    66  	})
    67  }
    68  
    69  func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
    70  	switch runtime.GOOS {
    71  	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
    72  		b.Skipf("not supported on %s", runtime.GOOS)
    73  	}
    74  
    75  	payload := []byte("HELLO-R-U-THERE")
    76  	iph, err := (&ipv4.Header{
    77  		Version:  ipv4.Version,
    78  		Len:      ipv4.HeaderLen,
    79  		TotalLen: ipv4.HeaderLen + len(payload),
    80  		TTL:      1,
    81  		Protocol: iana.ProtocolReserved,
    82  		Src:      net.IPv4(192, 0, 2, 1),
    83  		Dst:      net.IPv4(192, 0, 2, 254),
    84  	}).Marshal()
    85  	if err != nil {
    86  		b.Fatal(err)
    87  	}
    88  	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
    89  	datagram := append(greh, append(iph, payload...)...)
    90  	bb := make([]byte, 128)
    91  	cm := ipv4.ControlMessage{
    92  		Src: net.IPv4(127, 0, 0, 1),
    93  	}
    94  	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
    95  	if ifi != nil {
    96  		cm.IfIndex = ifi.Index
    97  	}
    98  
    99  	b.Run("UDP", func(b *testing.B) {
   100  		c, err := nettest.NewLocalPacketListener("udp4")
   101  		if err != nil {
   102  			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   103  		}
   104  		defer c.Close()
   105  		p := ipv4.NewPacketConn(c)
   106  		dst := c.LocalAddr()
   107  		cf := ipv4.FlagTTL | ipv4.FlagInterface
   108  		if err := p.SetControlMessage(cf, true); err != nil {
   109  			b.Fatal(err)
   110  		}
   111  		wms := []ipv4.Message{
   112  			{
   113  				Buffers: [][]byte{payload},
   114  				Addr:    dst,
   115  				OOB:     cm.Marshal(),
   116  			},
   117  		}
   118  		rms := []ipv4.Message{
   119  			{
   120  				Buffers: [][]byte{bb},
   121  				OOB:     ipv4.NewControlMessage(cf),
   122  			},
   123  		}
   124  		b.Run("Net", func(b *testing.B) {
   125  			for i := 0; i < b.N; i++ {
   126  				if _, err := c.WriteTo(payload, dst); err != nil {
   127  					b.Fatal(err)
   128  				}
   129  				if _, _, err := c.ReadFrom(bb); err != nil {
   130  					b.Fatal(err)
   131  				}
   132  			}
   133  		})
   134  		b.Run("ToFrom", func(b *testing.B) {
   135  			for i := 0; i < b.N; i++ {
   136  				if _, err := p.WriteTo(payload, &cm, dst); err != nil {
   137  					b.Fatal(err)
   138  				}
   139  				if _, _, _, err := p.ReadFrom(bb); err != nil {
   140  					b.Fatal(err)
   141  				}
   142  			}
   143  		})
   144  		b.Run("Batch", func(b *testing.B) {
   145  			for i := 0; i < b.N; i++ {
   146  				if _, err := p.WriteBatch(wms, 0); err != nil {
   147  					b.Fatal(err)
   148  				}
   149  				if _, err := p.ReadBatch(rms, 0); err != nil {
   150  					b.Fatal(err)
   151  				}
   152  			}
   153  		})
   154  	})
   155  	b.Run("IP", func(b *testing.B) {
   156  		switch runtime.GOOS {
   157  		case "netbsd":
   158  			b.Skip("need to configure gre on netbsd")
   159  		case "openbsd":
   160  			b.Skip("net.inet.gre.allow=0 by default on openbsd")
   161  		}
   162  
   163  		c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
   164  		if err != nil {
   165  			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   166  		}
   167  		defer c.Close()
   168  		p := ipv4.NewPacketConn(c)
   169  		dst := c.LocalAddr()
   170  		cf := ipv4.FlagTTL | ipv4.FlagInterface
   171  		if err := p.SetControlMessage(cf, true); err != nil {
   172  			b.Fatal(err)
   173  		}
   174  		wms := []ipv4.Message{
   175  			{
   176  				Buffers: [][]byte{datagram},
   177  				Addr:    dst,
   178  				OOB:     cm.Marshal(),
   179  			},
   180  		}
   181  		rms := []ipv4.Message{
   182  			{
   183  				Buffers: [][]byte{bb},
   184  				OOB:     ipv4.NewControlMessage(cf),
   185  			},
   186  		}
   187  		b.Run("Net", func(b *testing.B) {
   188  			for i := 0; i < b.N; i++ {
   189  				if _, err := c.WriteTo(datagram, dst); err != nil {
   190  					b.Fatal(err)
   191  				}
   192  				if _, _, err := c.ReadFrom(bb); err != nil {
   193  					b.Fatal(err)
   194  				}
   195  			}
   196  		})
   197  		b.Run("ToFrom", func(b *testing.B) {
   198  			for i := 0; i < b.N; i++ {
   199  				if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
   200  					b.Fatal(err)
   201  				}
   202  				if _, _, _, err := p.ReadFrom(bb); err != nil {
   203  					b.Fatal(err)
   204  				}
   205  			}
   206  		})
   207  		b.Run("Batch", func(b *testing.B) {
   208  			for i := 0; i < b.N; i++ {
   209  				if _, err := p.WriteBatch(wms, 0); err != nil {
   210  					b.Fatal(err)
   211  				}
   212  				if _, err := p.ReadBatch(rms, 0); err != nil {
   213  					b.Fatal(err)
   214  				}
   215  			}
   216  		})
   217  	})
   218  }
   219  
   220  func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
   221  	switch runtime.GOOS {
   222  	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
   223  		t.Skipf("not supported on %s", runtime.GOOS)
   224  	}
   225  
   226  	c, err := nettest.NewLocalPacketListener("udp4")
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	defer c.Close()
   231  	p := ipv4.NewPacketConn(c)
   232  	defer p.Close()
   233  
   234  	dst := c.LocalAddr()
   235  	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
   236  	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
   237  	wb := []byte("HELLO-R-U-THERE")
   238  
   239  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   240  		if protocolNotSupported(err) {
   241  			t.Skipf("not supported on %s", runtime.GOOS)
   242  		}
   243  		t.Fatal(err)
   244  	}
   245  
   246  	var wg sync.WaitGroup
   247  	reader := func() {
   248  		defer wg.Done()
   249  		rb := make([]byte, 128)
   250  		if n, cm, _, err := p.ReadFrom(rb); err != nil {
   251  			t.Error(err)
   252  			return
   253  		} else if !bytes.Equal(rb[:n], wb) {
   254  			t.Errorf("got %v; want %v", rb[:n], wb)
   255  			return
   256  		} else {
   257  			s := cm.String()
   258  			if strings.Contains(s, ",") {
   259  				t.Errorf("should be space-separated values: %s", s)
   260  			}
   261  		}
   262  	}
   263  	writer := func(toggle bool) {
   264  		defer wg.Done()
   265  		cm := ipv4.ControlMessage{
   266  			Src: net.IPv4(127, 0, 0, 1),
   267  		}
   268  		if ifi != nil {
   269  			cm.IfIndex = ifi.Index
   270  		}
   271  		if err := p.SetControlMessage(cf, toggle); err != nil {
   272  			t.Error(err)
   273  			return
   274  		}
   275  		if n, err := p.WriteTo(wb, &cm, dst); err != nil {
   276  			t.Error(err)
   277  			return
   278  		} else if n != len(wb) {
   279  			t.Errorf("got %d; want %d", n, len(wb))
   280  			return
   281  		}
   282  	}
   283  
   284  	const N = 10
   285  	wg.Add(N)
   286  	for i := 0; i < N; i++ {
   287  		go reader()
   288  	}
   289  	wg.Add(2 * N)
   290  	for i := 0; i < 2*N; i++ {
   291  		go writer(i%2 != 0)
   292  	}
   293  	wg.Add(N)
   294  	for i := 0; i < N; i++ {
   295  		go reader()
   296  	}
   297  	wg.Wait()
   298  }
   299  
   300  func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
   301  	switch runtime.GOOS {
   302  	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
   303  		t.Skipf("not supported on %s", runtime.GOOS)
   304  	}
   305  
   306  	payload := []byte("HELLO-R-U-THERE")
   307  	iph, err := (&ipv4.Header{
   308  		Version:  ipv4.Version,
   309  		Len:      ipv4.HeaderLen,
   310  		TotalLen: ipv4.HeaderLen + len(payload),
   311  		TTL:      1,
   312  		Protocol: iana.ProtocolReserved,
   313  		Src:      net.IPv4(192, 0, 2, 1),
   314  		Dst:      net.IPv4(192, 0, 2, 254),
   315  	}).Marshal()
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
   320  	datagram := append(greh, append(iph, payload...)...)
   321  
   322  	t.Run("UDP", func(t *testing.T) {
   323  		c, err := nettest.NewLocalPacketListener("udp4")
   324  		if err != nil {
   325  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   326  		}
   327  		defer c.Close()
   328  		p := ipv4.NewPacketConn(c)
   329  		t.Run("ToFrom", func(t *testing.T) {
   330  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
   331  		})
   332  		t.Run("Batch", func(t *testing.T) {
   333  			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
   334  		})
   335  	})
   336  	t.Run("IP", func(t *testing.T) {
   337  		switch runtime.GOOS {
   338  		case "netbsd":
   339  			t.Skip("need to configure gre on netbsd")
   340  		case "openbsd":
   341  			t.Skip("net.inet.gre.allow=0 by default on openbsd")
   342  		}
   343  
   344  		c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
   345  		if err != nil {
   346  			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
   347  		}
   348  		defer c.Close()
   349  		p := ipv4.NewPacketConn(c)
   350  		t.Run("ToFrom", func(t *testing.T) {
   351  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
   352  		})
   353  		t.Run("Batch", func(t *testing.T) {
   354  			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
   355  		})
   356  	})
   357  }
   358  
   359  func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) {
   360  	t.Helper()
   361  
   362  	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
   363  	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
   364  
   365  	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
   366  		if protocolNotSupported(err) {
   367  			t.Skipf("not supported on %s", runtime.GOOS)
   368  		}
   369  		t.Fatal(err)
   370  	}
   371  
   372  	var wg sync.WaitGroup
   373  	reader := func() {
   374  		defer wg.Done()
   375  		b := make([]byte, 128)
   376  		n, cm, _, err := p.ReadFrom(b)
   377  		if err != nil {
   378  			t.Error(err)
   379  			return
   380  		}
   381  		if !bytes.Equal(b[:n], data) {
   382  			t.Errorf("got %#v; want %#v", b[:n], data)
   383  			return
   384  		}
   385  		s := cm.String()
   386  		if strings.Contains(s, ",") {
   387  			t.Errorf("should be space-separated values: %s", s)
   388  			return
   389  		}
   390  	}
   391  	batchReader := func() {
   392  		defer wg.Done()
   393  		ms := []ipv4.Message{
   394  			{
   395  				Buffers: [][]byte{make([]byte, 128)},
   396  				OOB:     ipv4.NewControlMessage(cf),
   397  			},
   398  		}
   399  		n, err := p.ReadBatch(ms, 0)
   400  		if err != nil {
   401  			t.Error(err)
   402  			return
   403  		}
   404  		if n != len(ms) {
   405  			t.Errorf("got %d; want %d", n, len(ms))
   406  			return
   407  		}
   408  		var cm ipv4.ControlMessage
   409  		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
   410  			t.Error(err)
   411  			return
   412  		}
   413  		var b []byte
   414  		if _, ok := dst.(*net.IPAddr); ok {
   415  			var h ipv4.Header
   416  			if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil {
   417  				t.Error(err)
   418  				return
   419  			}
   420  			b = ms[0].Buffers[0][h.Len:ms[0].N]
   421  		} else {
   422  			b = ms[0].Buffers[0][:ms[0].N]
   423  		}
   424  		if !bytes.Equal(b, data) {
   425  			t.Errorf("got %#v; want %#v", b, data)
   426  			return
   427  		}
   428  		s := cm.String()
   429  		if strings.Contains(s, ",") {
   430  			t.Errorf("should be space-separated values: %s", s)
   431  			return
   432  		}
   433  	}
   434  	writer := func(toggle bool) {
   435  		defer wg.Done()
   436  		cm := ipv4.ControlMessage{
   437  			Src: net.IPv4(127, 0, 0, 1),
   438  		}
   439  		if ifi != nil {
   440  			cm.IfIndex = ifi.Index
   441  		}
   442  		if err := p.SetControlMessage(cf, toggle); err != nil {
   443  			t.Error(err)
   444  			return
   445  		}
   446  		n, err := p.WriteTo(data, &cm, dst)
   447  		if err != nil {
   448  			t.Error(err)
   449  			return
   450  		}
   451  		if n != len(data) {
   452  			t.Errorf("got %d; want %d", n, len(data))
   453  			return
   454  		}
   455  	}
   456  	batchWriter := func(toggle bool) {
   457  		defer wg.Done()
   458  		cm := ipv4.ControlMessage{
   459  			Src: net.IPv4(127, 0, 0, 1),
   460  		}
   461  		if ifi != nil {
   462  			cm.IfIndex = ifi.Index
   463  		}
   464  		if err := p.SetControlMessage(cf, toggle); err != nil {
   465  			t.Error(err)
   466  			return
   467  		}
   468  		ms := []ipv4.Message{
   469  			{
   470  				Buffers: [][]byte{data},
   471  				OOB:     cm.Marshal(),
   472  				Addr:    dst,
   473  			},
   474  		}
   475  		n, err := p.WriteBatch(ms, 0)
   476  		if err != nil {
   477  			t.Error(err)
   478  			return
   479  		}
   480  		if n != len(ms) {
   481  			t.Errorf("got %d; want %d", n, len(ms))
   482  			return
   483  		}
   484  		if ms[0].N != len(data) {
   485  			t.Errorf("got %d; want %d", ms[0].N, len(data))
   486  			return
   487  		}
   488  	}
   489  
   490  	const N = 10
   491  	wg.Add(N)
   492  	for i := 0; i < N; i++ {
   493  		if batch {
   494  			go batchReader()
   495  		} else {
   496  			go reader()
   497  		}
   498  	}
   499  	wg.Add(2 * N)
   500  	for i := 0; i < 2*N; i++ {
   501  		if batch {
   502  			go batchWriter(i%2 != 0)
   503  		} else {
   504  			go writer(i%2 != 0)
   505  		}
   506  
   507  	}
   508  	wg.Add(N)
   509  	for i := 0; i < N; i++ {
   510  		if batch {
   511  			go batchReader()
   512  		} else {
   513  			go reader()
   514  		}
   515  	}
   516  	wg.Wait()
   517  }