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 }