golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv4/unicast_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  	"net"
    10  	"os"
    11  	"runtime"
    12  	"testing"
    13  	"time"
    14  
    15  	"golang.org/x/net/icmp"
    16  	"golang.org/x/net/internal/iana"
    17  	"golang.org/x/net/ipv4"
    18  	"golang.org/x/net/nettest"
    19  )
    20  
    21  func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
    22  	switch runtime.GOOS {
    23  	case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
    24  		t.Skipf("not supported on %s", runtime.GOOS)
    25  	}
    26  	// Skip this check on z/OS since net.Interfaces() does not return loopback, however
    27  	// this does not affect the test and it will still pass.
    28  	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil && runtime.GOOS != "zos" {
    29  		t.Skipf("not available on %s", runtime.GOOS)
    30  	}
    31  
    32  	c, err := nettest.NewLocalPacketListener("udp4")
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  	defer c.Close()
    37  	p := ipv4.NewPacketConn(c)
    38  	defer p.Close()
    39  
    40  	dst := c.LocalAddr()
    41  	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
    42  	wb := []byte("HELLO-R-U-THERE")
    43  
    44  	for i, toggle := range []bool{true, false, true} {
    45  		if err := p.SetControlMessage(cf, toggle); err != nil {
    46  			if protocolNotSupported(err) {
    47  				t.Logf("not supported on %s", runtime.GOOS)
    48  				continue
    49  			}
    50  			t.Fatal(err)
    51  		}
    52  		p.SetTTL(i + 1)
    53  
    54  		backoff := time.Millisecond
    55  		for {
    56  			n, err := p.WriteTo(wb, nil, dst)
    57  			if err != nil {
    58  				if n == 0 && isENOBUFS(err) {
    59  					time.Sleep(backoff)
    60  					backoff *= 2
    61  					continue
    62  				}
    63  				t.Fatal(err)
    64  			}
    65  			if n != len(wb) {
    66  				t.Fatalf("got %d; want %d", n, len(wb))
    67  			}
    68  			break
    69  		}
    70  
    71  		rb := make([]byte, 128)
    72  		if n, _, _, err := p.ReadFrom(rb); err != nil {
    73  			t.Fatal(err)
    74  		} else if !bytes.Equal(rb[:n], wb) {
    75  			t.Fatalf("got %v; want %v", rb[:n], wb)
    76  		}
    77  	}
    78  }
    79  
    80  func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
    81  	if !nettest.SupportsRawSocket() {
    82  		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    83  	}
    84  	// Skip this check on z/OS since net.Interfaces() does not return loopback, however
    85  	// this does not affect the test and it will still pass.
    86  	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil && runtime.GOOS != "zos" {
    87  		t.Skipf("not available on %s", runtime.GOOS)
    88  	}
    89  
    90  	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	defer c.Close()
    95  
    96  	dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	p := ipv4.NewPacketConn(c)
   101  	defer p.Close()
   102  	cf := ipv4.FlagDst | ipv4.FlagInterface
   103  	if runtime.GOOS != "illumos" && runtime.GOOS != "solaris" {
   104  		// Illumos and Solaris never allow modification of ICMP properties.
   105  		cf |= ipv4.FlagTTL
   106  	}
   107  
   108  	for i, toggle := range []bool{true, false, true} {
   109  		wb, err := (&icmp.Message{
   110  			Type: ipv4.ICMPTypeEcho, Code: 0,
   111  			Body: &icmp.Echo{
   112  				ID: os.Getpid() & 0xffff, Seq: i + 1,
   113  				Data: []byte("HELLO-R-U-THERE"),
   114  			},
   115  		}).Marshal(nil)
   116  		if err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		if err := p.SetControlMessage(cf, toggle); err != nil {
   120  			if protocolNotSupported(err) {
   121  				t.Logf("not supported on %s", runtime.GOOS)
   122  				continue
   123  			}
   124  			t.Fatal(err)
   125  		}
   126  		p.SetTTL(i + 1)
   127  
   128  		backoff := time.Millisecond
   129  		for {
   130  			n, err := p.WriteTo(wb, nil, dst)
   131  			if err != nil {
   132  				if n == 0 && isENOBUFS(err) {
   133  					time.Sleep(backoff)
   134  					backoff *= 2
   135  					continue
   136  				}
   137  				t.Fatal(err)
   138  			}
   139  			if n != len(wb) {
   140  				t.Fatalf("got %d; want %d", n, len(wb))
   141  			}
   142  			break
   143  		}
   144  
   145  		rb := make([]byte, 128)
   146  	loop:
   147  		if n, _, _, err := p.ReadFrom(rb); err != nil {
   148  			t.Fatal(err)
   149  		} else {
   150  			m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n])
   151  			if err != nil {
   152  				t.Fatal(err)
   153  			}
   154  			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
   155  				// On Linux we must handle own sent packets.
   156  				goto loop
   157  			}
   158  			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
   159  				t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
   160  			}
   161  		}
   162  	}
   163  }
   164  
   165  func TestRawConnReadWriteUnicastICMP(t *testing.T) {
   166  	if !nettest.SupportsRawSocket() {
   167  		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
   168  	}
   169  	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil {
   170  		t.Skipf("not available on %s", runtime.GOOS)
   171  	}
   172  
   173  	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	defer c.Close()
   178  
   179  	dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	r, err := ipv4.NewRawConn(c)
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	defer r.Close()
   188  	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
   189  
   190  	for i, toggle := range []bool{true, false, true} {
   191  		wb, err := (&icmp.Message{
   192  			Type: ipv4.ICMPTypeEcho, Code: 0,
   193  			Body: &icmp.Echo{
   194  				ID: os.Getpid() & 0xffff, Seq: i + 1,
   195  				Data: []byte("HELLO-R-U-THERE"),
   196  			},
   197  		}).Marshal(nil)
   198  		if err != nil {
   199  			t.Fatal(err)
   200  		}
   201  		wh := &ipv4.Header{
   202  			Version:  ipv4.Version,
   203  			Len:      ipv4.HeaderLen,
   204  			TOS:      i + 1,
   205  			TotalLen: ipv4.HeaderLen + len(wb),
   206  			TTL:      i + 1,
   207  			Protocol: 1,
   208  			Dst:      dst.IP,
   209  		}
   210  		if err := r.SetControlMessage(cf, toggle); err != nil {
   211  			if protocolNotSupported(err) {
   212  				t.Logf("not supported on %s", runtime.GOOS)
   213  				continue
   214  			}
   215  			t.Fatal(err)
   216  		}
   217  		if err := r.WriteTo(wh, wb, nil); err != nil {
   218  			t.Fatal(err)
   219  		}
   220  		rb := make([]byte, ipv4.HeaderLen+128)
   221  	loop:
   222  		if _, b, _, err := r.ReadFrom(rb); err != nil {
   223  			t.Fatal(err)
   224  		} else {
   225  			m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
   226  			if err != nil {
   227  				t.Fatal(err)
   228  			}
   229  			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
   230  				// On Linux we must handle own sent packets.
   231  				goto loop
   232  			}
   233  			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
   234  				t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
   235  			}
   236  		}
   237  	}
   238  }