github.com/google/netstack@v0.0.0-20191123085552-55fcc16cd0eb/tcpip/link/fdbased/endpoint_test.go (about) 1 // Copyright 2018 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 // +build linux 16 17 package fdbased 18 19 import ( 20 "bytes" 21 "fmt" 22 "math/rand" 23 "reflect" 24 "syscall" 25 "testing" 26 "time" 27 "unsafe" 28 29 "github.com/google/netstack/tcpip" 30 "github.com/google/netstack/tcpip/buffer" 31 "github.com/google/netstack/tcpip/header" 32 "github.com/google/netstack/tcpip/link/rawfile" 33 "github.com/google/netstack/tcpip/stack" 34 ) 35 36 const ( 37 mtu = 1500 38 laddr = tcpip.LinkAddress("\x11\x22\x33\x44\x55\x66") 39 raddr = tcpip.LinkAddress("\x77\x88\x99\xaa\xbb\xcc") 40 proto = 10 41 csumOffset = 48 42 gsoMSS = 500 43 ) 44 45 type packetInfo struct { 46 raddr tcpip.LinkAddress 47 proto tcpip.NetworkProtocolNumber 48 contents tcpip.PacketBuffer 49 } 50 51 type context struct { 52 t *testing.T 53 fds [2]int 54 ep stack.LinkEndpoint 55 ch chan packetInfo 56 done chan struct{} 57 } 58 59 func newContext(t *testing.T, opt *Options) *context { 60 fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0) 61 if err != nil { 62 t.Fatalf("Socketpair failed: %v", err) 63 } 64 65 done := make(chan struct{}, 1) 66 opt.ClosedFunc = func(*tcpip.Error) { 67 done <- struct{}{} 68 } 69 70 opt.FDs = []int{fds[1]} 71 ep, err := New(opt) 72 if err != nil { 73 t.Fatalf("Failed to create FD endpoint: %v", err) 74 } 75 76 c := &context{ 77 t: t, 78 fds: fds, 79 ep: ep, 80 ch: make(chan packetInfo, 100), 81 done: done, 82 } 83 84 ep.Attach(c) 85 86 return c 87 } 88 89 func (c *context) cleanup() { 90 syscall.Close(c.fds[0]) 91 <-c.done 92 syscall.Close(c.fds[1]) 93 } 94 95 func (c *context) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt tcpip.PacketBuffer) { 96 c.ch <- packetInfo{remote, protocol, pkt} 97 } 98 99 func TestNoEthernetProperties(t *testing.T) { 100 c := newContext(t, &Options{MTU: mtu}) 101 defer c.cleanup() 102 103 if want, v := uint16(0), c.ep.MaxHeaderLength(); want != v { 104 t.Fatalf("MaxHeaderLength() = %v, want %v", v, want) 105 } 106 107 if want, v := uint32(mtu), c.ep.MTU(); want != v { 108 t.Fatalf("MTU() = %v, want %v", v, want) 109 } 110 } 111 112 func TestEthernetProperties(t *testing.T) { 113 c := newContext(t, &Options{EthernetHeader: true, MTU: mtu}) 114 defer c.cleanup() 115 116 if want, v := uint16(header.EthernetMinimumSize), c.ep.MaxHeaderLength(); want != v { 117 t.Fatalf("MaxHeaderLength() = %v, want %v", v, want) 118 } 119 120 if want, v := uint32(mtu), c.ep.MTU(); want != v { 121 t.Fatalf("MTU() = %v, want %v", v, want) 122 } 123 } 124 125 func TestAddress(t *testing.T) { 126 addrs := []tcpip.LinkAddress{"", "abc", "def"} 127 for _, a := range addrs { 128 t.Run(fmt.Sprintf("Address: %q", a), func(t *testing.T) { 129 c := newContext(t, &Options{Address: a, MTU: mtu}) 130 defer c.cleanup() 131 132 if want, v := a, c.ep.LinkAddress(); want != v { 133 t.Fatalf("LinkAddress() = %v, want %v", v, want) 134 } 135 }) 136 } 137 } 138 139 func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32) { 140 c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth, GSOMaxSize: gsoMaxSize}) 141 defer c.cleanup() 142 143 r := &stack.Route{ 144 RemoteLinkAddress: raddr, 145 } 146 147 // Build header. 148 hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()) + 100) 149 b := hdr.Prepend(100) 150 for i := range b { 151 b[i] = uint8(rand.Intn(256)) 152 } 153 154 // Build payload and write. 155 payload := make(buffer.View, plen) 156 for i := range payload { 157 payload[i] = uint8(rand.Intn(256)) 158 } 159 want := append(hdr.View(), payload...) 160 var gso *stack.GSO 161 if gsoMaxSize != 0 { 162 gso = &stack.GSO{ 163 Type: stack.GSOTCPv6, 164 NeedsCsum: true, 165 CsumOffset: csumOffset, 166 MSS: gsoMSS, 167 MaxSize: gsoMaxSize, 168 L3HdrLen: header.IPv4MaximumHeaderSize, 169 } 170 } 171 if err := c.ep.WritePacket(r, gso, proto, tcpip.PacketBuffer{ 172 Header: hdr, 173 Data: payload.ToVectorisedView(), 174 }); err != nil { 175 t.Fatalf("WritePacket failed: %v", err) 176 } 177 178 // Read from fd, then compare with what we wrote. 179 b = make([]byte, mtu) 180 n, err := syscall.Read(c.fds[0], b) 181 if err != nil { 182 t.Fatalf("Read failed: %v", err) 183 } 184 b = b[:n] 185 if gsoMaxSize != 0 { 186 vnetHdr := *(*virtioNetHdr)(unsafe.Pointer(&b[0])) 187 if vnetHdr.flags&_VIRTIO_NET_HDR_F_NEEDS_CSUM == 0 { 188 t.Fatalf("virtioNetHdr.flags %v doesn't contain %v", vnetHdr.flags, _VIRTIO_NET_HDR_F_NEEDS_CSUM) 189 } 190 csumStart := header.EthernetMinimumSize + gso.L3HdrLen 191 if vnetHdr.csumStart != csumStart { 192 t.Fatalf("vnetHdr.csumStart = %v, want %v", vnetHdr.csumStart, csumStart) 193 } 194 if vnetHdr.csumOffset != csumOffset { 195 t.Fatalf("vnetHdr.csumOffset = %v, want %v", vnetHdr.csumOffset, csumOffset) 196 } 197 gsoType := uint8(0) 198 if int(gso.MSS) < plen { 199 gsoType = _VIRTIO_NET_HDR_GSO_TCPV6 200 } 201 if vnetHdr.gsoType != gsoType { 202 t.Fatalf("vnetHdr.gsoType = %v, want %v", vnetHdr.gsoType, gsoType) 203 } 204 b = b[virtioNetHdrSize:] 205 } 206 if eth { 207 h := header.Ethernet(b) 208 b = b[header.EthernetMinimumSize:] 209 210 if a := h.SourceAddress(); a != laddr { 211 t.Fatalf("SourceAddress() = %v, want %v", a, laddr) 212 } 213 214 if a := h.DestinationAddress(); a != raddr { 215 t.Fatalf("DestinationAddress() = %v, want %v", a, raddr) 216 } 217 218 if et := h.Type(); et != proto { 219 t.Fatalf("Type() = %v, want %v", et, proto) 220 } 221 } 222 if len(b) != len(want) { 223 t.Fatalf("Read returned %v bytes, want %v", len(b), len(want)) 224 } 225 if !bytes.Equal(b, want) { 226 t.Fatalf("Read returned %x, want %x", b, want) 227 } 228 } 229 230 func TestWritePacket(t *testing.T) { 231 lengths := []int{0, 100, 1000} 232 eths := []bool{true, false} 233 gsos := []uint32{0, 32768} 234 235 for _, eth := range eths { 236 for _, plen := range lengths { 237 for _, gso := range gsos { 238 t.Run( 239 fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v", eth, plen, gso), 240 func(t *testing.T) { 241 testWritePacket(t, plen, eth, gso) 242 }, 243 ) 244 } 245 } 246 } 247 } 248 249 func TestPreserveSrcAddress(t *testing.T) { 250 baddr := tcpip.LinkAddress("\xcc\xbb\xaa\x77\x88\x99") 251 252 c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: true}) 253 defer c.cleanup() 254 255 // Set LocalLinkAddress in route to the value of the bridged address. 256 r := &stack.Route{ 257 RemoteLinkAddress: raddr, 258 LocalLinkAddress: baddr, 259 } 260 261 // WritePacket panics given a prependable with anything less than 262 // the minimum size of the ethernet header. 263 hdr := buffer.NewPrependable(header.EthernetMinimumSize) 264 if err := c.ep.WritePacket(r, nil /* gso */, proto, tcpip.PacketBuffer{ 265 Header: hdr, 266 Data: buffer.VectorisedView{}, 267 }); err != nil { 268 t.Fatalf("WritePacket failed: %v", err) 269 } 270 271 // Read from the FD, then compare with what we wrote. 272 b := make([]byte, mtu) 273 n, err := syscall.Read(c.fds[0], b) 274 if err != nil { 275 t.Fatalf("Read failed: %v", err) 276 } 277 b = b[:n] 278 h := header.Ethernet(b) 279 280 if a := h.SourceAddress(); a != baddr { 281 t.Fatalf("SourceAddress() = %v, want %v", a, baddr) 282 } 283 } 284 285 func TestDeliverPacket(t *testing.T) { 286 lengths := []int{100, 1000} 287 eths := []bool{true, false} 288 289 for _, eth := range eths { 290 for _, plen := range lengths { 291 t.Run(fmt.Sprintf("Eth=%v,PayloadLen=%v", eth, plen), func(t *testing.T) { 292 c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth}) 293 defer c.cleanup() 294 295 // Build packet. 296 b := make([]byte, plen) 297 all := b 298 for i := range b { 299 b[i] = uint8(rand.Intn(256)) 300 } 301 302 var hdr header.Ethernet 303 if !eth { 304 // So that it looks like an IPv4 packet. 305 b[0] = 0x40 306 } else { 307 hdr = make(header.Ethernet, header.EthernetMinimumSize) 308 hdr.Encode(&header.EthernetFields{ 309 SrcAddr: raddr, 310 DstAddr: laddr, 311 Type: proto, 312 }) 313 all = append(hdr, b...) 314 } 315 316 // Write packet via the file descriptor. 317 if _, err := syscall.Write(c.fds[0], all); err != nil { 318 t.Fatalf("Write failed: %v", err) 319 } 320 321 // Receive packet through the endpoint. 322 select { 323 case pi := <-c.ch: 324 want := packetInfo{ 325 raddr: raddr, 326 proto: proto, 327 contents: tcpip.PacketBuffer{ 328 Data: buffer.View(b).ToVectorisedView(), 329 LinkHeader: buffer.View(hdr), 330 }, 331 } 332 if !eth { 333 want.proto = header.IPv4ProtocolNumber 334 want.raddr = "" 335 } 336 // want.contents.Data will be a single 337 // view, so make pi do the same for the 338 // DeepEqual check. 339 pi.contents.Data = pi.contents.Data.ToView().ToVectorisedView() 340 if !reflect.DeepEqual(want, pi) { 341 t.Fatalf("Unexpected received packet: %+v, want %+v", pi, want) 342 } 343 case <-time.After(10 * time.Second): 344 t.Fatalf("Timed out waiting for packet") 345 } 346 }) 347 } 348 } 349 } 350 351 func TestBufConfigMaxLength(t *testing.T) { 352 got := 0 353 for _, i := range BufConfig { 354 got += i 355 } 356 want := header.MaxIPPacketSize // maximum TCP packet size 357 if got < want { 358 t.Errorf("total buffer size is invalid: got %d, want >= %d", got, want) 359 } 360 } 361 362 func TestBufConfigFirst(t *testing.T) { 363 // The stack assumes that the TCP/IP header is enterily contained in the first view. 364 // Therefore, the first view needs to be large enough to contain the maximum TCP/IP 365 // header, which is 120 bytes (60 bytes for IP + 60 bytes for TCP). 366 want := 120 367 got := BufConfig[0] 368 if got < want { 369 t.Errorf("first view has an invalid size: got %d, want >= %d", got, want) 370 } 371 } 372 373 var capLengthTestCases = []struct { 374 comment string 375 config []int 376 n int 377 wantUsed int 378 wantLengths []int 379 }{ 380 { 381 comment: "Single slice", 382 config: []int{2}, 383 n: 1, 384 wantUsed: 1, 385 wantLengths: []int{1}, 386 }, 387 { 388 comment: "Multiple slices", 389 config: []int{1, 2}, 390 n: 2, 391 wantUsed: 2, 392 wantLengths: []int{1, 1}, 393 }, 394 { 395 comment: "Entire buffer", 396 config: []int{1, 2}, 397 n: 3, 398 wantUsed: 2, 399 wantLengths: []int{1, 2}, 400 }, 401 { 402 comment: "Entire buffer but not on the last slice", 403 config: []int{1, 2, 3}, 404 n: 3, 405 wantUsed: 2, 406 wantLengths: []int{1, 2, 3}, 407 }, 408 } 409 410 func TestReadVDispatcherCapLength(t *testing.T) { 411 for _, c := range capLengthTestCases { 412 // fd does not matter for this test. 413 d := readVDispatcher{fd: -1, e: &endpoint{}} 414 d.views = make([]buffer.View, len(c.config)) 415 d.iovecs = make([]syscall.Iovec, len(c.config)) 416 d.allocateViews(c.config) 417 418 used := d.capViews(c.n, c.config) 419 if used != c.wantUsed { 420 t.Errorf("Test %q failed when calling capViews(%d, %v). Got %d. Want %d", c.comment, c.n, c.config, used, c.wantUsed) 421 } 422 lengths := make([]int, len(d.views)) 423 for i, v := range d.views { 424 lengths[i] = len(v) 425 } 426 if !reflect.DeepEqual(lengths, c.wantLengths) { 427 t.Errorf("Test %q failed when calling capViews(%d, %v). Got %v. Want %v", c.comment, c.n, c.config, lengths, c.wantLengths) 428 } 429 } 430 } 431 432 func TestRecvMMsgDispatcherCapLength(t *testing.T) { 433 for _, c := range capLengthTestCases { 434 d := recvMMsgDispatcher{ 435 fd: -1, // fd does not matter for this test. 436 e: &endpoint{}, 437 views: make([][]buffer.View, 1), 438 iovecs: make([][]syscall.Iovec, 1), 439 msgHdrs: make([]rawfile.MMsgHdr, 1), 440 } 441 442 for i, _ := range d.views { 443 d.views[i] = make([]buffer.View, len(c.config)) 444 } 445 for i := range d.iovecs { 446 d.iovecs[i] = make([]syscall.Iovec, len(c.config)) 447 } 448 for k, msgHdr := range d.msgHdrs { 449 msgHdr.Msg.Iov = &d.iovecs[k][0] 450 msgHdr.Msg.Iovlen = uint64(len(c.config)) 451 } 452 453 d.allocateViews(c.config) 454 455 used := d.capViews(0, c.n, c.config) 456 if used != c.wantUsed { 457 t.Errorf("Test %q failed when calling capViews(%d, %v). Got %d. Want %d", c.comment, c.n, c.config, used, c.wantUsed) 458 } 459 lengths := make([]int, len(d.views[0])) 460 for i, v := range d.views[0] { 461 lengths[i] = len(v) 462 } 463 if !reflect.DeepEqual(lengths, c.wantLengths) { 464 t.Errorf("Test %q failed when calling capViews(%d, %v). Got %v. Want %v", c.comment, c.n, c.config, lengths, c.wantLengths) 465 } 466 467 } 468 }