gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/transport/testing/context/flow.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package context
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"gvisor.dev/gvisor/pkg/buffer"
    22  	"gvisor.dev/gvisor/pkg/tcpip"
    23  	"gvisor.dev/gvisor/pkg/tcpip/checker"
    24  	"gvisor.dev/gvisor/pkg/tcpip/checksum"
    25  	"gvisor.dev/gvisor/pkg/tcpip/header"
    26  	"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
    27  	"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
    28  	"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
    29  )
    30  
    31  const (
    32  	v4MappedAddrPrefix = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"
    33  
    34  	// StackPort is the port TestFlow uses with StackAddr.
    35  	StackPort = 1234
    36  
    37  	// TestPort is the port TestFlow uses with TestAddr.
    38  	TestPort = 4096
    39  )
    40  
    41  var (
    42  	// BroadcastAddr is the IPv4 broadcast address.
    43  	BroadcastAddr = header.IPv4Broadcast
    44  
    45  	// StackAddr is the IPv4 address assigned to the stack's NIC and is used by
    46  	// TestFlow as the local address.
    47  	StackAddr = tcpip.AddrFromSlice([]byte("\x0a\x00\x00\x01"))
    48  
    49  	// StackV4MappedAddr is the IPv4-mapped IPv6 StackAddr.
    50  	StackV4MappedAddr = tcpip.AddrFromSlice(append([]byte(v4MappedAddrPrefix), StackAddr.AsSlice()...))
    51  
    52  	// TestAddr is the IPv4 address used by TestFlow as the remote address.
    53  	TestAddr = tcpip.AddrFromSlice([]byte("\x0a\x00\x00\x02"))
    54  
    55  	// TestV4MappedAddr is the IPv4-mapped IPv6 TestAddr.
    56  	TestV4MappedAddr = tcpip.AddrFromSlice(append([]byte(v4MappedAddrPrefix), TestAddr.AsSlice()...))
    57  
    58  	// MulticastAddr is the IPv4 multicast address used by IPv4 multicast
    59  	// TestFlow.
    60  	MulticastAddr = tcpip.AddrFromSlice([]byte("\xe8\x2b\xd3\xea"))
    61  
    62  	// MulticastV4MappedAddr is the IPv4-mapped IPv6 MulticastAddr.
    63  	MulticastV4MappedAddr = tcpip.AddrFromSlice(append([]byte(v4MappedAddrPrefix), MulticastAddr.AsSlice()...))
    64  
    65  	// BroadcastV4MappedAddr is the IPv4-mapped IPv6 BroadcastAddr.
    66  	BroadcastV4MappedAddr = tcpip.AddrFromSlice(append([]byte(v4MappedAddrPrefix), BroadcastAddr.AsSlice()...))
    67  
    68  	// V4MappedWildcardAddr is the IPv4-mapped IPv6 wildcard (any) address.
    69  	V4MappedWildcardAddr = tcpip.AddrFromSlice([]byte(v4MappedAddrPrefix + "\x00\x00\x00\x00"))
    70  
    71  	// StackV6Addr is the IPv6 address assigned to the stack's NIC and is used by
    72  	// TestFlow as the local address.
    73  	StackV6Addr = tcpip.AddrFromSlice([]byte("\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"))
    74  
    75  	// TestV6Addr is the IPv6 address used by TestFlow as the remote address.
    76  	TestV6Addr = tcpip.AddrFromSlice([]byte("\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"))
    77  
    78  	// MulticastV6Addr is the IPv6 multicast address used by IPv6 multicast
    79  	// TestFlow.
    80  	MulticastV6Addr = tcpip.AddrFromSlice([]byte("\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"))
    81  )
    82  
    83  // Header4Tuple stores the 4-tuple {src-IP, src-port, dst-IP, dst-port} used in
    84  // a packet header. These values are used to populate a header or verify one.
    85  // Note that because they are used in packet headers, the addresses are never in
    86  // a V4-mapped format.
    87  type Header4Tuple struct {
    88  	Src tcpip.FullAddress
    89  	Dst tcpip.FullAddress
    90  }
    91  
    92  // TestFlow implements a helper type used for sending and receiving test
    93  // packets. A given test TestFlow value defines 1) the socket endpoint used for
    94  // the test and 2) the type of packet send or received on the endpoint. E.g., a
    95  // MulticastV6Only TestFlow is a IPv6 multicast packet passing through a V6-only
    96  // endpoint. The type provides helper methods to characterize the TestFlow
    97  // (e.g., IsV4) as well as return a proper Header4Tuple for it.
    98  type TestFlow int
    99  
   100  const (
   101  	_ TestFlow = iota
   102  
   103  	// UnicastV4 is IPv4 unicast on an IPv4 socket
   104  	UnicastV4
   105  
   106  	// UnicastV4in6 is IPv4-mapped IPv6 unicast on an IPv6 dual socket
   107  	UnicastV4in6
   108  
   109  	// UnicastV6 is IPv6 unicast on an IPv6 socket
   110  	UnicastV6
   111  
   112  	// UnicastV6Only is IPv6 unicast on an IPv6-only socket
   113  	UnicastV6Only
   114  
   115  	// MulticastV4 is IPv4 multicast on an IPv4 socket
   116  	MulticastV4
   117  
   118  	// MulticastV4in6 is IPv4-mapped IPv6 multicast on an IPv6 dual socket
   119  	MulticastV4in6
   120  
   121  	// MulticastV6 is IPv6 multicast on an IPv6 socket
   122  	MulticastV6
   123  
   124  	// MulticastV6Only IPv6 multicast on an IPv6-only socket
   125  	MulticastV6Only
   126  
   127  	// Broadcast is IPv4 broadcast on an IPv4 socket
   128  	Broadcast
   129  
   130  	// BroadcastIn6 is IPv4-mapped IPv6 broadcast on an IPv6 dual socket
   131  	BroadcastIn6
   132  
   133  	// ReverseMulticastV4 is IPv4 multicast src. Must fail.
   134  	ReverseMulticastV4
   135  
   136  	// ReverseMulticastV6 is IPv6 multicast src. Must fail.
   137  	ReverseMulticastV6
   138  )
   139  
   140  // String implements fmt.Stringer interface.
   141  func (flow TestFlow) String() string {
   142  	switch flow {
   143  	case UnicastV4:
   144  		return "UnicastV4"
   145  	case UnicastV6:
   146  		return "UnicastV6"
   147  	case UnicastV6Only:
   148  		return "UnicastV6Only"
   149  	case UnicastV4in6:
   150  		return "UnicastV4in6"
   151  	case MulticastV4:
   152  		return "MulticastV4"
   153  	case MulticastV6:
   154  		return "MulticastV6"
   155  	case MulticastV6Only:
   156  		return "MulticastV6Only"
   157  	case MulticastV4in6:
   158  		return "MulticastV4in6"
   159  	case Broadcast:
   160  		return "Broadcast"
   161  	case BroadcastIn6:
   162  		return "BroadcastIn6"
   163  	case ReverseMulticastV4:
   164  		return "ReverseMulticastV4"
   165  	case ReverseMulticastV6:
   166  		return "ReverseMulticastV6"
   167  	default:
   168  		return "Unknown"
   169  	}
   170  }
   171  
   172  // PacketDirection specifies the direction of a TestFlow.
   173  type PacketDirection int
   174  
   175  const (
   176  	_ PacketDirection = iota
   177  
   178  	// Incoming indicates the direction from Test*Addr to Stack*Addr.
   179  	Incoming
   180  
   181  	// Outgoing indicates the direction from Test*Addr to Stack*Addr.
   182  	Outgoing
   183  )
   184  
   185  // MakeHeader4Tuple returns the Header4Tuple for the given TestFlow and direction. Note
   186  // that the tuple contains no mapped addresses as those only exist at the socket
   187  // level but not at the packet header level.
   188  func (flow TestFlow) MakeHeader4Tuple(direction PacketDirection) Header4Tuple {
   189  	var h Header4Tuple
   190  	if flow.IsV4() {
   191  		switch direction {
   192  		case Outgoing:
   193  			h = Header4Tuple{
   194  				Src: tcpip.FullAddress{Addr: StackAddr, Port: StackPort},
   195  				Dst: tcpip.FullAddress{Addr: TestAddr, Port: TestPort},
   196  			}
   197  		case Incoming:
   198  			h = Header4Tuple{
   199  				Src: tcpip.FullAddress{Addr: TestAddr, Port: TestPort},
   200  				Dst: tcpip.FullAddress{Addr: StackAddr, Port: StackPort},
   201  			}
   202  		default:
   203  			panic(fmt.Sprintf("unknown direction %d", direction))
   204  		}
   205  
   206  		if flow.IsMulticast() {
   207  			h.Dst.Addr = MulticastAddr
   208  		} else if flow.isBroadcast() {
   209  			h.Dst.Addr = BroadcastAddr
   210  		}
   211  	} else { // IPv6
   212  		switch direction {
   213  		case Outgoing:
   214  			h = Header4Tuple{
   215  				Src: tcpip.FullAddress{Addr: StackV6Addr, Port: StackPort},
   216  				Dst: tcpip.FullAddress{Addr: TestV6Addr, Port: TestPort},
   217  			}
   218  		case Incoming:
   219  			h = Header4Tuple{
   220  				Src: tcpip.FullAddress{Addr: TestV6Addr, Port: TestPort},
   221  				Dst: tcpip.FullAddress{Addr: StackV6Addr, Port: StackPort},
   222  			}
   223  		default:
   224  			panic(fmt.Sprintf("unknown direction %d", direction))
   225  		}
   226  
   227  		if flow.IsMulticast() {
   228  			h.Dst.Addr = MulticastV6Addr
   229  		}
   230  	}
   231  
   232  	if flow.isReverseMulticast() {
   233  		h.Src.Addr = flow.GetMulticastAddr()
   234  	}
   235  
   236  	return h
   237  }
   238  
   239  // GetMulticastAddr returns the multicast address of a TestFlow.
   240  func (flow TestFlow) GetMulticastAddr() tcpip.Address {
   241  	if flow.IsV4() {
   242  		return MulticastAddr
   243  	}
   244  	return MulticastV6Addr
   245  }
   246  
   247  // MapAddrIfApplicable converts the given IPv4 address into its V4-mapped
   248  // version if it is applicable to the TestFlow.
   249  func (flow TestFlow) MapAddrIfApplicable(v4Addr tcpip.Address) tcpip.Address {
   250  	if flow.isMapped() {
   251  		return tcpip.AddrFromSlice(append([]byte(v4MappedAddrPrefix), v4Addr.AsSlice()...))
   252  	}
   253  	return v4Addr
   254  }
   255  
   256  // NetProto returns the network protocol of a TestFlow.
   257  func (flow TestFlow) NetProto() tcpip.NetworkProtocolNumber {
   258  	if flow.IsV4() {
   259  		return ipv4.ProtocolNumber
   260  	}
   261  	return ipv6.ProtocolNumber
   262  }
   263  
   264  // SockProto returns the network protocol number a socket must be configured
   265  // with to support a given TestFlow.
   266  func (flow TestFlow) SockProto() tcpip.NetworkProtocolNumber {
   267  	switch flow {
   268  	case UnicastV4in6, UnicastV6, UnicastV6Only, MulticastV4in6, MulticastV6, MulticastV6Only, BroadcastIn6, ReverseMulticastV6:
   269  		return ipv6.ProtocolNumber
   270  	case UnicastV4, MulticastV4, Broadcast, ReverseMulticastV4:
   271  		return ipv4.ProtocolNumber
   272  	default:
   273  		panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
   274  	}
   275  }
   276  
   277  // CheckerFn returns the correct network checker for the current TestFlow.
   278  func (flow TestFlow) CheckerFn() func(*testing.T, *buffer.View, ...checker.NetworkChecker) {
   279  	if flow.IsV4() {
   280  		return checker.IPv4
   281  	}
   282  	return checker.IPv6
   283  }
   284  
   285  // IsV4 returns true for IPv4 TestFlow's.
   286  func (flow TestFlow) IsV4() bool {
   287  	return flow.SockProto() == ipv4.ProtocolNumber || flow.isMapped()
   288  }
   289  
   290  // IsV6 returns true for IPv6 TestFlow's.
   291  func (flow TestFlow) IsV6() bool { return !flow.IsV4() }
   292  
   293  func (flow TestFlow) isV6Only() bool {
   294  	switch flow {
   295  	case UnicastV6Only, MulticastV6Only:
   296  		return true
   297  	case UnicastV4, UnicastV4in6, UnicastV6, MulticastV4, MulticastV4in6, MulticastV6, Broadcast, BroadcastIn6, ReverseMulticastV4, ReverseMulticastV6:
   298  		return false
   299  	default:
   300  		panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
   301  	}
   302  }
   303  
   304  // IsMulticast returns true if the TestFlow is multicast.
   305  func (flow TestFlow) IsMulticast() bool {
   306  	switch flow {
   307  	case MulticastV4, MulticastV4in6, MulticastV6, MulticastV6Only:
   308  		return true
   309  	case UnicastV4, UnicastV4in6, UnicastV6, UnicastV6Only, Broadcast, BroadcastIn6, ReverseMulticastV4, ReverseMulticastV6:
   310  		return false
   311  	default:
   312  		panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
   313  	}
   314  }
   315  
   316  func (flow TestFlow) isBroadcast() bool {
   317  	switch flow {
   318  	case Broadcast, BroadcastIn6:
   319  		return true
   320  	case UnicastV4, UnicastV4in6, UnicastV6, UnicastV6Only, MulticastV4, MulticastV4in6, MulticastV6, MulticastV6Only, ReverseMulticastV4, ReverseMulticastV6:
   321  		return false
   322  	default:
   323  		panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
   324  	}
   325  }
   326  
   327  func (flow TestFlow) isMapped() bool {
   328  	switch flow {
   329  	case UnicastV4in6, MulticastV4in6, BroadcastIn6:
   330  		return true
   331  	case UnicastV4, UnicastV6, UnicastV6Only, MulticastV4, MulticastV6, MulticastV6Only, Broadcast, ReverseMulticastV4, ReverseMulticastV6:
   332  		return false
   333  	default:
   334  		panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
   335  	}
   336  }
   337  
   338  func (flow TestFlow) isReverseMulticast() bool {
   339  	switch flow {
   340  	case ReverseMulticastV4, ReverseMulticastV6:
   341  		return true
   342  	default:
   343  		return false
   344  	}
   345  }
   346  
   347  // BuildV4UDPPacket builds an IPv4 UDP packet.
   348  func BuildV4UDPPacket(payload []byte, h Header4Tuple, tos, ttl uint8, badChecksum bool) []byte {
   349  	// Allocate a buffer for data and headers.
   350  	buf := make([]byte, header.UDPMinimumSize+header.IPv4MinimumSize+len(payload))
   351  	payloadStart := len(buf) - len(payload)
   352  	copy(buf[payloadStart:], payload)
   353  
   354  	// Initialize the IP header.
   355  	ip := header.IPv4(buf)
   356  	ip.Encode(&header.IPv4Fields{
   357  		TOS:         tos,
   358  		TotalLength: uint16(len(buf)),
   359  		TTL:         ttl,
   360  		Protocol:    uint8(udp.ProtocolNumber),
   361  		SrcAddr:     h.Src.Addr,
   362  		DstAddr:     h.Dst.Addr,
   363  	})
   364  	ip.SetChecksum(^ip.CalculateChecksum())
   365  
   366  	// Initialize the UDP header.
   367  	u := header.UDP(buf[header.IPv4MinimumSize:])
   368  	u.Encode(&header.UDPFields{
   369  		SrcPort: h.Src.Port,
   370  		DstPort: h.Dst.Port,
   371  		Length:  uint16(header.UDPMinimumSize + len(payload)),
   372  	})
   373  
   374  	// Calculate the UDP pseudo-header checksum.
   375  	xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, h.Src.Addr, h.Dst.Addr, uint16(len(u)))
   376  
   377  	// Calculate the UDP checksum and set it.
   378  	xsum = checksum.Checksum(payload, xsum)
   379  	u.SetChecksum(^u.CalculateChecksum(xsum))
   380  
   381  	if badChecksum {
   382  		// Invalidate the UDP header checksum field, taking care to avoid overflow
   383  		// to zero, which would disable checksum validation.
   384  		for {
   385  			u.SetChecksum(u.Checksum() + 1)
   386  			if u.Checksum() != 0 {
   387  				break
   388  			}
   389  		}
   390  	}
   391  
   392  	return buf
   393  }
   394  
   395  // BuildV6UDPPacket builds an IPv6 UDP packet.
   396  func BuildV6UDPPacket(payload []byte, h Header4Tuple, tclass, hoplimit uint8, badChecksum bool) []byte {
   397  	// Allocate a buffer for data and headers.
   398  	buf := make([]byte, header.UDPMinimumSize+header.IPv6MinimumSize+len(payload))
   399  	payloadStart := len(buf) - len(payload)
   400  	copy(buf[payloadStart:], payload)
   401  
   402  	// Initialize the IP header.
   403  	ip := header.IPv6(buf)
   404  	ip.Encode(&header.IPv6Fields{
   405  		TrafficClass:      tclass,
   406  		PayloadLength:     uint16(header.UDPMinimumSize + len(payload)),
   407  		TransportProtocol: udp.ProtocolNumber,
   408  		HopLimit:          hoplimit,
   409  		SrcAddr:           h.Src.Addr,
   410  		DstAddr:           h.Dst.Addr,
   411  	})
   412  
   413  	// Initialize the UDP header.
   414  	u := header.UDP(buf[header.IPv6MinimumSize:])
   415  	u.Encode(&header.UDPFields{
   416  		SrcPort: h.Src.Port,
   417  		DstPort: h.Dst.Port,
   418  		Length:  uint16(header.UDPMinimumSize + len(payload)),
   419  	})
   420  
   421  	// Calculate the UDP pseudo-header checksum.
   422  	xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, h.Src.Addr, h.Dst.Addr, uint16(len(u)))
   423  
   424  	// Calculate the UDP checksum and set it.
   425  	xsum = checksum.Checksum(payload, xsum)
   426  	u.SetChecksum(^u.CalculateChecksum(xsum))
   427  
   428  	if badChecksum {
   429  		// Invalidate the UDP header checksum field (Unlike IPv4, zero is a valid
   430  		// checksum value for IPv6 so no need to avoid it).
   431  		u := header.UDP(buf[header.IPv6MinimumSize:])
   432  		u.SetChecksum(u.Checksum() + 1)
   433  	}
   434  
   435  	return buf
   436  }
   437  
   438  // BuildUDPPacket builds an IPv4 or IPv6 UDP packet, depending on the specified
   439  // TestFlow.
   440  func BuildUDPPacket(payload []byte, flow TestFlow, direction PacketDirection, tosOrTclass, ttlOrHopLimit uint8, badChecksum bool) []byte {
   441  	h := flow.MakeHeader4Tuple(direction)
   442  	if flow.IsV4() {
   443  		return BuildV4UDPPacket(payload, h, tosOrTclass, ttlOrHopLimit, badChecksum)
   444  	}
   445  	return BuildV6UDPPacket(payload, h, tosOrTclass, ttlOrHopLimit, badChecksum)
   446  }