Merge remote-tracking branch 'upstream/master' into merge
Change-Id: Id51717d67c23beedf87f68ba8e67f7577e953727
diff --git a/AUTHORS b/AUTHORS
index cc86037..3ae667d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,13 +1,4 @@
-# This is the official list of Netstack authors for copyright purposes.
-# This file is distinct from the CONTRIBUTORS files.
-# See the latter for an explanation.
-
-# Names should be added to this file as one of
-# Organization's name
-# Individual's name <submission email address>
-# Individual's name <submission email address> <email2> <emailN>
-# See CONTRIBUTORS for the meaning of multiple email addresses.
-
-# Please keep the list sorted.
-
-Google Inc.
+# Netstack is part of the gVisor project. Please see the gVisor AUTHORS file
+# for an official list of authors:
+#
+# https://gvisor.googlesource.com/gvisor/+/refs/heads/master/AUTHORS
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
deleted file mode 100644
index 80c2d19..0000000
--- a/CONTRIBUTORS
+++ /dev/null
@@ -1,14 +0,0 @@
-# This is the official list of people who can contribute
-# (and typically have contributed) code to the Netstack repository.
-# The AUTHORS file lists the copyright holders; this file
-# lists people. For example, Google employees are listed here
-# but not in AUTHORS, because Google holds the copyright.
-
-Adin Scannell <ascannell@google.com>
-Ben Karas <benkaras@google.com>
-David Crawshaw <crawshaw@golang.org>
-Ian Gudger <igudger@google.com>
-Nicolas Lacasse <nlacasse@google.com>
-Sebastiano Spicuglia <spicuglia@google.com>
-Wedson Almeida Filho <wedsonaf@google.com>
-Zhaozhong Ni <nzz@google.com>
diff --git a/dhcp/client.go b/dhcp/client.go
index 02ea5e2..78ee4d0 100644
--- a/dhcp/client.go
+++ b/dhcp/client.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -120,7 +120,7 @@
// If the server sets a lease limit a timer is set to automatically
// renew it.
func (c *Client) Request(ctx context.Context, requestedAddr tcpip.Address) (cfg Config, reterr error) {
- // TODO: remove calls to {Add,Remove}Address when they're no
+ // TODO(b/127321246): remove calls to {Add,Remove}Address when they're no
// longer required to send and receive broadcast.
if err := c.stack.AddAddressWithOptions(c.nicid, ipv4.ProtocolNumber, tcpipHeader.IPv4Any, stack.NeverPrimaryEndpoint); err != nil && err != tcpip.ErrDuplicateAddress {
return Config{}, fmt.Errorf("dhcp: AddAddressWithOptions(): %s", err)
diff --git a/dhcp/dhcp.go b/dhcp/dhcp.go
index af1a07c..7057544 100644
--- a/dhcp/dhcp.go
+++ b/dhcp/dhcp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/dhcp/dhcp_string.go b/dhcp/dhcp_string.go
index a013940..6652e29 100644
--- a/dhcp/dhcp_string.go
+++ b/dhcp/dhcp_string.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/dhcp/dhcp_test.go b/dhcp/dhcp_test.go
index 6f6610f..2d4edb0 100644
--- a/dhcp/dhcp_test.go
+++ b/dhcp/dhcp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/dhcp/server.go b/dhcp/server.go
index 8b339a0..111faa2 100644
--- a/dhcp/server.go
+++ b/dhcp/server.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/gate/gate.go b/gate/gate.go
index 48122bf..bda6aae 100644
--- a/gate/gate.go
+++ b/gate/gate.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/gate/gate_test.go b/gate/gate_test.go
index 5c74773..83392a6 100644
--- a/gate/gate_test.go
+++ b/gate/gate_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/ilist/list.go b/ilist/list.go
index 51c9b6d..019caad 100644
--- a/ilist/list.go
+++ b/ilist/list.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/rand/rand.go b/rand/rand.go
index 593a143..a271478 100644
--- a/rand/rand.go
+++ b/rand/rand.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/rand/rand_linux.go b/rand/rand_linux.go
index 7ebe8f3..2b92db3 100644
--- a/rand/rand_linux.go
+++ b/rand/rand_linux.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/commit_amd64.s b/sleep/commit_amd64.s
index d08df7f..bc4ac2c 100644
--- a/sleep/commit_amd64.s
+++ b/sleep/commit_amd64.s
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/commit_asm.go b/sleep/commit_asm.go
index 90eef4c..35e2cc3 100644
--- a/sleep/commit_asm.go
+++ b/sleep/commit_asm.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/commit_noasm.go b/sleep/commit_noasm.go
index 967d22e..686b1da 100644
--- a/sleep/commit_noasm.go
+++ b/sleep/commit_noasm.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/empty.s b/sleep/empty.s
index 85d52cd..fb37360 100644
--- a/sleep/empty.s
+++ b/sleep/empty.s
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/sleep_test.go b/sleep/sleep_test.go
index 8feb9ff..130806c 100644
--- a/sleep/sleep_test.go
+++ b/sleep/sleep_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/sleep/sleep_unsafe.go b/sleep/sleep_unsafe.go
index 45fb6f0..62e0abc 100644
--- a/sleep/sleep_unsafe.go
+++ b/sleep/sleep_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/adapters/gonet/gonet.go b/tcpip/adapters/gonet/gonet.go
index 3d4a8f2..e407577 100644
--- a/tcpip/adapters/gonet/gonet.go
+++ b/tcpip/adapters/gonet/gonet.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -435,6 +435,28 @@
return nil
}
+// CloseRead shuts down the reading side of the TCP connection. Most callers
+// should just use Close.
+//
+// A TCP Half-Close is performed the same as CloseRead for *net.TCPConn.
+func (c *Conn) CloseRead() error {
+ if terr := c.ep.Shutdown(tcpip.ShutdownRead); terr != nil {
+ return c.newOpError("close", errors.New(terr.String()))
+ }
+ return nil
+}
+
+// CloseWrite shuts down the writing side of the TCP connection. Most callers
+// should just use Close.
+//
+// A TCP Half-Close is performed the same as CloseWrite for *net.TCPConn.
+func (c *Conn) CloseWrite() error {
+ if terr := c.ep.Shutdown(tcpip.ShutdownWrite); terr != nil {
+ return c.newOpError("close", errors.New(terr.String()))
+ }
+ return nil
+}
+
// LocalAddr implements net.Conn.LocalAddr.
func (c *Conn) LocalAddr() net.Addr {
a, err := c.ep.GetLocalAddress()
diff --git a/tcpip/adapters/gonet/gonet_test.go b/tcpip/adapters/gonet/gonet_test.go
index 5d1b8b5..9556af2 100644
--- a/tcpip/adapters/gonet/gonet_test.go
+++ b/tcpip/adapters/gonet/gonet_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io"
"net"
"reflect"
"strings"
@@ -222,6 +223,171 @@
sender.close()
}
+func TestCloseRead(t *testing.T) {
+ s, terr := newLoopbackStack()
+ if terr != nil {
+ t.Fatalf("newLoopbackStack() = %v", terr)
+ }
+
+ addr := tcpip.FullAddress{NICID, tcpip.Address(net.IPv4(169, 254, 10, 1).To4()), 11211}
+ s.AddAddress(NICID, ipv4.ProtocolNumber, addr.Addr)
+
+ fwd := tcp.NewForwarder(s, 30000, 10, func(r *tcp.ForwarderRequest) {
+ var wq waiter.Queue
+ ep, err := r.CreateEndpoint(&wq)
+ if err != nil {
+ t.Fatalf("r.CreateEndpoint() = %v", err)
+ }
+ defer ep.Close()
+ r.Complete(false)
+
+ c := NewConn(&wq, ep)
+
+ buf := make([]byte, 256)
+ n, e := c.Read(buf)
+ if e != nil || string(buf[:n]) != "abc123" {
+ t.Fatalf("c.Read() = (%d, %v), want (6, nil)", n, e)
+ }
+
+ if n, e = c.Write([]byte("abc123")); e != nil {
+ t.Errorf("c.Write() = (%d, %v), want (6, nil)", n, e)
+ }
+ })
+
+ s.SetTransportProtocolHandler(tcp.ProtocolNumber, fwd.HandlePacket)
+
+ tc, terr := connect(s, addr)
+ if terr != nil {
+ t.Fatalf("connect() = %v", terr)
+ }
+ c := NewConn(tc.wq, tc.ep)
+
+ if err := c.CloseRead(); err != nil {
+ t.Errorf("c.CloseRead() = %v", err)
+ }
+
+ buf := make([]byte, 256)
+ if n, err := c.Read(buf); err != io.EOF {
+ t.Errorf("c.Read() = (%d, %v), want (0, io.EOF)", n, err)
+ }
+
+ if n, err := c.Write([]byte("abc123")); n != 6 || err != nil {
+ t.Errorf("c.Write() = (%d, %v), want (6, nil)", n, err)
+ }
+}
+
+func TestCloseWrite(t *testing.T) {
+ s, terr := newLoopbackStack()
+ if terr != nil {
+ t.Fatalf("newLoopbackStack() = %v", terr)
+ }
+
+ addr := tcpip.FullAddress{NICID, tcpip.Address(net.IPv4(169, 254, 10, 1).To4()), 11211}
+ s.AddAddress(NICID, ipv4.ProtocolNumber, addr.Addr)
+
+ fwd := tcp.NewForwarder(s, 30000, 10, func(r *tcp.ForwarderRequest) {
+ var wq waiter.Queue
+ ep, err := r.CreateEndpoint(&wq)
+ if err != nil {
+ t.Fatalf("r.CreateEndpoint() = %v", err)
+ }
+ defer ep.Close()
+ r.Complete(false)
+
+ c := NewConn(&wq, ep)
+
+ n, e := c.Read(make([]byte, 256))
+ if n != 0 || e != io.EOF {
+ t.Errorf("c.Read() = (%d, %v), want (0, io.EOF)", n, e)
+ }
+
+ if n, e = c.Write([]byte("abc123")); n != 6 || e != nil {
+ t.Errorf("c.Write() = (%d, %v), want (6, nil)", n, e)
+ }
+ })
+
+ s.SetTransportProtocolHandler(tcp.ProtocolNumber, fwd.HandlePacket)
+
+ tc, terr := connect(s, addr)
+ if terr != nil {
+ t.Fatalf("connect() = %v", terr)
+ }
+ c := NewConn(tc.wq, tc.ep)
+
+ if err := c.CloseWrite(); err != nil {
+ t.Errorf("c.CloseWrite() = %v", err)
+ }
+
+ buf := make([]byte, 256)
+ n, err := c.Read(buf)
+ if err != nil || string(buf[:n]) != "abc123" {
+ t.Fatalf("c.Read() = (%d, %v), want (6, nil)", n, err)
+ }
+
+ n, err = c.Write([]byte("abc123"))
+ got, ok := err.(*net.OpError)
+ want := "endpoint is closed for send"
+ if n != 0 || !ok || got.Op != "write" || got.Err == nil || !strings.HasSuffix(got.Err.Error(), want) {
+ t.Errorf("c.Write() = (%d, %v), want (0, OpError(Op: write, Err: %s))", n, err, want)
+ }
+}
+
+func TestUDPForwarder(t *testing.T) {
+ s, terr := newLoopbackStack()
+ if terr != nil {
+ t.Fatalf("newLoopbackStack() = %v", terr)
+ }
+
+ ip1 := tcpip.Address(net.IPv4(169, 254, 10, 1).To4())
+ addr1 := tcpip.FullAddress{NICID, ip1, 11211}
+ s.AddAddress(NICID, ipv4.ProtocolNumber, ip1)
+ ip2 := tcpip.Address(net.IPv4(169, 254, 10, 2).To4())
+ addr2 := tcpip.FullAddress{NICID, ip2, 11311}
+ s.AddAddress(NICID, ipv4.ProtocolNumber, ip2)
+
+ done := make(chan struct{})
+ fwd := udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
+ defer close(done)
+
+ var wq waiter.Queue
+ ep, err := r.CreateEndpoint(&wq)
+ if err != nil {
+ t.Fatalf("r.CreateEndpoint() = %v", err)
+ }
+ defer ep.Close()
+
+ c := NewConn(&wq, ep)
+
+ buf := make([]byte, 256)
+ n, e := c.Read(buf)
+ if e != nil {
+ t.Errorf("c.Read() = %v", e)
+ }
+
+ if _, e := c.Write(buf[:n]); e != nil {
+ t.Errorf("c.Write() = %v", e)
+ }
+ })
+ s.SetTransportProtocolHandler(udp.ProtocolNumber, fwd.HandlePacket)
+
+ c2, err := NewPacketConn(s, addr2, ipv4.ProtocolNumber)
+ if err != nil {
+ t.Fatal("NewPacketConn(port 5):", err)
+ }
+
+ sent := "abc123"
+ sendAddr := fullToUDPAddr(addr1)
+ if n, err := c2.WriteTo([]byte(sent), sendAddr); err != nil || n != len(sent) {
+ t.Errorf("c1.WriteTo(%q, %v) = %d, %v, want = %d, %v", sent, sendAddr, n, err, len(sent), nil)
+ }
+
+ buf := make([]byte, 256)
+ n, recvAddr, err := c2.ReadFrom(buf)
+ if err != nil || recvAddr.String() != sendAddr.String() {
+ t.Errorf("c1.ReadFrom() = %d, %v, %v, want = %d, %v, %v", n, recvAddr, err, len(sent), sendAddr, nil)
+ }
+}
+
// TestDeadlineChange tests that changing the deadline affects currently blocked reads.
func TestDeadlineChange(t *testing.T) {
s, err := newLoopbackStack()
diff --git a/tcpip/buffer/prependable.go b/tcpip/buffer/prependable.go
index d3a9a0f..43cbbc7 100644
--- a/tcpip/buffer/prependable.go
+++ b/tcpip/buffer/prependable.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/buffer/view.go b/tcpip/buffer/view.go
index 43cbb94..1a9d407 100644
--- a/tcpip/buffer/view.go
+++ b/tcpip/buffer/view.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/buffer/view_test.go b/tcpip/buffer/view_test.go
index 74a0a96..ebc3a17 100644
--- a/tcpip/buffer/view_test.go
+++ b/tcpip/buffer/view_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/checker/checker.go b/tcpip/checker/checker.go
index 554a710..ba5a2c5 100644
--- a/tcpip/checker/checker.go
+++ b/tcpip/checker/checker.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/hash/jenkins/jenkins.go b/tcpip/hash/jenkins/jenkins.go
index e66d5f1..52c2223 100644
--- a/tcpip/hash/jenkins/jenkins.go
+++ b/tcpip/hash/jenkins/jenkins.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/hash/jenkins/jenkins_test.go b/tcpip/hash/jenkins/jenkins_test.go
index 9d86174..4c78b58 100644
--- a/tcpip/hash/jenkins/jenkins_test.go
+++ b/tcpip/hash/jenkins/jenkins_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/arp.go b/tcpip/header/arp.go
index dac9f04..03dc4b0 100644
--- a/tcpip/header/arp.go
+++ b/tcpip/header/arp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/checksum.go b/tcpip/header/checksum.go
index d18bd87..9698763 100644
--- a/tcpip/header/checksum.go
+++ b/tcpip/header/checksum.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/eth.go b/tcpip/header/eth.go
index b57bfbb..3c182fa 100644
--- a/tcpip/header/eth.go
+++ b/tcpip/header/eth.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/gue.go b/tcpip/header/gue.go
index 2ad1395..10d358c 100644
--- a/tcpip/header/gue.go
+++ b/tcpip/header/gue.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/icmpv4.go b/tcpip/header/icmpv4.go
index 7ee8ad8..75bc038 100644
--- a/tcpip/header/icmpv4.go
+++ b/tcpip/header/icmpv4.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/icmpv6.go b/tcpip/header/icmpv6.go
index d8e76d3..70a3901 100644
--- a/tcpip/header/icmpv6.go
+++ b/tcpip/header/icmpv6.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/interfaces.go b/tcpip/header/interfaces.go
index ffeaf61..bb65e38 100644
--- a/tcpip/header/interfaces.go
+++ b/tcpip/header/interfaces.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/ipv4.go b/tcpip/header/ipv4.go
index 5c06c93..f7b04be 100644
--- a/tcpip/header/ipv4.go
+++ b/tcpip/header/ipv4.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/ipv6.go b/tcpip/header/ipv6.go
index 76f932b..b190ea7 100644
--- a/tcpip/header/ipv6.go
+++ b/tcpip/header/ipv6.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/ipv6_fragment.go b/tcpip/header/ipv6_fragment.go
index 790951d..f0cf2f2 100644
--- a/tcpip/header/ipv6_fragment.go
+++ b/tcpip/header/ipv6_fragment.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/ipversion_test.go b/tcpip/header/ipversion_test.go
index 332bb38..e0c6134 100644
--- a/tcpip/header/ipversion_test.go
+++ b/tcpip/header/ipversion_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/tcp.go b/tcpip/header/tcp.go
index 4b0fc41..f990ebe 100644
--- a/tcpip/header/tcp.go
+++ b/tcpip/header/tcp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,16 +22,17 @@
"github.com/google/netstack/tcpip/seqnum"
)
+// These constants are the offsets of the respective fields in the TCP header.
const (
- srcPort = 0
- dstPort = 2
- seqNum = 4
- ackNum = 8
- dataOffset = 12
- tcpFlags = 13
- winSize = 14
- tcpChecksum = 16
- urgentPtr = 18
+ TCPSrcPortOffset = 0
+ TCPDstPortOffset = 2
+ TCPSeqNumOffset = 4
+ TCPAckNumOffset = 8
+ TCPDataOffset = 12
+ TCPFlagsOffset = 13
+ TCPWinSizeOffset = 14
+ TCPChecksumOffset = 16
+ TCPUrgentPtrOffset = 18
)
const (
@@ -179,27 +180,27 @@
// SourcePort returns the "source port" field of the tcp header.
func (b TCP) SourcePort() uint16 {
- return binary.BigEndian.Uint16(b[srcPort:])
+ return binary.BigEndian.Uint16(b[TCPSrcPortOffset:])
}
// DestinationPort returns the "destination port" field of the tcp header.
func (b TCP) DestinationPort() uint16 {
- return binary.BigEndian.Uint16(b[dstPort:])
+ return binary.BigEndian.Uint16(b[TCPDstPortOffset:])
}
// SequenceNumber returns the "sequence number" field of the tcp header.
func (b TCP) SequenceNumber() uint32 {
- return binary.BigEndian.Uint32(b[seqNum:])
+ return binary.BigEndian.Uint32(b[TCPSeqNumOffset:])
}
// AckNumber returns the "ack number" field of the tcp header.
func (b TCP) AckNumber() uint32 {
- return binary.BigEndian.Uint32(b[ackNum:])
+ return binary.BigEndian.Uint32(b[TCPAckNumOffset:])
}
// DataOffset returns the "data offset" field of the tcp header.
func (b TCP) DataOffset() uint8 {
- return (b[dataOffset] >> 4) * 4
+ return (b[TCPDataOffset] >> 4) * 4
}
// Payload returns the data in the tcp packet.
@@ -209,32 +210,32 @@
// Flags returns the flags field of the tcp header.
func (b TCP) Flags() uint8 {
- return b[tcpFlags]
+ return b[TCPFlagsOffset]
}
// WindowSize returns the "window size" field of the tcp header.
func (b TCP) WindowSize() uint16 {
- return binary.BigEndian.Uint16(b[winSize:])
+ return binary.BigEndian.Uint16(b[TCPWinSizeOffset:])
}
// Checksum returns the "checksum" field of the tcp header.
func (b TCP) Checksum() uint16 {
- return binary.BigEndian.Uint16(b[tcpChecksum:])
+ return binary.BigEndian.Uint16(b[TCPChecksumOffset:])
}
// SetSourcePort sets the "source port" field of the tcp header.
func (b TCP) SetSourcePort(port uint16) {
- binary.BigEndian.PutUint16(b[srcPort:], port)
+ binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], port)
}
// SetDestinationPort sets the "destination port" field of the tcp header.
func (b TCP) SetDestinationPort(port uint16) {
- binary.BigEndian.PutUint16(b[dstPort:], port)
+ binary.BigEndian.PutUint16(b[TCPDstPortOffset:], port)
}
// SetChecksum sets the checksum field of the tcp header.
func (b TCP) SetChecksum(checksum uint16) {
- binary.BigEndian.PutUint16(b[tcpChecksum:], checksum)
+ binary.BigEndian.PutUint16(b[TCPChecksumOffset:], checksum)
}
// CalculateChecksum calculates the checksum of the tcp segment.
@@ -258,20 +259,20 @@
}
func (b TCP) encodeSubset(seq, ack uint32, flags uint8, rcvwnd uint16) {
- binary.BigEndian.PutUint32(b[seqNum:], seq)
- binary.BigEndian.PutUint32(b[ackNum:], ack)
- b[tcpFlags] = flags
- binary.BigEndian.PutUint16(b[winSize:], rcvwnd)
+ binary.BigEndian.PutUint32(b[TCPSeqNumOffset:], seq)
+ binary.BigEndian.PutUint32(b[TCPAckNumOffset:], ack)
+ b[TCPFlagsOffset] = flags
+ binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
}
// Encode encodes all the fields of the tcp header.
func (b TCP) Encode(t *TCPFields) {
b.encodeSubset(t.SeqNum, t.AckNum, t.Flags, t.WindowSize)
- binary.BigEndian.PutUint16(b[srcPort:], t.SrcPort)
- binary.BigEndian.PutUint16(b[dstPort:], t.DstPort)
- b[dataOffset] = (t.DataOffset / 4) << 4
- binary.BigEndian.PutUint16(b[tcpChecksum:], t.Checksum)
- binary.BigEndian.PutUint16(b[urgentPtr:], t.UrgentPointer)
+ binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], t.SrcPort)
+ binary.BigEndian.PutUint16(b[TCPDstPortOffset:], t.DstPort)
+ b[TCPDataOffset] = (t.DataOffset / 4) << 4
+ binary.BigEndian.PutUint16(b[TCPChecksumOffset:], t.Checksum)
+ binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], t.UrgentPointer)
}
// EncodePartial updates a subset of the fields of the tcp header. It is useful
@@ -290,18 +291,13 @@
b.encodeSubset(seqnum, acknum, flags, rcvwnd)
// Add the contributions of the passed-in fields to the checksum.
- checksum = Checksum(b[seqNum:seqNum+8], checksum)
- checksum = Checksum(b[winSize:winSize+2], checksum)
+ checksum = Checksum(b[TCPSeqNumOffset:TCPSeqNumOffset+8], checksum)
+ checksum = Checksum(b[TCPWinSizeOffset:TCPWinSizeOffset+2], checksum)
// Encode the checksum.
b.SetChecksum(^checksum)
}
-// TCPChecksumOffset returns offset of the checksum field.
-func TCPChecksumOffset() uint16 {
- return tcpChecksum
-}
-
// ParseSynOptions parses the options received in a SYN segment and returns the
// relevant ones. opts should point to the option part of the TCP Header.
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
diff --git a/tcpip/header/tcp_test.go b/tcpip/header/tcp_test.go
index b360fd4..ebbdde0 100644
--- a/tcpip/header/tcp_test.go
+++ b/tcpip/header/tcp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/header/udp.go b/tcpip/header/udp.go
index 2639758..805c5d9 100644
--- a/tcpip/header/udp.go
+++ b/tcpip/header/udp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/channel/channel.go b/tcpip/link/channel/channel.go
index 1ef3d98..4997e41 100644
--- a/tcpip/link/channel/channel.go
+++ b/tcpip/link/channel/channel.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
Header buffer.View
Payload buffer.View
Proto tcpip.NetworkProtocolNumber
+ GSO *stack.GSO
}
// Endpoint is link layer endpoint that stores outbound packets in a channel
@@ -36,6 +37,7 @@
dispatcher stack.NetworkDispatcher
mtu uint32
linkAddr tcpip.LinkAddress
+ GSO bool
// C is where outbound packets are queued.
C chan PacketInfo
@@ -93,8 +95,17 @@
}
// Capabilities implements stack.LinkEndpoint.Capabilities.
-func (*Endpoint) Capabilities() stack.LinkEndpointCapabilities {
- return 0
+func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
+ caps := stack.LinkEndpointCapabilities(0)
+ if e.GSO {
+ caps |= stack.CapabilityGSO
+ }
+ return caps
+}
+
+// GSOMaxSize returns the maximum GSO packet size.
+func (*Endpoint) GSOMaxSize() uint32 {
+ return 1 << 15
}
// MaxHeaderLength returns the maximum size of the link layer header. Given it
@@ -109,11 +120,12 @@
}
// WritePacket stores outbound packets into the channel.
-func (e *Endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
+func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
p := PacketInfo{
Header: hdr.View(),
Proto: protocol,
Payload: payload.ToView(),
+ GSO: gso,
}
select {
diff --git a/tcpip/link/fdbased/endpoint.go b/tcpip/link/fdbased/endpoint.go
index e099482..1245493 100644
--- a/tcpip/link/fdbased/endpoint.go
+++ b/tcpip/link/fdbased/endpoint.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -122,28 +122,32 @@
FD int
MTU uint32
EthernetHeader bool
- ChecksumOffload bool
ClosedFunc func(*tcpip.Error)
Address tcpip.LinkAddress
SaveRestore bool
DisconnectOk bool
GSOMaxSize uint32
PacketDispatchMode PacketDispatchMode
+ TXChecksumOffload bool
+ RXChecksumOffload bool
}
// New creates a new fd-based endpoint.
//
// Makes fd non-blocking, but does not take ownership of fd, which must remain
// open for the lifetime of the returned endpoint.
-func New(opts *Options) tcpip.LinkEndpointID {
+func New(opts *Options) (tcpip.LinkEndpointID, error) {
if err := syscall.SetNonblock(opts.FD, true); err != nil {
- // TODO : replace panic with an error return.
- panic(fmt.Sprintf("syscall.SetNonblock(%v) failed: %v", opts.FD, err))
+ return 0, fmt.Errorf("syscall.SetNonblock(%v) failed: %v", opts.FD, err)
}
caps := stack.LinkEndpointCapabilities(0)
- if opts.ChecksumOffload {
- caps |= stack.CapabilityChecksumOffload
+ if opts.RXChecksumOffload {
+ caps |= stack.CapabilityRXChecksumOffload
+ }
+
+ if opts.TXChecksumOffload {
+ caps |= stack.CapabilityTXChecksumOffload
}
hdrSize := 0
@@ -170,27 +174,35 @@
packetDispatchMode: opts.PacketDispatchMode,
}
- if opts.GSOMaxSize != 0 && isSocketFD(opts.FD) {
- e.caps |= stack.CapabilityGSO
- e.gsoMaxSize = opts.GSOMaxSize
- }
- if isSocketFD(opts.FD) && e.packetDispatchMode == PacketMMap {
- if err := e.setupPacketRXRing(); err != nil {
- // TODO: replace panic with an error return.
- panic(fmt.Sprintf("e.setupPacketRXRing failed: %v", err))
- }
- e.inboundDispatcher = e.packetMMapDispatch
- return stack.RegisterLinkEndpoint(e)
- }
-
- // For non-socket FDs we read one packet a time (e.g. TAP devices)
+ // For non-socket FDs we read one packet a time (e.g. TAP devices).
msgsPerRecv := 1
e.inboundDispatcher = e.dispatch
- // If the provided FD is a socket then we optimize packet reads by
- // using recvmmsg() instead of read() to read packets in a batch.
- if isSocketFD(opts.FD) && e.packetDispatchMode == RecvMMsg {
- e.inboundDispatcher = e.recvMMsgDispatch
- msgsPerRecv = MaxMsgsPerRecv
+
+ isSocket, err := isSocketFD(opts.FD)
+ if err != nil {
+ return 0, err
+ }
+ if isSocket {
+ if opts.GSOMaxSize != 0 {
+ e.caps |= stack.CapabilityGSO
+ e.gsoMaxSize = opts.GSOMaxSize
+ }
+
+ switch e.packetDispatchMode {
+ case PacketMMap:
+ if err := e.setupPacketRXRing(); err != nil {
+ return 0, fmt.Errorf("e.setupPacketRXRing failed: %v", err)
+ }
+ e.inboundDispatcher = e.packetMMapDispatch
+ return stack.RegisterLinkEndpoint(e), nil
+
+ case RecvMMsg:
+ // If the provided FD is a socket then we optimize
+ // packet reads by using recvmmsg() instead of read() to
+ // read packets in a batch.
+ e.inboundDispatcher = e.recvMMsgDispatch
+ msgsPerRecv = MaxMsgsPerRecv
+ }
}
e.views = make([][]buffer.View, msgsPerRecv)
@@ -212,16 +224,15 @@
e.msgHdrs[i].Msg.Iovlen = uint64(iovLen)
}
- return stack.RegisterLinkEndpoint(e)
+ return stack.RegisterLinkEndpoint(e), nil
}
-func isSocketFD(fd int) bool {
+func isSocketFD(fd int) (bool, error) {
var stat syscall.Stat_t
if err := syscall.Fstat(fd, &stat); err != nil {
- // TODO : replace panic with an error return.
- panic(fmt.Sprintf("syscall.Fstat(%v,...) failed: %v", fd, err))
+ return false, fmt.Errorf("syscall.Fstat(%v,...) failed: %v", fd, err)
}
- return (stat.Mode & syscall.S_IFSOCK) == syscall.S_IFSOCK
+ return (stat.Mode & syscall.S_IFSOCK) == syscall.S_IFSOCK, nil
}
// Attach launches the goroutine that reads packets from the file descriptor and
@@ -527,12 +538,13 @@
}
// NewInjectable creates a new fd-based InjectableEndpoint.
-func NewInjectable(fd int, mtu uint32) (tcpip.LinkEndpointID, *InjectableEndpoint) {
+func NewInjectable(fd int, mtu uint32, capabilities stack.LinkEndpointCapabilities) (tcpip.LinkEndpointID, *InjectableEndpoint) {
syscall.SetNonblock(fd, true)
e := &InjectableEndpoint{endpoint: endpoint{
- fd: fd,
- mtu: mtu,
+ fd: fd,
+ mtu: mtu,
+ caps: capabilities,
}}
return stack.RegisterLinkEndpoint(e), e
diff --git a/tcpip/link/fdbased/endpoint_test.go b/tcpip/link/fdbased/endpoint_test.go
index c7be8b2..a029a80 100644
--- a/tcpip/link/fdbased/endpoint_test.go
+++ b/tcpip/link/fdbased/endpoint_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -68,7 +68,11 @@
}
opt.FD = fds[1]
- ep := stack.FindLinkEndpoint(New(opt)).(*endpoint)
+ epID, err := New(opt)
+ if err != nil {
+ t.Fatalf("Failed to create FD endpoint: %v", err)
+ }
+ ep := stack.FindLinkEndpoint(epID).(*endpoint)
c := &context{
t: t,
@@ -162,6 +166,7 @@
CsumOffset: csumOffset,
MSS: gsoMSS,
MaxSize: gsoMaxSize,
+ L3HdrLen: header.IPv4MaximumHeaderSize,
}
}
if err := c.ep.WritePacket(r, gso, hdr, payload.ToVectorisedView(), proto); err != nil {
diff --git a/tcpip/link/fdbased/endpoint_unsafe.go b/tcpip/link/fdbased/endpoint_unsafe.go
index 36e7fe5..97a477b 100644
--- a/tcpip/link/fdbased/endpoint_unsafe.go
+++ b/tcpip/link/fdbased/endpoint_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/fdbased/mmap.go b/tcpip/link/fdbased/mmap.go
index 1c9af5a..14b0d52 100644
--- a/tcpip/link/fdbased/mmap.go
+++ b/tcpip/link/fdbased/mmap.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/fdbased/mmap_amd64_unsafe.go b/tcpip/link/fdbased/mmap_amd64_unsafe.go
index 9687816..8080113 100644
--- a/tcpip/link/fdbased/mmap_amd64_unsafe.go
+++ b/tcpip/link/fdbased/mmap_amd64_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
import (
"encoding/binary"
"fmt"
+ "sync/atomic"
"syscall"
"unsafe"
@@ -42,7 +43,12 @@
//
// Memory allocated for the ring buffer: tpBlockSize * tpBlockNR = 2 MiB
//
-// NOTE: Frames need to be aligned at 16 byte boundaries.
+// NOTE:
+// Frames need to be aligned at 16 byte boundaries.
+// BlockSize needs to be page aligned.
+//
+// For details see PACKET_MMAP setting constraints in
+// https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
const (
tpFrameSize = 65536 + 128
tpBlockSize = tpFrameSize * 32
@@ -81,12 +87,22 @@
tpUSecOffset = 24
)
+// tpStatus returns the frame status field.
+// The status is concurrently updated by the kernel as a result we must
+// use atomic operations to prevent races.
func (t tPacketHdr) tpStatus() uint32 {
- return binary.LittleEndian.Uint32(t[tpStatusOffset:])
+ hdr := unsafe.Pointer(&t[0])
+ statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset))
+ return atomic.LoadUint32((*uint32)(statusPtr))
}
+// setTPStatus set's the frame status to the provided status.
+// The status is concurrently updated by the kernel as a result we must
+// use atomic operations to prevent races.
func (t tPacketHdr) setTPStatus(status uint32) {
- binary.LittleEndian.PutUint32(t[tpStatusOffset:], status)
+ hdr := unsafe.Pointer(&t[0])
+ statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset))
+ atomic.StoreUint32((*uint32)(statusPtr), status)
}
func (t tPacketHdr) tpLen() uint32 {
@@ -118,6 +134,10 @@
}
func (e *endpoint) setupPacketRXRing() error {
+ pageSize := unix.Getpagesize()
+ if tpBlockSize%pageSize != 0 {
+ return fmt.Errorf("tpBlockSize: %d is not page aligned, pagesize: %d", tpBlockSize, pageSize)
+ }
tReq := tPacketReq{
tpBlockSize: uint32(tpBlockSize),
tpBlockNR: uint32(tpBlockNR),
@@ -139,8 +159,8 @@
}
func (e *endpoint) readMMappedPacket() ([]byte, *tcpip.Error) {
- hdr := (tPacketHdr)(e.ringBuffer[0+e.ringOffset*tpFrameSize:])
- for (hdr.tpStatus() & tpStatusUser) == 0 {
+ hdr := (tPacketHdr)(e.ringBuffer[e.ringOffset*tpFrameSize:])
+ for hdr.tpStatus()&tpStatusUser == 0 {
event := rawfile.PollEvent{
FD: int32(e.fd),
Events: unix.POLLIN | unix.POLLERR,
@@ -153,9 +173,11 @@
return nil, rawfile.TranslateErrno(errno)
}
if hdr.tpStatus()&tpStatusCopy != 0 {
- continue
- }
- if hdr.tpStatus()&tpStatusLosing != 0 {
+ // This frame is truncated so skip it after flipping the
+ // buffer to the kernel.
+ hdr.setTPStatus(tpStatusKernel)
+ e.ringOffset = (e.ringOffset + 1) % tpFrameNR
+ hdr = (tPacketHdr)(e.ringBuffer[e.ringOffset*tpFrameSize:])
continue
}
}
diff --git a/tcpip/link/loopback/loopback.go b/tcpip/link/loopback/loopback.go
index 25ab69b..e4a178e 100644
--- a/tcpip/link/loopback/loopback.go
+++ b/tcpip/link/loopback/loopback.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@
// Capabilities implements stack.LinkEndpoint.Capabilities. Loopback advertises
// itself as supporting checksum offload, but in reality it's just omitted.
func (*endpoint) Capabilities() stack.LinkEndpointCapabilities {
- return stack.CapabilityChecksumOffload | stack.CapabilitySaveRestore | stack.CapabilityLoopback
+ return stack.CapabilityRXChecksumOffload | stack.CapabilityTXChecksumOffload | stack.CapabilitySaveRestore | stack.CapabilityLoopback
}
// MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength. Given that the
diff --git a/tcpip/link/muxed/injectable.go b/tcpip/link/muxed/injectable.go
index cd08bfb..ece764f 100644
--- a/tcpip/link/muxed/injectable.go
+++ b/tcpip/link/muxed/injectable.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -105,7 +105,7 @@
}
// NewInjectableEndpoint creates a new multi-endpoint injectable endpoint.
-func NewInjectableEndpoint(routes map[tcpip.Address]stack.InjectableLinkEndpoint, mtu uint32) (tcpip.LinkEndpointID, *InjectableEndpoint) {
+func NewInjectableEndpoint(routes map[tcpip.Address]stack.InjectableLinkEndpoint) (tcpip.LinkEndpointID, *InjectableEndpoint) {
e := &InjectableEndpoint{
routes: routes,
}
diff --git a/tcpip/link/muxed/injectable_test.go b/tcpip/link/muxed/injectable_test.go
index 2d4e167..0e292af 100644
--- a/tcpip/link/muxed/injectable_test.go
+++ b/tcpip/link/muxed/injectable_test.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -87,8 +87,8 @@
if err != nil {
t.Fatal("Failed to create socket pair:", err)
}
- _, underlyingEndpoint := fdbased.NewInjectable(pair[1], 6500)
+ _, underlyingEndpoint := fdbased.NewInjectable(pair[1], 6500, stack.CapabilityNone)
routes := map[tcpip.Address]stack.InjectableLinkEndpoint{dstIP: underlyingEndpoint}
- _, endpoint := NewInjectableEndpoint(routes, 6500)
+ _, endpoint := NewInjectableEndpoint(routes)
return endpoint, os.NewFile(uintptr(pair[0]), "test route end"), dstIP
}
diff --git a/tcpip/link/rawfile/blockingpoll_amd64.s b/tcpip/link/rawfile/blockingpoll_amd64.s
index 9dade54..b541315 100644
--- a/tcpip/link/rawfile/blockingpoll_amd64.s
+++ b/tcpip/link/rawfile/blockingpoll_amd64.s
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go b/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go
index 3ba96a1..0b51982 100644
--- a/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go
+++ b/tcpip/link/rawfile/blockingpoll_amd64_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/rawfile/blockingpoll_unsafe.go b/tcpip/link/rawfile/blockingpoll_unsafe.go
index 94ddad8..4eab77c 100644
--- a/tcpip/link/rawfile/blockingpoll_unsafe.go
+++ b/tcpip/link/rawfile/blockingpoll_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/rawfile/errors.go b/tcpip/link/rawfile/errors.go
index a247a78..312645a 100644
--- a/tcpip/link/rawfile/errors.go
+++ b/tcpip/link/rawfile/errors.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/rawfile/rawfile_unsafe.go b/tcpip/link/rawfile/rawfile_unsafe.go
index 71f8c7b..41f8562 100644
--- a/tcpip/link/rawfile/rawfile_unsafe.go
+++ b/tcpip/link/rawfile/rawfile_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/pipe/pipe.go b/tcpip/link/sharedmem/pipe/pipe.go
index e014324..74c9f03 100644
--- a/tcpip/link/sharedmem/pipe/pipe.go
+++ b/tcpip/link/sharedmem/pipe/pipe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/pipe/pipe_test.go b/tcpip/link/sharedmem/pipe/pipe_test.go
index 30742cc..59ef69a 100644
--- a/tcpip/link/sharedmem/pipe/pipe_test.go
+++ b/tcpip/link/sharedmem/pipe/pipe_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/pipe/pipe_unsafe.go b/tcpip/link/sharedmem/pipe/pipe_unsafe.go
index f491d74..62d1702 100644
--- a/tcpip/link/sharedmem/pipe/pipe_unsafe.go
+++ b/tcpip/link/sharedmem/pipe/pipe_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/pipe/rx.go b/tcpip/link/sharedmem/pipe/rx.go
index 8d641c7..f22e533 100644
--- a/tcpip/link/sharedmem/pipe/rx.go
+++ b/tcpip/link/sharedmem/pipe/rx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/pipe/tx.go b/tcpip/link/sharedmem/pipe/tx.go
index e75175d..9841eb2 100644
--- a/tcpip/link/sharedmem/pipe/tx.go
+++ b/tcpip/link/sharedmem/pipe/tx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/queue/queue_test.go b/tcpip/link/sharedmem/queue/queue_test.go
index 5853c77..f65f561 100644
--- a/tcpip/link/sharedmem/queue/queue_test.go
+++ b/tcpip/link/sharedmem/queue/queue_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/queue/rx.go b/tcpip/link/sharedmem/queue/rx.go
index 0f1efe8..b75bd6d 100644
--- a/tcpip/link/sharedmem/queue/rx.go
+++ b/tcpip/link/sharedmem/queue/rx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/queue/tx.go b/tcpip/link/sharedmem/queue/tx.go
index 5e93dee..099bfc8 100644
--- a/tcpip/link/sharedmem/queue/tx.go
+++ b/tcpip/link/sharedmem/queue/tx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/rx.go b/tcpip/link/sharedmem/rx.go
index 5ac511e..9808422 100644
--- a/tcpip/link/sharedmem/rx.go
+++ b/tcpip/link/sharedmem/rx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/sharedmem.go b/tcpip/link/sharedmem/sharedmem.go
index b4ef64f..7fa69ed 100644
--- a/tcpip/link/sharedmem/sharedmem.go
+++ b/tcpip/link/sharedmem/sharedmem.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/sharedmem_test.go b/tcpip/link/sharedmem/sharedmem_test.go
index eb30bfa..a329ff8 100644
--- a/tcpip/link/sharedmem/sharedmem_test.go
+++ b/tcpip/link/sharedmem/sharedmem_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/sharedmem_unsafe.go b/tcpip/link/sharedmem/sharedmem_unsafe.go
index b91adba..f7e816a 100644
--- a/tcpip/link/sharedmem/sharedmem_unsafe.go
+++ b/tcpip/link/sharedmem/sharedmem_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sharedmem/tx.go b/tcpip/link/sharedmem/tx.go
index c986971..0f5af2e 100644
--- a/tcpip/link/sharedmem/tx.go
+++ b/tcpip/link/sharedmem/tx.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sniffer/pcap.go b/tcpip/link/sniffer/pcap.go
index 3d0d8d8..c16c196 100644
--- a/tcpip/link/sniffer/pcap.go
+++ b/tcpip/link/sniffer/pcap.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/sniffer/sniffer.go b/tcpip/link/sniffer/sniffer.go
index eb964e3..473f756 100644
--- a/tcpip/link/sniffer/sniffer.go
+++ b/tcpip/link/sniffer/sniffer.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/tun/tun_unsafe.go b/tcpip/link/tun/tun_unsafe.go
index e4c589d..09ca9b5 100644
--- a/tcpip/link/tun/tun_unsafe.go
+++ b/tcpip/link/tun/tun_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/waitable/waitable.go b/tcpip/link/waitable/waitable.go
index 8555626..56a38e1 100644
--- a/tcpip/link/waitable/waitable.go
+++ b/tcpip/link/waitable/waitable.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/link/waitable/waitable_test.go b/tcpip/link/waitable/waitable_test.go
index a2f1904..49c37ee 100644
--- a/tcpip/link/waitable/waitable_test.go
+++ b/tcpip/link/waitable/waitable_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/arp/arp.go b/tcpip/network/arp/arp.go
index 7238abc..09ecb1c 100644
--- a/tcpip/network/arp/arp.go
+++ b/tcpip/network/arp/arp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/arp/arp_test.go b/tcpip/network/arp/arp_test.go
index a830432..187dfa2 100644
--- a/tcpip/network/arp/arp_test.go
+++ b/tcpip/network/arp/arp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/frag_heap.go b/tcpip/network/fragmentation/frag_heap.go
index 0498670..0ec88ce 100644
--- a/tcpip/network/fragmentation/frag_heap.go
+++ b/tcpip/network/fragmentation/frag_heap.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/frag_heap_test.go b/tcpip/network/fragmentation/frag_heap_test.go
index 5dd7c67..a8c8131 100644
--- a/tcpip/network/fragmentation/frag_heap_test.go
+++ b/tcpip/network/fragmentation/frag_heap_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/fragmentation.go b/tcpip/network/fragmentation/fragmentation.go
index 622019e..e2873a4 100644
--- a/tcpip/network/fragmentation/fragmentation.go
+++ b/tcpip/network/fragmentation/fragmentation.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/fragmentation_test.go b/tcpip/network/fragmentation/fragmentation_test.go
index 4758a2d..f3d2ba0 100644
--- a/tcpip/network/fragmentation/fragmentation_test.go
+++ b/tcpip/network/fragmentation/fragmentation_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/reassembler.go b/tcpip/network/fragmentation/reassembler.go
index f8d8857..19b61de 100644
--- a/tcpip/network/fragmentation/reassembler.go
+++ b/tcpip/network/fragmentation/reassembler.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/fragmentation/reassembler_test.go b/tcpip/network/fragmentation/reassembler_test.go
index a2bc970..7eee071 100644
--- a/tcpip/network/fragmentation/reassembler_test.go
+++ b/tcpip/network/fragmentation/reassembler_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/hash/hash.go b/tcpip/network/hash/hash.go
index b0672f3..144a9fa 100644
--- a/tcpip/network/hash/hash.go
+++ b/tcpip/network/hash/hash.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/ip_test.go b/tcpip/network/ip_test.go
index 0cf365b..60c5ac3 100644
--- a/tcpip/network/ip_test.go
+++ b/tcpip/network/ip_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/ipv4/icmp.go b/tcpip/network/ipv4/icmp.go
index d17da80..db8dbab 100644
--- a/tcpip/network/ipv4/icmp.go
+++ b/tcpip/network/ipv4/icmp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@
}
h := header.ICMPv4(v)
- // TODO: Meaningfully handle all ICMP types.
+ // TODO(b/112892170): Meaningfully handle all ICMP types.
switch h.Type() {
case header.ICMPv4Echo:
received.Echo.Increment()
diff --git a/tcpip/network/ipv4/ipv4.go b/tcpip/network/ipv4/ipv4.go
index d336bce..929a72a 100644
--- a/tcpip/network/ipv4/ipv4.go
+++ b/tcpip/network/ipv4/ipv4.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/ipv4/ipv4_test.go b/tcpip/network/ipv4/ipv4_test.go
index 3846839..64531e4 100644
--- a/tcpip/network/ipv4/ipv4_test.go
+++ b/tcpip/network/ipv4/ipv4_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/ipv6/icmp.go b/tcpip/network/ipv6/icmp.go
index 9a526fa..c544a55 100644
--- a/tcpip/network/ipv6/icmp.go
+++ b/tcpip/network/ipv6/icmp.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@
}
h := header.ICMPv6(v)
- // TODO: Meaningfully handle all ICMP types.
+ // TODO(b/112892170): Meaningfully handle all ICMP types.
switch h.Type() {
case header.ICMPv6PacketTooBig:
received.PacketTooBig.Increment()
@@ -247,7 +247,7 @@
DstAddr: r.RemoteAddress,
})
- // TODO: count this in ICMP stats.
+ // TODO(stijlist): count this in ICMP stats.
return linkEP.WritePacket(r, nil /* gso */, hdr, buffer.VectorisedView{}, ProtocolNumber)
}
diff --git a/tcpip/network/ipv6/icmp_test.go b/tcpip/network/ipv6/icmp_test.go
index fc76bdd..d0445e5 100644
--- a/tcpip/network/ipv6/icmp_test.go
+++ b/tcpip/network/ipv6/icmp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/network/ipv6/ipv6.go b/tcpip/network/ipv6/ipv6.go
index 92d851d..8e70baa 100644
--- a/tcpip/network/ipv6/ipv6.go
+++ b/tcpip/network/ipv6/ipv6.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/ports/ports.go b/tcpip/ports/ports.go
index eb0ada8..e9445ab 100644
--- a/tcpip/ports/ports.go
+++ b/tcpip/ports/ports.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/ports/ports_test.go b/tcpip/ports/ports_test.go
index bee0e6d..6fbc60e 100644
--- a/tcpip/ports/ports_test.go
+++ b/tcpip/ports/ports_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/sample/tun_tcp_connect/main.go b/tcpip/sample/tun_tcp_connect/main.go
index d113c1d..6cc61dc 100644
--- a/tcpip/sample/tun_tcp_connect/main.go
+++ b/tcpip/sample/tun_tcp_connect/main.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -137,7 +137,10 @@
log.Fatal(err)
}
- linkID := fdbased.New(&fdbased.Options{FD: fd, MTU: mtu})
+ linkID, err := fdbased.New(&fdbased.Options{FD: fd, MTU: mtu})
+ if err != nil {
+ log.Fatal(err)
+ }
if err := s.CreateNIC(1, sniffer.New(linkID)); err != nil {
log.Fatal(err)
}
diff --git a/tcpip/sample/tun_tcp_echo/main.go b/tcpip/sample/tun_tcp_echo/main.go
index 40550d8..9f0bae8 100644
--- a/tcpip/sample/tun_tcp_echo/main.go
+++ b/tcpip/sample/tun_tcp_echo/main.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -128,12 +128,15 @@
log.Fatal(err)
}
- linkID := fdbased.New(&fdbased.Options{
+ linkID, err := fdbased.New(&fdbased.Options{
FD: fd,
MTU: mtu,
EthernetHeader: *tap,
Address: tcpip.LinkAddress(maddr),
})
+ if err != nil {
+ log.Fatal(err)
+ }
if err := s.CreateNIC(1, linkID); err != nil {
log.Fatal(err)
}
diff --git a/tcpip/seqnum/seqnum.go b/tcpip/seqnum/seqnum.go
index f2b9888..b40a3c2 100644
--- a/tcpip/seqnum/seqnum.go
+++ b/tcpip/seqnum/seqnum.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/stack/linkaddrcache.go b/tcpip/stack/linkaddrcache.go
index e52b815..ae58b6a 100644
--- a/tcpip/stack/linkaddrcache.go
+++ b/tcpip/stack/linkaddrcache.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/stack/linkaddrcache_test.go b/tcpip/stack/linkaddrcache_test.go
index 358e91e..59eae3d 100644
--- a/tcpip/stack/linkaddrcache_test.go
+++ b/tcpip/stack/linkaddrcache_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/stack/nic.go b/tcpip/stack/nic.go
index eefdc51..3da0db7 100644
--- a/tcpip/stack/nic.go
+++ b/tcpip/stack/nic.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@
primary map[tcpip.NetworkProtocolNumber]*ilist.List
endpoints map[NetworkEndpointID]*referencedNetworkEndpoint
subnets []tcpip.Subnet
+ mcastJoins map[NetworkEndpointID]int32
stats NICStats
}
@@ -79,14 +80,15 @@
func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback bool) *NIC {
return &NIC{
- stack: stack,
- id: id,
- name: name,
- linkEP: ep,
- loopback: loopback,
- demux: newTransportDemuxer(stack),
- primary: make(map[tcpip.NetworkProtocolNumber]*ilist.List),
- endpoints: make(map[NetworkEndpointID]*referencedNetworkEndpoint),
+ stack: stack,
+ id: id,
+ name: name,
+ linkEP: ep,
+ loopback: loopback,
+ demux: newTransportDemuxer(stack),
+ primary: make(map[tcpip.NetworkProtocolNumber]*ilist.List),
+ endpoints: make(map[NetworkEndpointID]*referencedNetworkEndpoint),
+ mcastJoins: make(map[NetworkEndpointID]int32),
stats: NICStats{
Tx: DirectionStats{
Packets: &tcpip.StatCounter{},
@@ -176,7 +178,7 @@
for e := list.Front(); e != nil; e = e.Next() {
r := e.(*referencedNetworkEndpoint)
- // TODO: allow broadcast address when SO_BROADCAST is set.
+ // TODO(crawshaw): allow broadcast address when SO_BROADCAST is set.
switch r.ep.ID().LocalAddress {
case header.IPv4Broadcast, header.IPv4Any:
continue
@@ -384,23 +386,65 @@
n.mu.Unlock()
}
-// RemoveAddress removes an address from n.
-func (n *NIC) RemoveAddress(addr tcpip.Address) *tcpip.Error {
- n.mu.Lock()
+func (n *NIC) removeAddressLocked(addr tcpip.Address) *tcpip.Error {
r := n.endpoints[NetworkEndpointID{addr}]
if r == nil || !r.holdsInsertRef {
- n.mu.Unlock()
return tcpip.ErrBadLocalAddress
}
r.holdsInsertRef = false
- n.mu.Unlock()
- r.decRef()
+ r.decRefLocked()
return nil
}
+// RemoveAddress removes an address from n.
+func (n *NIC) RemoveAddress(addr tcpip.Address) *tcpip.Error {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ return n.removeAddressLocked(addr)
+}
+
+// joinGroup adds a new endpoint for the given multicast address, if none
+// exists yet. Otherwise it just increments its count.
+func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) *tcpip.Error {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+
+ id := NetworkEndpointID{addr}
+ joins := n.mcastJoins[id]
+ if joins == 0 {
+ if _, err := n.addAddressLocked(protocol, addr, NeverPrimaryEndpoint, false); err != nil {
+ return err
+ }
+ }
+ n.mcastJoins[id] = joins + 1
+ return nil
+}
+
+// leaveGroup decrements the count for the given multicast address, and when it
+// reaches zero removes the endpoint for this address.
+func (n *NIC) leaveGroup(addr tcpip.Address) *tcpip.Error {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+
+ id := NetworkEndpointID{addr}
+ joins := n.mcastJoins[id]
+ switch joins {
+ case 0:
+ // There are no joins with this address on this NIC.
+ return tcpip.ErrBadLocalAddress
+ case 1:
+ // This is the last one, clean up.
+ if err := n.removeAddressLocked(addr); err != nil {
+ return err
+ }
+ }
+ n.mcastJoins[id] = joins - 1
+ return nil
+}
+
// DeliverNetworkPacket finds the appropriate network protocol endpoint and
// hands the packet over for further processing. This function is called when
// the NIC receives a packet from the physical interface.
@@ -476,7 +520,7 @@
n.mu.RUnlock()
if ok && ref.tryIncRef() {
r.RemoteAddress = src
- // TODO: Update the source NIC as well.
+ // TODO(b/123449044): Update the source NIC as well.
ref.ep.HandlePacket(&r, vv)
ref.decRef()
} else {
@@ -485,7 +529,7 @@
hdr := buffer.NewPrependableFromView(vv.First())
vv.RemoveFirst()
- // TODO: use route.WritePacket.
+ // TODO(b/128629022): use route.WritePacket.
if err := n.linkEP.WritePacket(&r, nil /* gso */, hdr, vv, protocol); err != nil {
r.Stats().IP.OutgoingPacketErrors.Increment()
} else {
@@ -644,6 +688,14 @@
}
}
+// decRefLocked is the same as decRef but assumes that the NIC.mu mutex is
+// locked.
+func (r *referencedNetworkEndpoint) decRefLocked() {
+ if atomic.AddInt32(&r.refs, -1) == 0 {
+ r.nic.removeEndpointLocked(r)
+ }
+}
+
// incRef increments the ref count. It must only be called when the caller is
// known to be holding a reference to the endpoint, otherwise tryIncRef should
// be used.
diff --git a/tcpip/stack/registration.go b/tcpip/stack/registration.go
index 4c14efc..78135dc 100644
--- a/tcpip/stack/registration.go
+++ b/tcpip/stack/registration.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -64,13 +64,24 @@
type TransportEndpoint interface {
// HandlePacket is called by the stack when new packets arrive to
// this transport endpoint.
- HandlePacket(r *Route, id TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView)
+ HandlePacket(r *Route, id TransportEndpointID, vv buffer.VectorisedView)
// HandleControlPacket is called by the stack when new control (e.g.,
// ICMP) packets arrive to this transport endpoint.
HandleControlPacket(id TransportEndpointID, typ ControlType, extra uint32, vv buffer.VectorisedView)
}
+// RawTransportEndpoint is the interface that needs to be implemented by raw
+// transport protocol endpoints. RawTransportEndpoints receive the entire
+// packet - including the link, network, and transport headers - as delivered
+// to netstack.
+type RawTransportEndpoint interface {
+ // HandlePacket is called by the stack when new packets arrive to
+ // this transport endpoint. The packet contains all data from the link
+ // layer up.
+ HandlePacket(r *Route, netHeader buffer.View, packet buffer.VectorisedView)
+}
+
// TransportProtocol is the interface that needs to be implemented by transport
// protocols (e.g., tcp, udp) that want to be part of the networking stack.
type TransportProtocol interface {
@@ -221,7 +232,15 @@
// The following are the supported link endpoint capabilities.
const (
- CapabilityChecksumOffload LinkEndpointCapabilities = 1 << iota
+ CapabilityNone LinkEndpointCapabilities = 0
+ // CapabilityTXChecksumOffload indicates that the link endpoint supports
+ // checksum computation for outgoing packets and the stack can skip
+ // computing checksums when sending packets.
+ CapabilityTXChecksumOffload LinkEndpointCapabilities = 1 << iota
+ // CapabilityRXChecksumOffload indicates that the link endpoint supports
+ // checksum verification on received packets and that it's safe for the
+ // stack to skip checksum verification.
+ CapabilityRXChecksumOffload
CapabilityResolutionRequired
CapabilitySaveRestore
CapabilityDisconnectOk
diff --git a/tcpip/stack/route.go b/tcpip/stack/route.go
index e99da39..f4414dc 100644
--- a/tcpip/stack/route.go
+++ b/tcpip/stack/route.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/stack/stack.go b/tcpip/stack/stack.go
index 02640ea..a83659b 100644
--- a/tcpip/stack/stack.go
+++ b/tcpip/stack/stack.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -291,6 +291,10 @@
linkAddrCache *linkAddrCache
+ // raw indicates whether raw sockets may be created. It is set during
+ // Stack creation and is immutable.
+ raw bool
+
mu sync.RWMutex
nics map[tcpip.NICID]*NIC
forwarding bool
@@ -327,6 +331,9 @@
// should be handled by the stack internally (true) or outside the
// stack (false).
HandleLocal bool
+
+ // Raw indicates whether raw sockets may be created.
+ Raw bool
}
// New allocates a new networking stack with only the requested networking and
@@ -352,6 +359,7 @@
clock: clock,
stats: opts.Stats.FillIn(),
handleLocal: opts.HandleLocal,
+ raw: opts.Raw,
}
// Add specified network protocols.
@@ -468,7 +476,7 @@
// SetForwarding enables or disables the packet forwarding between NICs.
func (s *Stack) SetForwarding(enable bool) {
- // TODO: Expose via /proc/sys/net/ipv4/ip_forward.
+ // TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward.
s.mu.Lock()
s.forwarding = enable
s.mu.Unlock()
@@ -476,7 +484,7 @@
// Forwarding returns if the packet forwarding between NICs is enabled.
func (s *Stack) Forwarding() bool {
- // TODO: Expose via /proc/sys/net/ipv4/ip_forward.
+ // TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward.
s.mu.RLock()
defer s.mu.RUnlock()
return s.forwarding
@@ -512,6 +520,10 @@
// protocol. Raw endpoints receive all traffic for a given protocol regardless
// of address.
func (s *Stack) NewRawEndpoint(transport tcpip.TransportProtocolNumber, network tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+ if !s.raw {
+ return nil, tcpip.ErrNotPermitted
+ }
+
t, ok := s.transportProtocols[transport]
if !ok {
return nil, tcpip.ErrUnknownProtocol
@@ -955,11 +967,11 @@
}
// RegisterRawTransportEndpoint registers the given endpoint with the stack
-// transport dispatcher. Received packets that match the provided protocol will
-// be delivered to the given endpoint.
-func (s *Stack) RegisterRawTransportEndpoint(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, ep TransportEndpoint, reusePort bool) *tcpip.Error {
+// transport dispatcher. Received packets that match the provided transport
+// protocol will be delivered to the given endpoint.
+func (s *Stack) RegisterRawTransportEndpoint(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ep RawTransportEndpoint) *tcpip.Error {
if nicID == 0 {
- return s.demux.registerRawEndpoint(netProtos, protocol, ep, reusePort)
+ return s.demux.registerRawEndpoint(netProto, transProto, ep)
}
s.mu.RLock()
@@ -970,14 +982,14 @@
return tcpip.ErrUnknownNICID
}
- return nic.demux.registerRawEndpoint(netProtos, protocol, ep, reusePort)
+ return nic.demux.registerRawEndpoint(netProto, transProto, ep)
}
-// UnregisterRawTransportEndpoint removes the endpoint for the protocol from
-// the stack transport dispatcher.
-func (s *Stack) UnregisterRawTransportEndpoint(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, ep TransportEndpoint) {
+// UnregisterRawTransportEndpoint removes the endpoint for the transport
+// protocol from the stack transport dispatcher.
+func (s *Stack) UnregisterRawTransportEndpoint(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ep RawTransportEndpoint) {
if nicID == 0 {
- s.demux.unregisterRawEndpoint(netProtos, protocol, ep)
+ s.demux.unregisterRawEndpoint(netProto, transProto, ep)
return
}
@@ -986,7 +998,7 @@
nic := s.nics[nicID]
if nic != nil {
- nic.demux.unregisterRawEndpoint(netProtos, protocol, ep)
+ nic.demux.unregisterRawEndpoint(netProto, transProto, ep)
}
}
@@ -1050,10 +1062,22 @@
// JoinGroup joins the given multicast group on the given NIC.
func (s *Stack) JoinGroup(protocol tcpip.NetworkProtocolNumber, nicID tcpip.NICID, multicastAddr tcpip.Address) *tcpip.Error {
// TODO: notify network of subscription via igmp protocol.
- return s.AddAddressWithOptions(nicID, protocol, multicastAddr, NeverPrimaryEndpoint)
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ if nic, ok := s.nics[nicID]; ok {
+ return nic.joinGroup(protocol, multicastAddr)
+ }
+ return tcpip.ErrUnknownNICID
}
// LeaveGroup leaves the given multicast group on the given NIC.
func (s *Stack) LeaveGroup(protocol tcpip.NetworkProtocolNumber, nicID tcpip.NICID, multicastAddr tcpip.Address) *tcpip.Error {
- return s.RemoveAddress(nicID, multicastAddr)
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ if nic, ok := s.nics[nicID]; ok {
+ return nic.leaveGroup(multicastAddr)
+ }
+ return tcpip.ErrUnknownNICID
}
diff --git a/tcpip/stack/stack_test.go b/tcpip/stack/stack_test.go
index 248ba1b..fc22865 100644
--- a/tcpip/stack/stack_test.go
+++ b/tcpip/stack/stack_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/stack/transport_demuxer.go b/tcpip/stack/transport_demuxer.go
index fab68ba..9bf3e99 100644
--- a/tcpip/stack/transport_demuxer.go
+++ b/tcpip/stack/transport_demuxer.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
package stack
import (
+ "fmt"
"math/rand"
"sync"
@@ -37,7 +38,7 @@
endpoints map[TransportEndpointID]TransportEndpoint
// rawEndpoints contains endpoints for raw sockets, which receive all
// traffic of a given protocol regardless of port.
- rawEndpoints []TransportEndpoint
+ rawEndpoints []RawTransportEndpoint
}
// unregisterEndpoint unregisters the endpoint with the given id such that it
@@ -60,8 +61,10 @@
// transportDemuxer demultiplexes packets targeted at a transport endpoint
// (i.e., after they've been parsed by the network layer). It does two levels
// of demultiplexing: first based on the network and transport protocols, then
-// based on endpoints IDs.
+// based on endpoints IDs. It should only be instantiated via
+// newTransportDemuxer.
type transportDemuxer struct {
+ // protocol is immutable.
protocol map[protocolIDs]*transportEndpoints
}
@@ -137,22 +140,22 @@
// HandlePacket is called by the stack when new packets arrive to this transport
// endpoint.
-func (ep *multiPortEndpoint) HandlePacket(r *Route, id TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) {
- // If this is a broadcast datagram, deliver the datagram to all endpoints
- // managed by ep.
- if id.LocalAddress == header.IPv4Broadcast {
+func (ep *multiPortEndpoint) HandlePacket(r *Route, id TransportEndpointID, vv buffer.VectorisedView) {
+ // If this is a broadcast or multicast datagram, deliver the datagram to all
+ // endpoints managed by ep.
+ if id.LocalAddress == header.IPv4Broadcast || header.IsV4MulticastAddress(id.LocalAddress) || header.IsV6MulticastAddress(id.LocalAddress) {
for i, endpoint := range ep.endpointsArr {
// HandlePacket modifies vv, so each endpoint needs its own copy.
if i == len(ep.endpointsArr)-1 {
- endpoint.HandlePacket(r, id, netHeader, vv)
+ endpoint.HandlePacket(r, id, vv)
break
}
vvCopy := buffer.NewView(vv.Size())
copy(vvCopy, vv.ToView())
- endpoint.HandlePacket(r, id, buffer.NewViewFromBytes(netHeader), vvCopy.ToVectorisedView())
+ endpoint.HandlePacket(r, id, vvCopy.ToVectorisedView())
}
} else {
- ep.selectEndpoint(id).HandlePacket(r, id, netHeader, vv)
+ ep.selectEndpoint(id).HandlePacket(r, id, vv)
}
}
@@ -168,7 +171,7 @@
// A new endpoint is added into endpointsArr and its index there is
// saved in endpointsMap. This will allows to remove endpoint from
// the array fast.
- ep.endpointsMap[ep] = len(ep.endpointsArr)
+ ep.endpointsMap[t] = len(ep.endpointsArr)
ep.endpointsArr = append(ep.endpointsArr, t)
}
@@ -286,17 +289,17 @@
// As in net/ipv4/ip_input.c:ip_local_deliver, attempt to deliver via
// raw endpoint first. If there are multipe raw endpoints, they all
// receive the packet.
- found := false
+ foundRaw := false
for _, rawEP := range eps.rawEndpoints {
// Each endpoint gets its own copy of the packet for the sake
// of save/restore.
- rawEP.HandlePacket(r, id, buffer.NewViewFromBytes(netHeader), vv.ToView().ToVectorisedView())
- found = true
+ rawEP.HandlePacket(r, buffer.NewViewFromBytes(netHeader), vv.ToView().ToVectorisedView())
+ foundRaw = true
}
eps.mu.RUnlock()
// Fail if we didn't find at least one matching transport endpoint.
- if len(destEps) == 0 && !found {
+ if len(destEps) == 0 && !foundRaw {
// UDP packet could not be delivered to an unknown destination port.
if protocol == header.UDPProtocolNumber {
r.Stats().UDP.UnknownPortErrors.Increment()
@@ -306,7 +309,7 @@
// Deliver the packet.
for _, ep := range destEps {
- ep.HandlePacket(r, id, netHeader, vv)
+ ep.HandlePacket(r, id, vv)
}
return true
@@ -371,19 +374,8 @@
// that packets of the appropriate protocol are delivered to it. A single
// packet can be sent to one or more raw endpoints along with a non-raw
// endpoint.
-func (d *transportDemuxer) registerRawEndpoint(netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, ep TransportEndpoint, reusePort bool) *tcpip.Error {
- for i, n := range netProtos {
- if err := d.singleRegisterRawEndpoint(n, protocol, ep); err != nil {
- d.unregisterRawEndpoint(netProtos[:i], protocol, ep)
- return err
- }
- }
-
- return nil
-}
-
-func (d *transportDemuxer) singleRegisterRawEndpoint(netProto tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, ep TransportEndpoint) *tcpip.Error {
- eps, ok := d.protocol[protocolIDs{netProto, protocol}]
+func (d *transportDemuxer) registerRawEndpoint(netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ep RawTransportEndpoint) *tcpip.Error {
+ eps, ok := d.protocol[protocolIDs{netProto, transProto}]
if !ok {
return nil
}
@@ -395,19 +387,20 @@
return nil
}
-// unregisterRawEndpoint unregisters the raw endpoint for the given protocol
-// such that it won't receive any more packets.
-func (d *transportDemuxer) unregisterRawEndpoint(netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, ep TransportEndpoint) {
- for _, n := range netProtos {
- if eps, ok := d.protocol[protocolIDs{n, protocol}]; ok {
- eps.mu.Lock()
- defer eps.mu.Unlock()
- for i, rawEP := range eps.rawEndpoints {
- if rawEP == ep {
- eps.rawEndpoints = append(eps.rawEndpoints[:i], eps.rawEndpoints[i+1:]...)
- return
- }
- }
+// unregisterRawEndpoint unregisters the raw endpoint for the given transport
+// protocol such that it won't receive any more packets.
+func (d *transportDemuxer) unregisterRawEndpoint(netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ep RawTransportEndpoint) {
+ eps, ok := d.protocol[protocolIDs{netProto, transProto}]
+ if !ok {
+ panic(fmt.Errorf("tried to unregister endpoint with unsupported network and transport protocol pair: %d, %d", netProto, transProto))
+ }
+
+ eps.mu.Lock()
+ defer eps.mu.Unlock()
+ for i, rawEP := range eps.rawEndpoints {
+ if rawEP == ep {
+ eps.rawEndpoints = append(eps.rawEndpoints[:i], eps.rawEndpoints[i+1:]...)
+ return
}
}
}
diff --git a/tcpip/stack/transport_test.go b/tcpip/stack/transport_test.go
index 81329d5..8cbf9b8 100644
--- a/tcpip/stack/transport_test.go
+++ b/tcpip/stack/transport_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -168,7 +168,7 @@
return tcpip.FullAddress{}, nil
}
-func (f *fakeTransportEndpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, _ buffer.View, _ buffer.VectorisedView) {
+func (f *fakeTransportEndpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, _ buffer.VectorisedView) {
// Increment the number of received packets.
f.proto.packetCount++
if f.acceptQueue != nil {
@@ -453,7 +453,7 @@
s := stack.New([]string{"fakeNet"}, []string{"fakeTrans"}, stack.Options{})
s.SetForwarding(true)
- // TODO: Change this to a channel NIC.
+ // TODO(b/123449044): Change this to a channel NIC.
id1 := loopback.New()
if err := s.CreateNIC(1, id1); err != nil {
t.Fatalf("CreateNIC #1 failed: %v", err)
diff --git a/tcpip/tcpip.go b/tcpip/tcpip.go
index 9f47898..1d14f30 100644
--- a/tcpip/tcpip.go
+++ b/tcpip/tcpip.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -102,6 +102,7 @@
ErrMessageTooLong = &Error{msg: "message too long"}
ErrNoBufferSpace = &Error{msg: "no buffer space available"}
ErrBroadcastDisabled = &Error{msg: "broadcast socket option disabled"}
+ ErrNotPermitted = &Error{msg: "operation not permitted"}
)
// Errors related to Subnet
@@ -443,7 +444,7 @@
// TCPInfoOption is used by GetSockOpt to expose TCP statistics.
//
-// TODO: Add and populate stat fields.
+// TODO(b/64800844): Add and populate stat fields.
type TCPInfoOption struct {
RTT time.Duration
RTTVar time.Duration
@@ -801,6 +802,9 @@
// Timeouts is the number of times the RTO expired.
Timeouts *StatCounter
+
+ // ChecksumErrors is the number of segments dropped due to bad checksums.
+ ChecksumErrors *StatCounter
}
// UDPStats collects UDP-specific stats.
diff --git a/tcpip/tcpip_test.go b/tcpip/tcpip_test.go
index 1f7b043..ebb1c1b 100644
--- a/tcpip/tcpip_test.go
+++ b/tcpip/tcpip_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/time.s b/tcpip/time.s
index 85d52cd..fb37360 100644
--- a/tcpip/time.s
+++ b/tcpip/time.s
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/time_unsafe.go b/tcpip/time_unsafe.go
index 7ec5741..1a30748 100644
--- a/tcpip/time_unsafe.go
+++ b/tcpip/time_unsafe.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/icmp/endpoint.go b/tcpip/transport/icmp/endpoint.go
index 9aa6741..55a1fc1 100644
--- a/tcpip/transport/icmp/endpoint.go
+++ b/tcpip/transport/icmp/endpoint.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -59,10 +59,6 @@
netProto tcpip.NetworkProtocolNumber
transProto tcpip.TransportProtocolNumber
waiterQueue *waiter.Queue
- // raw indicates whether the endpoint is intended for use by a raw
- // socket, which returns the network layer header along with the
- // payload. It is immutable.
- raw bool
// The following fields are used to manage the receive queue, and are
// protected by rcvMu.
@@ -80,32 +76,26 @@
shutdownFlags tcpip.ShutdownFlags
id stack.TransportEndpointID
state endpointState
- bindNICID tcpip.NICID
- bindAddr tcpip.Address
- regNICID tcpip.NICID
- route stack.Route
+ // bindNICID and bindAddr are set via calls to Bind(). They are used to
+ // reject attempts to send data or connect via a different NIC or
+ // address
+ bindNICID tcpip.NICID
+ bindAddr tcpip.Address
+ // regNICID is the default NIC to be used when callers don't specify a
+ // NIC.
+ regNICID tcpip.NICID
+ route stack.Route
}
-func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue, raw bool) (*endpoint, *tcpip.Error) {
- e := &endpoint{
+func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+ return &endpoint{
stack: stack,
netProto: netProto,
transProto: transProto,
waiterQueue: waiterQueue,
rcvBufSizeMax: 32 * 1024,
sndBufSize: 32 * 1024,
- raw: raw,
- }
-
- // Raw endpoints must be immediately bound because they receive all
- // ICMP traffic starting from when they're created via socket().
- if raw {
- if err := e.bindLocked(tcpip.FullAddress{}); err != nil {
- return nil, err
- }
- }
-
- return e, nil
+ }, nil
}
// Close puts the endpoint in a closed state and frees all resources
@@ -115,11 +105,7 @@
e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite
switch e.state {
case stateBound, stateConnected:
- if e.raw {
- e.stack.UnregisterRawTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.transProto, e)
- } else {
- e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.transProto, e.id, e)
- }
+ e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.transProto, e.id, e)
}
// Close the receive list and drain it.
@@ -244,8 +230,9 @@
route = &e.route
if route.IsResolutionRequired() {
- // Promote lock to exclusive if using a shared route, given that it may
- // need to change in Route.Resolve() call below.
+ // Promote lock to exclusive if using a shared route,
+ // given that it may need to change in Route.Resolve()
+ // call below.
e.mu.RUnlock()
defer e.mu.RLock()
@@ -290,8 +277,9 @@
waker := &sleep.Waker{}
if ch, err := route.Resolve(waker); err != nil {
if err == tcpip.ErrWouldBlock {
- // Link address needs to be resolved. Resolution was triggered the
- // background. Better luck next time.
+ // Link address needs to be resolved.
+ // Resolution was triggered the background.
+ // Better luck next time.
route.RemoveWaker(waker)
return 0, ch, tcpip.ErrNoLinkAddress
}
@@ -368,11 +356,6 @@
}
func (e *endpoint) send4(r *stack.Route, data buffer.View) *tcpip.Error {
- if e.raw {
- hdr := buffer.NewPrependable(len(data) + int(r.MaxHeaderLength()))
- return r.WritePacket(nil /* gso */, hdr, data.ToVectorisedView(), header.ICMPv4ProtocolNumber, r.DefaultTTL())
- }
-
if len(data) < header.ICMPv4EchoMinimumSize {
return tcpip.ErrInvalidEndpointState
}
@@ -439,11 +422,6 @@
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
- // TODO: We don't yet support connect on a raw socket.
- if e.raw {
- return tcpip.ErrNotSupported
- }
-
e.mu.Lock()
defer e.mu.Unlock()
@@ -547,11 +525,6 @@
}
func (e *endpoint) registerWithStack(nicid tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, *tcpip.Error) {
- if e.raw {
- err := e.stack.RegisterRawTransportEndpoint(nicid, netProtos, e.transProto, e, false)
- return stack.TransportEndpointID{}, err
- }
-
if id.LocalPort != 0 {
// The endpoint already has a local port, just attempt to
// register it.
@@ -687,11 +660,12 @@
// HandlePacket is called by the stack when new packets arrive to this transport
// endpoint.
-func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) {
+func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) {
e.rcvMu.Lock()
// Drop the packet if our buffer is currently full.
if !e.rcvReady || e.rcvClosed || e.rcvBufSize >= e.rcvBufSizeMax {
+ e.stack.Stats().DroppedPackets.Increment()
e.rcvMu.Unlock()
return
}
@@ -706,13 +680,7 @@
},
}
- if e.raw {
- combinedVV := netHeader.ToVectorisedView()
- combinedVV.Append(vv)
- pkt.data = combinedVV.Clone(pkt.views[:])
- } else {
- pkt.data = vv.Clone(pkt.views[:])
- }
+ pkt.data = vv.Clone(pkt.views[:])
e.rcvList.PushBack(pkt)
e.rcvBufSize += pkt.data.Size()
diff --git a/tcpip/transport/icmp/protocol.go b/tcpip/transport/icmp/protocol.go
index 57e5fcc..c884977 100644
--- a/tcpip/transport/icmp/protocol.go
+++ b/tcpip/transport/icmp/protocol.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/stack"
+ "github.com/google/netstack/tcpip/transport/raw"
"github.com/google/netstack/waiter"
)
@@ -73,7 +74,7 @@
if netProto != p.netProto() {
return nil, tcpip.ErrUnknownProtocol
}
- return newEndpoint(stack, netProto, p.number, waiterQueue, false)
+ return newEndpoint(stack, netProto, p.number, waiterQueue)
}
// NewRawEndpoint creates a new raw icmp endpoint. It implements
@@ -82,7 +83,7 @@
if netProto != p.netProto() {
return nil, tcpip.ErrUnknownProtocol
}
- return newEndpoint(stack, netProto, p.number, waiterQueue, true)
+ return raw.NewEndpoint(stack, netProto, p.number, waiterQueue)
}
// MinimumPacketSize returns the minimum valid icmp packet size.
diff --git a/tcpip/transport/raw/raw.go b/tcpip/transport/raw/raw.go
new file mode 100644
index 0000000..816ef2d
--- /dev/null
+++ b/tcpip/transport/raw/raw.go
@@ -0,0 +1,558 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package raw provides the implementation of raw sockets (see raw(7)). Raw
+// sockets allow applications to:
+//
+// * manually write and inspect transport layer headers and payloads
+// * receive all traffic of a given transport protcol (e.g. ICMP or UDP)
+// * optionally write and inspect network layer and link layer headers for
+// packets
+//
+// Raw sockets don't have any notion of ports, and incoming packets are
+// demultiplexed solely by protocol number. Thus, a raw UDP endpoint will
+// receive every UDP packet received by netstack. bind(2) and connect(2) can be
+// used to filter incoming packets by source and destination.
+package raw
+
+import (
+ "sync"
+
+ "github.com/google/netstack/sleep"
+ "github.com/google/netstack/tcpip"
+ "github.com/google/netstack/tcpip/buffer"
+ "github.com/google/netstack/tcpip/header"
+ "github.com/google/netstack/tcpip/stack"
+ "github.com/google/netstack/waiter"
+)
+
+// +stateify savable
+type packet struct {
+ packetEntry
+ // data holds the actual packet data, including any headers and
+ // payload.
+ data buffer.VectorisedView
+ // views is pre-allocated space to back data. As long as the packet is
+ // made up of fewer than 8 buffer.Views, no extra allocation is
+ // necessary to store packet data.
+ views [8]buffer.View
+ // timestampNS is the unix time at which the packet was received.
+ timestampNS int64
+ // senderAddr is the network address of the sender.
+ senderAddr tcpip.FullAddress
+}
+
+// endpoint is the raw socket implementation of tcpip.Endpoint. It is legal to
+// have goroutines make concurrent calls into the endpoint.
+//
+// Lock order:
+// endpoint.mu
+// endpoint.rcvMu
+//
+// +stateify savable
+type endpoint struct {
+ // The following fields are initialized at creation time and are
+ // immutable.
+ stack *stack.Stack
+ netProto tcpip.NetworkProtocolNumber
+ transProto tcpip.TransportProtocolNumber
+ waiterQueue *waiter.Queue
+
+ // The following fields are used to manage the receive queue and are
+ // protected by rcvMu.
+ rcvMu sync.Mutex
+ rcvList packetList
+ rcvBufSizeMax int
+ rcvBufSize int
+ rcvClosed bool
+
+ // The following fields are protected by mu.
+ mu sync.RWMutex
+ sndBufSize int
+ // shutdownFlags represent the current shutdown state of the endpoint.
+ shutdownFlags tcpip.ShutdownFlags
+ closed bool
+ connected bool
+ bound bool
+ // registeredNIC is the NIC to which th endpoint is explicitly
+ // registered. Is set when Connect or Bind are used to specify a NIC.
+ registeredNIC tcpip.NICID
+ // boundNIC and boundAddr are set on calls to Bind(). When callers
+ // attempt actions that would invalidate the binding data (e.g. sending
+ // data via a NIC other than boundNIC), the endpoint will return an
+ // error.
+ boundNIC tcpip.NICID
+ boundAddr tcpip.Address
+ // route is the route to a remote network endpoint. It is set via
+ // Connect(), and is valid only when conneted is true.
+ route stack.Route
+}
+
+// NewEndpoint returns a raw endpoint for the given protocols.
+// TODO(b/129292371): IP_HDRINCL, IPPROTO_RAW, and AF_PACKET.
+func NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+ if netProto != header.IPv4ProtocolNumber {
+ return nil, tcpip.ErrUnknownProtocol
+ }
+
+ ep := &endpoint{
+ stack: stack,
+ netProto: netProto,
+ transProto: transProto,
+ waiterQueue: waiterQueue,
+ rcvBufSizeMax: 32 * 1024,
+ sndBufSize: 32 * 1024,
+ }
+
+ if err := ep.stack.RegisterRawTransportEndpoint(ep.registeredNIC, ep.netProto, ep.transProto, ep); err != nil {
+ return nil, err
+ }
+
+ return ep, nil
+}
+
+// Close implements tcpip.Endpoint.Close.
+func (ep *endpoint) Close() {
+ ep.mu.Lock()
+ defer ep.mu.Unlock()
+
+ if ep.closed {
+ return
+ }
+
+ ep.stack.UnregisterRawTransportEndpoint(ep.registeredNIC, ep.netProto, ep.transProto, ep)
+
+ ep.rcvMu.Lock()
+ defer ep.rcvMu.Unlock()
+
+ // Clear the receive list.
+ ep.rcvClosed = true
+ ep.rcvBufSize = 0
+ for !ep.rcvList.Empty() {
+ ep.rcvList.Remove(ep.rcvList.Front())
+ }
+
+ if ep.connected {
+ ep.route.Release()
+ }
+
+ ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
+}
+
+// Read implements tcpip.Endpoint.Read.
+func (ep *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
+ ep.rcvMu.Lock()
+
+ // If there's no data to read, return that read would block or that the
+ // endpoint is closed.
+ if ep.rcvList.Empty() {
+ err := tcpip.ErrWouldBlock
+ if ep.rcvClosed {
+ err = tcpip.ErrClosedForReceive
+ }
+ ep.rcvMu.Unlock()
+ return buffer.View{}, tcpip.ControlMessages{}, err
+ }
+
+ packet := ep.rcvList.Front()
+ ep.rcvList.Remove(packet)
+ ep.rcvBufSize -= packet.data.Size()
+
+ ep.rcvMu.Unlock()
+
+ if addr != nil {
+ *addr = packet.senderAddr
+ }
+
+ return packet.data.ToView(), tcpip.ControlMessages{HasTimestamp: true, Timestamp: packet.timestampNS}, nil
+}
+
+// Write implements tcpip.Endpoint.Write.
+func (ep *endpoint) Write(payload tcpip.Payload, opts tcpip.WriteOptions) (uintptr, <-chan struct{}, *tcpip.Error) {
+ // MSG_MORE is unimplemented. This also means that MSG_EOR is a no-op.
+ if opts.More {
+ return 0, nil, tcpip.ErrInvalidOptionValue
+ }
+
+ ep.mu.RLock()
+
+ if ep.closed {
+ ep.mu.RUnlock()
+ return 0, nil, tcpip.ErrInvalidEndpointState
+ }
+
+ // Check whether we've shutdown writing.
+ if ep.shutdownFlags&tcpip.ShutdownWrite != 0 {
+ ep.mu.RUnlock()
+ return 0, nil, tcpip.ErrClosedForSend
+ }
+
+ // Did the user caller provide a destination? If not, use the connected
+ // destination.
+ if opts.To == nil {
+ // If the user doesn't specify a destination, they should have
+ // connected to another address.
+ if !ep.connected {
+ ep.mu.RUnlock()
+ return 0, nil, tcpip.ErrNotConnected
+ }
+
+ if ep.route.IsResolutionRequired() {
+ savedRoute := &ep.route
+ // Promote lock to exclusive if using a shared route,
+ // given that it may need to change in finishWrite.
+ ep.mu.RUnlock()
+ ep.mu.Lock()
+
+ // Make sure that the route didn't change during the
+ // time we didn't hold the lock.
+ if !ep.connected || savedRoute != &ep.route {
+ ep.mu.Unlock()
+ return 0, nil, tcpip.ErrInvalidEndpointState
+ }
+
+ n, ch, err := ep.finishWrite(payload, savedRoute)
+ ep.mu.Unlock()
+ return n, ch, err
+ }
+
+ n, ch, err := ep.finishWrite(payload, &ep.route)
+ ep.mu.RUnlock()
+ return n, ch, err
+ }
+
+ // The caller provided a destination. Reject destination address if it
+ // goes through a different NIC than the endpoint was bound to.
+ nic := opts.To.NIC
+ if ep.bound && nic != 0 && nic != ep.boundNIC {
+ ep.mu.RUnlock()
+ return 0, nil, tcpip.ErrNoRoute
+ }
+
+ // We don't support IPv6 yet, so this has to be an IPv4 address.
+ if len(opts.To.Addr) != header.IPv4AddressSize {
+ ep.mu.RUnlock()
+ return 0, nil, tcpip.ErrInvalidEndpointState
+ }
+
+ // Find the route to the destination. If boundAddress is 0,
+ // FindRoute will choose an appropriate source address.
+ route, err := ep.stack.FindRoute(nic, ep.boundAddr, opts.To.Addr, ep.netProto, false)
+ if err != nil {
+ ep.mu.RUnlock()
+ return 0, nil, err
+ }
+
+ n, ch, err := ep.finishWrite(payload, &route)
+ route.Release()
+ ep.mu.RUnlock()
+ return n, ch, err
+}
+
+// finishWrite writes the payload to a route. It resolves the route if
+// necessary. It's really just a helper to make defer unnecessary in Write.
+func (ep *endpoint) finishWrite(payload tcpip.Payload, route *stack.Route) (uintptr, <-chan struct{}, *tcpip.Error) {
+ // We may need to resolve the route (match a link layer address to the
+ // network address). If that requires blocking (e.g. to use ARP),
+ // return a channel on which the caller can wait.
+ if route.IsResolutionRequired() {
+ waker := &sleep.Waker{}
+ if ch, err := route.Resolve(waker); err != nil {
+ if err == tcpip.ErrWouldBlock {
+ // Link address needs to be resolved.
+ // Resolution was triggered the background.
+ // Better luck next time.
+ route.RemoveWaker(waker)
+ return 0, ch, tcpip.ErrNoLinkAddress
+ }
+ return 0, nil, err
+ }
+ }
+
+ payloadBytes, err := payload.Get(payload.Size())
+ if err != nil {
+ return 0, nil, err
+ }
+
+ switch ep.netProto {
+ case header.IPv4ProtocolNumber:
+ hdr := buffer.NewPrependable(len(payloadBytes) + int(route.MaxHeaderLength()))
+ if err := route.WritePacket(nil /* gso */, hdr, buffer.View(payloadBytes).ToVectorisedView(), header.ICMPv4ProtocolNumber, route.DefaultTTL()); err != nil {
+ return 0, nil, err
+ }
+
+ default:
+ return 0, nil, tcpip.ErrUnknownProtocol
+ }
+
+ return uintptr(len(payloadBytes)), nil, nil
+}
+
+// Peek implements tcpip.Endpoint.Peek.
+func (ep *endpoint) Peek([][]byte) (uintptr, tcpip.ControlMessages, *tcpip.Error) {
+ return 0, tcpip.ControlMessages{}, nil
+}
+
+// Connect implements tcpip.Endpoint.Connect.
+func (ep *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
+ ep.mu.Lock()
+ defer ep.mu.Unlock()
+
+ if ep.closed {
+ return tcpip.ErrInvalidEndpointState
+ }
+
+ // We don't support IPv6 yet.
+ if len(addr.Addr) != header.IPv4AddressSize {
+ return tcpip.ErrInvalidEndpointState
+ }
+
+ nic := addr.NIC
+ if ep.bound {
+ if ep.boundNIC == 0 {
+ // If we're bound, but not to a specific NIC, the NIC
+ // in addr will be used. Nothing to do here.
+ } else if addr.NIC == 0 {
+ // If we're bound to a specific NIC, but addr doesn't
+ // specify a NIC, use the bound NIC.
+ nic = ep.boundNIC
+ } else if addr.NIC != ep.boundNIC {
+ // We're bound and addr specifies a NIC. They must be
+ // the same.
+ return tcpip.ErrInvalidEndpointState
+ }
+ }
+
+ // Find a route to the destination.
+ route, err := ep.stack.FindRoute(nic, tcpip.Address(""), addr.Addr, ep.netProto, false)
+ if err != nil {
+ return err
+ }
+ defer route.Release()
+
+ // Re-register the endpoint with the appropriate NIC.
+ if err := ep.stack.RegisterRawTransportEndpoint(addr.NIC, ep.netProto, ep.transProto, ep); err != nil {
+ return err
+ }
+ ep.stack.UnregisterRawTransportEndpoint(ep.registeredNIC, ep.netProto, ep.transProto, ep)
+
+ // Save the route and NIC we've connected via.
+ ep.route = route.Clone()
+ ep.registeredNIC = nic
+ ep.connected = true
+
+ return nil
+}
+
+// Shutdown implements tcpip.Endpoint.Shutdown.
+func (ep *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
+ ep.mu.Lock()
+ defer ep.mu.Unlock()
+
+ if !ep.connected {
+ return tcpip.ErrNotConnected
+ }
+
+ ep.shutdownFlags |= flags
+
+ if flags&tcpip.ShutdownRead != 0 {
+ ep.rcvMu.Lock()
+ wasClosed := ep.rcvClosed
+ ep.rcvClosed = true
+ ep.rcvMu.Unlock()
+
+ if !wasClosed {
+ ep.waiterQueue.Notify(waiter.EventIn)
+ }
+ }
+
+ return nil
+}
+
+// Listen implements tcpip.Endpoint.Listen.
+func (ep *endpoint) Listen(backlog int) *tcpip.Error {
+ return tcpip.ErrNotSupported
+}
+
+// Accept implements tcpip.Endpoint.Accept.
+func (ep *endpoint) Accept() (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
+ return nil, nil, tcpip.ErrNotSupported
+}
+
+// Bind implements tcpip.Endpoint.Bind.
+func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
+ ep.mu.Lock()
+ defer ep.mu.Unlock()
+
+ // Callers must provide an IPv4 address or no network address (for
+ // binding to a NIC, but not an address).
+ if len(addr.Addr) != 0 && len(addr.Addr) != 4 {
+ return tcpip.ErrInvalidEndpointState
+ }
+
+ // If a local address was specified, verify that it's valid.
+ if len(addr.Addr) == header.IPv4AddressSize && ep.stack.CheckLocalAddress(addr.NIC, ep.netProto, addr.Addr) == 0 {
+ return tcpip.ErrBadLocalAddress
+ }
+
+ // Re-register the endpoint with the appropriate NIC.
+ if err := ep.stack.RegisterRawTransportEndpoint(addr.NIC, ep.netProto, ep.transProto, ep); err != nil {
+ return err
+ }
+ ep.stack.UnregisterRawTransportEndpoint(ep.registeredNIC, ep.netProto, ep.transProto, ep)
+
+ ep.registeredNIC = addr.NIC
+ ep.boundNIC = addr.NIC
+ ep.boundAddr = addr.Addr
+ ep.bound = true
+
+ return nil
+}
+
+// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
+func (ep *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
+ return tcpip.FullAddress{}, tcpip.ErrNotSupported
+}
+
+// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
+func (ep *endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+ ep.mu.RLock()
+ defer ep.mu.RUnlock()
+
+ if !ep.connected {
+ return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ }
+
+ return tcpip.FullAddress{
+ NIC: ep.registeredNIC,
+ Addr: ep.route.RemoteAddress,
+ }, nil
+}
+
+// Readiness implements tcpip.Endpoint.Readiness.
+func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
+ // The endpoint is always writable.
+ result := waiter.EventOut & mask
+
+ // Determine whether the endpoint is readable.
+ if (mask & waiter.EventIn) != 0 {
+ ep.rcvMu.Lock()
+ if !ep.rcvList.Empty() || ep.rcvClosed {
+ result |= waiter.EventIn
+ }
+ ep.rcvMu.Unlock()
+ }
+
+ return result
+}
+
+// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
+func (ep *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
+ return nil
+}
+
+// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
+func (ep *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
+ switch o := opt.(type) {
+ case tcpip.ErrorOption:
+ return nil
+
+ case *tcpip.SendBufferSizeOption:
+ ep.mu.Lock()
+ *o = tcpip.SendBufferSizeOption(ep.sndBufSize)
+ ep.mu.Unlock()
+ return nil
+
+ case *tcpip.ReceiveBufferSizeOption:
+ ep.rcvMu.Lock()
+ *o = tcpip.ReceiveBufferSizeOption(ep.rcvBufSizeMax)
+ ep.rcvMu.Unlock()
+ return nil
+
+ case *tcpip.ReceiveQueueSizeOption:
+ ep.rcvMu.Lock()
+ if ep.rcvList.Empty() {
+ *o = 0
+ } else {
+ p := ep.rcvList.Front()
+ *o = tcpip.ReceiveQueueSizeOption(p.data.Size())
+ }
+ ep.rcvMu.Unlock()
+ return nil
+
+ case *tcpip.KeepaliveEnabledOption:
+ *o = 0
+ return nil
+
+ default:
+ return tcpip.ErrUnknownProtocolOption
+ }
+}
+
+// HandlePacket implements stack.RawTransportEndpoint.HandlePacket.
+func (ep *endpoint) HandlePacket(route *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) {
+ ep.rcvMu.Lock()
+
+ // Drop the packet if our buffer is currently full.
+ if ep.rcvClosed || ep.rcvBufSize >= ep.rcvBufSizeMax {
+ ep.stack.Stats().DroppedPackets.Increment()
+ ep.rcvMu.Unlock()
+ return
+ }
+
+ if ep.bound {
+ // If bound to a NIC, only accept data for that NIC.
+ if ep.boundNIC != 0 && ep.boundNIC != route.NICID() {
+ ep.rcvMu.Unlock()
+ return
+ }
+ // If bound to an address, only accept data for that address.
+ if ep.boundAddr != "" && ep.boundAddr != route.RemoteAddress {
+ ep.rcvMu.Unlock()
+ return
+ }
+ }
+
+ // If connected, only accept packets from the remote address we
+ // connected to.
+ if ep.connected && ep.route.RemoteAddress != route.RemoteAddress {
+ ep.rcvMu.Unlock()
+ return
+ }
+
+ wasEmpty := ep.rcvBufSize == 0
+
+ // Push new packet into receive list and increment the buffer size.
+ packet := &packet{
+ senderAddr: tcpip.FullAddress{
+ NIC: route.NICID(),
+ Addr: route.RemoteAddress,
+ },
+ }
+
+ combinedVV := netHeader.ToVectorisedView()
+ combinedVV.Append(vv)
+ packet.data = combinedVV.Clone(packet.views[:])
+ packet.timestampNS = ep.stack.NowNanoseconds()
+
+ ep.rcvList.PushBack(packet)
+ ep.rcvBufSize += packet.data.Size()
+
+ ep.rcvMu.Unlock()
+
+ // Notify waiters that there's data to be read.
+ if wasEmpty {
+ ep.waiterQueue.Notify(waiter.EventIn)
+ }
+}
diff --git a/tcpip/transport/raw/state.go b/tcpip/transport/raw/state.go
new file mode 100644
index 0000000..a7363b4
--- /dev/null
+++ b/tcpip/transport/raw/state.go
@@ -0,0 +1,88 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package raw
+
+import (
+ "github.com/google/netstack/tcpip"
+ "github.com/google/netstack/tcpip/buffer"
+ "github.com/google/netstack/tcpip/stack"
+)
+
+// saveData saves packet.data field.
+func (p *packet) saveData() buffer.VectorisedView {
+ // We cannot save p.data directly as p.data.views may alias to p.views,
+ // which is not allowed by state framework (in-struct pointer).
+ return p.data.Clone(nil)
+}
+
+// loadData loads packet.data field.
+func (p *packet) loadData(data buffer.VectorisedView) {
+ // NOTE: We cannot do the p.data = data.Clone(p.views[:]) optimization
+ // here because data.views is not guaranteed to be loaded by now. Plus,
+ // data.views will be allocated anyway so there really is little point
+ // of utilizing p.views for data.views.
+ p.data = data
+}
+
+// beforeSave is invoked by stateify.
+func (ep *endpoint) beforeSave() {
+ // Stop incoming packets from being handled (and mutate endpoint state).
+ // The lock will be released after saveRcvBufSizeMax(), which would have
+ // saved ep.rcvBufSizeMax and set it to 0 to continue blocking incoming
+ // packets.
+ ep.rcvMu.Lock()
+}
+
+// saveRcvBufSizeMax is invoked by stateify.
+func (ep *endpoint) saveRcvBufSizeMax() int {
+ max := ep.rcvBufSizeMax
+ // Make sure no new packets will be handled regardless of the lock.
+ ep.rcvBufSizeMax = 0
+ // Release the lock acquired in beforeSave() so regular endpoint closing
+ // logic can proceed after save.
+ ep.rcvMu.Unlock()
+ return max
+}
+
+// loadRcvBufSizeMax is invoked by stateify.
+func (ep *endpoint) loadRcvBufSizeMax(max int) {
+ ep.rcvBufSizeMax = max
+}
+
+// afterLoad is invoked by stateify.
+func (ep *endpoint) afterLoad() {
+ // StackFromEnv is a stack used specifically for save/restore.
+ ep.stack = stack.StackFromEnv
+
+ // If the endpoint is connected, re-connect via the save/restore stack.
+ if ep.connected {
+ var err *tcpip.Error
+ ep.route, err = ep.stack.FindRoute(ep.registeredNIC, ep.boundAddr, ep.route.RemoteAddress, ep.netProto, false)
+ if err != nil {
+ panic(*err)
+ }
+ }
+
+ // If the endpoint is bound, re-bind via the save/restore stack.
+ if ep.bound {
+ if ep.stack.CheckLocalAddress(ep.registeredNIC, ep.netProto, ep.boundAddr) == 0 {
+ panic(tcpip.ErrBadLocalAddress)
+ }
+ }
+
+ if err := ep.stack.RegisterRawTransportEndpoint(ep.registeredNIC, ep.netProto, ep.transProto, ep); err != nil {
+ panic(*err)
+ }
+}
diff --git a/tcpip/transport/tcp/accept.go b/tcpip/transport/tcp/accept.go
index 7cfc12c..b1d3710 100644
--- a/tcpip/transport/tcp/accept.go
+++ b/tcpip/transport/tcp/accept.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/connect.go b/tcpip/transport/tcp/connect.go
index 4c36700..1aaa3b0 100644
--- a/tcpip/transport/tcp/connect.go
+++ b/tcpip/transport/tcp/connect.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -595,7 +595,7 @@
// TCP header, then the kernel calculate a checksum of the
// header and data and get the right sum of the TCP packet.
tcp.SetChecksum(xsum)
- } else if r.Capabilities()&stack.CapabilityChecksumOffload == 0 {
+ } else if r.Capabilities()&stack.CapabilityTXChecksumOffload == 0 {
xsum = header.ChecksumVV(data, xsum)
tcp.SetChecksum(^tcp.CalculateChecksum(xsum))
}
diff --git a/tcpip/transport/tcp/cubic.go b/tcpip/transport/tcp/cubic.go
index 003525d..e618cd2 100644
--- a/tcpip/transport/tcp/cubic.go
+++ b/tcpip/transport/tcp/cubic.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/dual_stack_test.go b/tcpip/transport/tcp/dual_stack_test.go
index b1816be..9916cd8 100644
--- a/tcpip/transport/tcp/dual_stack_test.go
+++ b/tcpip/transport/tcp/dual_stack_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
"time"
"github.com/google/netstack/tcpip"
+ "github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/checker"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/network/ipv4"
@@ -330,6 +331,9 @@
}
func testV4Accept(t *testing.T, c *context.Context) {
+ c.SetGSOEnabled(true)
+ defer c.SetGSOEnabled(false)
+
// Start listening.
if err := c.EP.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
@@ -406,6 +410,14 @@
if addr.Addr != context.TestAddr {
t.Fatalf("Unexpected remote address: got %v, want %v", addr.Addr, context.TestAddr)
}
+
+ data := "Don't panic"
+ nep.Write(tcpip.SlicePayload(buffer.NewViewFromBytes([]byte(data))), tcpip.WriteOptions{})
+ b = c.GetPacket()
+ tcp = header.TCP(header.IPv4(b).Payload())
+ if string(tcp.Payload()) != data {
+ t.Fatalf("Unexpected data: got %v, want %v", string(tcp.Payload()), data)
+ }
}
func TestV4AcceptOnV6(t *testing.T) {
diff --git a/tcpip/transport/tcp/endpoint.go b/tcpip/transport/tcp/endpoint.go
index 49828bd..88e2a73 100644
--- a/tcpip/transport/tcp/endpoint.go
+++ b/tcpip/transport/tcp/endpoint.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1199,21 +1199,24 @@
switch e.state {
case stateConnected:
+ // Close for read.
+ if (e.shutdownFlags & tcpip.ShutdownRead) != 0 {
+ // Mark read side as closed.
+ e.rcvListMu.Lock()
+ e.rcvClosed = true
+ rcvBufUsed := e.rcvBufUsed
+ e.rcvListMu.Unlock()
+
+ // If we're fully closed and we have unread data we need to abort
+ // the connection with a RST.
+ if (e.shutdownFlags&tcpip.ShutdownWrite) != 0 && rcvBufUsed > 0 {
+ e.notifyProtocolGoroutine(notifyReset)
+ return nil
+ }
+ }
+
// Close for write.
if (e.shutdownFlags & tcpip.ShutdownWrite) != 0 {
- if (e.shutdownFlags & tcpip.ShutdownRead) != 0 {
- // We're fully closed, if we have unread data we need to abort
- // the connection with a RST.
- e.rcvListMu.Lock()
- rcvBufUsed := e.rcvBufUsed
- e.rcvListMu.Unlock()
-
- if rcvBufUsed > 0 {
- e.notifyProtocolGoroutine(notifyReset)
- return nil
- }
- }
-
e.sndBufMu.Lock()
if e.sndClosed {
@@ -1438,7 +1441,7 @@
// HandlePacket is called by the stack when new packets arrive to this transport
// endpoint.
-func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) {
+func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) {
s := newSegment(r, id, vv)
if !s.parse() {
e.stack.Stats().MalformedRcvdPackets.Increment()
@@ -1447,6 +1450,13 @@
return
}
+ if !s.csumValid {
+ e.stack.Stats().MalformedRcvdPackets.Increment()
+ e.stack.Stats().TCP.ChecksumErrors.Increment()
+ s.decRef()
+ return
+ }
+
e.stack.Stats().TCP.ValidSegmentsReceived.Increment()
if (s.flags & header.TCPFlagRst) != 0 {
e.stack.Stats().TCP.ResetsReceived.Increment()
@@ -1710,7 +1720,7 @@
}
gso := &stack.GSO{}
- switch e.netProto {
+ switch e.route.NetProto {
case header.IPv4ProtocolNumber:
gso.Type = stack.GSOTCPv4
gso.L3HdrLen = header.IPv4MinimumSize
@@ -1721,7 +1731,7 @@
panic(fmt.Sprintf("Unknown netProto: %v", e.netProto))
}
gso.NeedsCsum = true
- gso.CsumOffset = header.TCPChecksumOffset()
+ gso.CsumOffset = header.TCPChecksumOffset
gso.MaxSize = e.route.GSOMaxSize()
e.gso = gso
}
diff --git a/tcpip/transport/tcp/forwarder.go b/tcpip/transport/tcp/forwarder.go
index 9f49732..18848eb 100644
--- a/tcpip/transport/tcp/forwarder.go
+++ b/tcpip/transport/tcp/forwarder.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@
defer s.decRef()
// We only care about well-formed SYN packets.
- if !s.parse() || s.flags != header.TCPFlagSyn {
+ if !s.parse() || !s.csumValid || s.flags != header.TCPFlagSyn {
return false
}
diff --git a/tcpip/transport/tcp/protocol.go b/tcpip/transport/tcp/protocol.go
index 9be0b2c..aa3b0f3 100644
--- a/tcpip/transport/tcp/protocol.go
+++ b/tcpip/transport/tcp/protocol.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -130,7 +130,7 @@
s := newSegment(r, id, vv)
defer s.decRef()
- if !s.parse() {
+ if !s.parse() || !s.csumValid {
return false
}
diff --git a/tcpip/transport/tcp/rcv.go b/tcpip/transport/tcp/rcv.go
index 29fc666..e0d55a0 100644
--- a/tcpip/transport/tcp/rcv.go
+++ b/tcpip/transport/tcp/rcv.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/reno.go b/tcpip/transport/tcp/reno.go
index e4f8b7d..f83ebc7 100644
--- a/tcpip/transport/tcp/reno.go
+++ b/tcpip/transport/tcp/reno.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/sack.go b/tcpip/transport/tcp/sack.go
index 99b271f..be749e4 100644
--- a/tcpip/transport/tcp/sack.go
+++ b/tcpip/transport/tcp/sack.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/sack_scoreboard.go b/tcpip/transport/tcp/sack_scoreboard.go
index 1e6e4ee..dbb4a29 100644
--- a/tcpip/transport/tcp/sack_scoreboard.go
+++ b/tcpip/transport/tcp/sack_scoreboard.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@
sacked := i.(header.SACKBlock)
// There is a hole between these two SACK blocks, so we can't
// merge anymore.
- if r.End.LessThan(r.Start) {
+ if r.End.LessThan(sacked.Start) {
return false
}
// There is some overlap at this point, merge the blocks and
@@ -167,7 +167,11 @@
// Delete removes all SACK information prior to seq.
func (s *SACKScoreboard) Delete(seq seqnum.Value) {
+ if s.Empty() {
+ return
+ }
toDelete := []btree.Item{}
+ toInsert := []btree.Item{}
r := header.SACKBlock{seq, seq.Add(1)}
s.ranges.DescendLessOrEqual(r, func(i btree.Item) bool {
if i == r {
@@ -179,13 +183,16 @@
s.sacked -= sb.Start.Size(sb.End)
} else {
newSB := header.SACKBlock{seq, sb.End}
- s.ranges.ReplaceOrInsert(newSB)
+ toInsert = append(toInsert, newSB)
s.sacked -= sb.Start.Size(seq)
}
return true
})
- for _, i := range toDelete {
- s.ranges.Delete(i)
+ for _, sb := range toDelete {
+ s.ranges.Delete(sb)
+ }
+ for _, sb := range toInsert {
+ s.ranges.ReplaceOrInsert(sb)
}
}
diff --git a/tcpip/transport/tcp/sack_scoreboard_test.go b/tcpip/transport/tcp/sack_scoreboard_test.go
index a33f7be..3fcfa51 100644
--- a/tcpip/transport/tcp/sack_scoreboard_test.go
+++ b/tcpip/transport/tcp/sack_scoreboard_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -77,6 +77,15 @@
},
4294254144,
},
+ {
+ "Test disjoint SACKBlocks out of order",
+ []header.SACKBlock{{827450276, 827454536}, {827426028, 827428868}},
+ []blockTest{
+ {header.SACKBlock{827426028, 827428867}, true},
+ {header.SACKBlock{827450168, 827450275}, false},
+ },
+ 827426000,
+ },
}
for _, tc := range testCases {
sb := initScoreboard(tc.scoreboardBlocks, tc.iss)
diff --git a/tcpip/transport/tcp/segment.go b/tcpip/transport/tcp/segment.go
index 7b67007..783207b 100644
--- a/tcpip/transport/tcp/segment.go
+++ b/tcpip/transport/tcp/segment.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -45,6 +45,10 @@
ackNumber seqnum.Value
flags uint8
window seqnum.Size
+ // csum is only populated for received segments.
+ csum uint16
+ // csumValid is true if the csum in the received segment is valid.
+ csumValid bool
// parsedOptions stores the parsed values from the options in the segment.
parsedOptions header.TCPOptions
@@ -124,7 +128,13 @@
// parse populates the sequence & ack numbers, flags, and window fields of the
// segment from the TCP header stored in the data. It then updates the view to
-// skip the data. Returns boolean indicating if the parsing was successful.
+// skip the header.
+//
+// Returns boolean indicating if the parsing was successful.
+//
+// If checksum verification is not offloaded then parse also verifies the
+// TCP checksum and stores the checksum and result of checksum verification in
+// the csum and csumValid fields of the segment.
func (s *segment) parse() bool {
h := header.TCP(s.data.First())
@@ -145,12 +155,27 @@
s.options = []byte(h[header.TCPMinimumSize:offset])
s.parsedOptions = header.ParseTCPOptions(s.options)
- s.data.TrimFront(offset)
+
+ // Query the link capabilities to decide if checksum validation is
+ // required.
+ verifyChecksum := true
+ if s.route.Capabilities()&stack.CapabilityRXChecksumOffload != 0 {
+ s.csumValid = true
+ verifyChecksum = false
+ s.data.TrimFront(offset)
+ }
+ if verifyChecksum {
+ s.csum = h.Checksum()
+ xsum := s.route.PseudoHeaderChecksum(ProtocolNumber, uint16(s.data.Size()))
+ xsum = h.CalculateChecksum(xsum)
+ s.data.TrimFront(offset)
+ xsum = header.ChecksumVV(s.data, xsum)
+ s.csumValid = xsum == 0xffff
+ }
s.sequenceNumber = seqnum.Value(h.SequenceNumber())
s.ackNumber = seqnum.Value(h.AckNumber())
s.flags = h.Flags()
s.window = seqnum.Size(h.WindowSize())
-
return true
}
diff --git a/tcpip/transport/tcp/segment_heap.go b/tcpip/transport/tcp/segment_heap.go
index 98422fa..9fd061d 100644
--- a/tcpip/transport/tcp/segment_heap.go
+++ b/tcpip/transport/tcp/segment_heap.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/segment_queue.go b/tcpip/transport/tcp/segment_queue.go
index 65fad45..a90429a 100644
--- a/tcpip/transport/tcp/segment_queue.go
+++ b/tcpip/transport/tcp/segment_queue.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/snd.go b/tcpip/transport/tcp/snd.go
index de0ce5f..200e175 100644
--- a/tcpip/transport/tcp/snd.go
+++ b/tcpip/transport/tcp/snd.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/tcp_sack_test.go b/tcpip/transport/tcp/tcp_sack_test.go
index 6a3c92e..057dfa5 100644
--- a/tcpip/transport/tcp/tcp_sack_test.go
+++ b/tcpip/transport/tcp/tcp_sack_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/tcp_test.go b/tcpip/transport/tcp/tcp_test.go
index 86de038..b4e8322 100644
--- a/tcpip/transport/tcp/tcp_test.go
+++ b/tcpip/transport/tcp/tcp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -687,6 +687,25 @@
})
}
+func TestShutdownRead(t *testing.T) {
+ c := context.New(t, defaultMTU)
+ defer c.Cleanup()
+
+ c.CreateConnected(789, 30000, nil)
+
+ if _, _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
+ t.Fatalf("got c.EP.Read(nil) = %v, want = %v", err, tcpip.ErrWouldBlock)
+ }
+
+ if err := c.EP.Shutdown(tcpip.ShutdownRead); err != nil {
+ t.Fatalf("Shutdown failed: %v", err)
+ }
+
+ if _, _, err := c.EP.Read(nil); err != tcpip.ErrClosedForReceive {
+ t.Fatalf("got c.EP.Read(nil) = %v, want = %v", err, tcpip.ErrClosedForReceive)
+ }
+}
+
func TestFullWindowReceive(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
@@ -2963,8 +2982,7 @@
RcvWnd: 30000,
})
tcpbuf := vv.First()[header.IPv4MinimumSize:]
- // 12 is the TCP header data offset.
- tcpbuf[12] = ((header.TCPMinimumSize - 1) / 4) << 4
+ tcpbuf[header.TCPDataOffset] = ((header.TCPMinimumSize - 1) / 4) << 4
c.SendSegment(vv)
@@ -2973,6 +2991,32 @@
}
}
+func TestReceivedIncorrectChecksumIncrement(t *testing.T) {
+ c := context.New(t, defaultMTU)
+ defer c.Cleanup()
+ c.CreateConnected(789, 30000, nil)
+ stats := c.Stack().Stats()
+ want := stats.TCP.ChecksumErrors.Value() + 1
+ vv := c.BuildSegment([]byte{0x1, 0x2, 0x3}, &context.Headers{
+ SrcPort: context.TestPort,
+ DstPort: c.Port,
+ Flags: header.TCPFlagAck,
+ SeqNum: seqnum.Value(790),
+ AckNum: c.IRS.Add(1),
+ RcvWnd: 30000,
+ })
+ tcpbuf := vv.First()[header.IPv4MinimumSize:]
+ // Overwrite a byte in the payload which should cause checksum
+ // verification to fail.
+ tcpbuf[(tcpbuf[header.TCPDataOffset]>>4)*4] = 0x4
+
+ c.SendSegment(vv)
+
+ if got := stats.TCP.ChecksumErrors.Value(); got != want {
+ t.Errorf("got stats.TCP.ChecksumErrors.Value() = %d, want = %d", got, want)
+ }
+}
+
func TestReceivedSegmentQueuing(t *testing.T) {
// This test sends 200 segments containing a few bytes each to an
// endpoint and checks that they're all received and acknowledged by
diff --git a/tcpip/transport/tcp/tcp_timestamp_test.go b/tcpip/transport/tcp/tcp_timestamp_test.go
index 9a2e066..5688c57 100644
--- a/tcpip/transport/tcp/tcp_timestamp_test.go
+++ b/tcpip/transport/tcp/tcp_timestamp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcp/testing/context/context.go b/tcpip/transport/tcp/testing/context/context.go
index 03c7799..a51b27e 100644
--- a/tcpip/transport/tcp/testing/context/context.go
+++ b/tcpip/transport/tcp/testing/context/context.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -234,6 +234,10 @@
copy(b, p.Header)
copy(b[len(p.Header):], p.Payload)
+ if p.GSO != nil && p.GSO.L3HdrLen != header.IPv4MinimumSize {
+ c.t.Errorf("L3HdrLen %v (expected %v)", p.GSO.L3HdrLen, header.IPv4MinimumSize)
+ }
+
checker.IPv4(c.t, b, checker.SrcAddr(StackAddr), checker.DstAddr(TestAddr))
return b
@@ -327,9 +331,7 @@
})
// Calculate the TCP pseudo-header checksum.
- xsum := header.Checksum([]byte(TestAddr), 0)
- xsum = header.Checksum([]byte(StackAddr), xsum)
- xsum = header.Checksum([]byte{0, uint8(tcp.ProtocolNumber)}, xsum)
+ xsum := header.PseudoHeaderChecksum(tcp.ProtocolNumber, TestAddr, StackAddr, uint16(len(t)))
// Calculate the TCP checksum and set it.
xsum = header.Checksum(payload, xsum)
@@ -481,9 +483,7 @@
})
// Calculate the TCP pseudo-header checksum.
- xsum := header.Checksum([]byte(TestV6Addr), 0)
- xsum = header.Checksum([]byte(StackV6Addr), xsum)
- xsum = header.Checksum([]byte{0, uint8(tcp.ProtocolNumber)}, xsum)
+ xsum := header.PseudoHeaderChecksum(tcp.ProtocolNumber, TestV6Addr, StackV6Addr, uint16(len(t)))
// Calculate the TCP checksum and set it.
xsum = header.Checksum(payload, xsum)
@@ -960,3 +960,8 @@
}
return bool(v)
}
+
+// SetGSOEnabled enables or disables generic segmentation offload.
+func (c *Context) SetGSOEnabled(enable bool) {
+ c.linkEP.GSO = enable
+}
diff --git a/tcpip/transport/tcp/timer.go b/tcpip/transport/tcp/timer.go
index 312690d..f770be2 100644
--- a/tcpip/transport/tcp/timer.go
+++ b/tcpip/transport/tcp/timer.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcpconntrack/tcp_conntrack.go b/tcpip/transport/tcpconntrack/tcp_conntrack.go
index a779701..4b69fb9 100644
--- a/tcpip/transport/tcpconntrack/tcp_conntrack.go
+++ b/tcpip/transport/tcpconntrack/tcp_conntrack.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/tcpconntrack/tcp_conntrack_test.go b/tcpip/transport/tcpconntrack/tcp_conntrack_test.go
index 8e06990..d8360eb 100644
--- a/tcpip/transport/tcpconntrack/tcp_conntrack_test.go
+++ b/tcpip/transport/tcpconntrack/tcp_conntrack_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/udp/endpoint.go b/tcpip/transport/udp/endpoint.go
index 25e62ea..70ad355 100644
--- a/tcpip/transport/udp/endpoint.go
+++ b/tcpip/transport/udp/endpoint.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -458,14 +458,22 @@
return tcpip.ErrUnknownDevice
}
- if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
- return err
- }
+ memToInsert := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
e.mu.Lock()
defer e.mu.Unlock()
- e.multicastMemberships = append(e.multicastMemberships, multicastMembership{nicID, v.MulticastAddr})
+ for _, mem := range e.multicastMemberships {
+ if mem == memToInsert {
+ return tcpip.ErrPortInUse
+ }
+ }
+
+ if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
+ return err
+ }
+
+ e.multicastMemberships = append(e.multicastMemberships, memToInsert)
case tcpip.RemoveMembershipOption:
if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
@@ -488,21 +496,28 @@
return tcpip.ErrUnknownDevice
}
+ memToRemove := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
+ memToRemoveIndex := -1
+
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ for i, mem := range e.multicastMemberships {
+ if mem == memToRemove {
+ memToRemoveIndex = i
+ break
+ }
+ }
+ if memToRemoveIndex == -1 {
+ return tcpip.ErrBadLocalAddress
+ }
+
if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
return err
}
- e.mu.Lock()
- defer e.mu.Unlock()
- for i, mem := range e.multicastMemberships {
- if mem.nicID == nicID && mem.multicastAddr == v.MulticastAddr {
- // Only remove the first match, so that each added membership above is
- // paired with exactly 1 removal.
- e.multicastMemberships[i] = e.multicastMemberships[len(e.multicastMemberships)-1]
- e.multicastMemberships = e.multicastMemberships[:len(e.multicastMemberships)-1]
- break
- }
- }
+ e.multicastMemberships[memToRemoveIndex] = e.multicastMemberships[len(e.multicastMemberships)-1]
+ e.multicastMemberships = e.multicastMemberships[:len(e.multicastMemberships)-1]
case tcpip.MulticastLoopOption:
e.mu.Lock()
@@ -640,7 +655,7 @@
})
// Only calculate the checksum if offloading isn't supported.
- if r.Capabilities()&stack.CapabilityChecksumOffload == 0 {
+ if r.Capabilities()&stack.CapabilityTXChecksumOffload == 0 {
xsum := r.PseudoHeaderChecksum(ProtocolNumber, length)
for _, v := range data.Views() {
xsum = header.Checksum(v, xsum)
@@ -940,7 +955,7 @@
// HandlePacket is called by the stack when new packets arrive to this transport
// endpoint.
-func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) {
+func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) {
// Get the header then trim it from the view.
hdr := header.UDP(vv.First())
if int(hdr.Length()) > vv.Size() {
diff --git a/tcpip/transport/udp/forwarder.go b/tcpip/transport/udp/forwarder.go
new file mode 100644
index 0000000..b0b0183
--- /dev/null
+++ b/tcpip/transport/udp/forwarder.go
@@ -0,0 +1,96 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package udp
+
+import (
+ "github.com/google/netstack/tcpip"
+ "github.com/google/netstack/tcpip/buffer"
+ "github.com/google/netstack/tcpip/stack"
+ "github.com/google/netstack/waiter"
+)
+
+// Forwarder is a session request forwarder, which allows clients to decide
+// what to do with a session request, for example: ignore it, or process it.
+//
+// The canonical way of using it is to pass the Forwarder.HandlePacket function
+// to stack.SetTransportProtocolHandler.
+type Forwarder struct {
+ handler func(*ForwarderRequest)
+
+ stack *stack.Stack
+}
+
+// NewForwarder allocates and initializes a new forwarder.
+func NewForwarder(s *stack.Stack, handler func(*ForwarderRequest)) *Forwarder {
+ return &Forwarder{
+ stack: s,
+ handler: handler,
+ }
+}
+
+// HandlePacket handles all packets.
+//
+// This function is expected to be passed as an argument to the
+// stack.SetTransportProtocolHandler function.
+func (f *Forwarder) HandlePacket(r *stack.Route, id stack.TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) bool {
+ f.handler(&ForwarderRequest{
+ stack: f.stack,
+ route: r,
+ id: id,
+ vv: vv,
+ })
+
+ return true
+}
+
+// ForwarderRequest represents a session request received by the forwarder and
+// passed to the client. Clients may optionally create an endpoint to represent
+// it via CreateEndpoint.
+type ForwarderRequest struct {
+ stack *stack.Stack
+ route *stack.Route
+ id stack.TransportEndpointID
+ vv buffer.VectorisedView
+}
+
+// ID returns the 4-tuple (src address, src port, dst address, dst port) that
+// represents the session request.
+func (r *ForwarderRequest) ID() stack.TransportEndpointID {
+ return r.id
+}
+
+// CreateEndpoint creates a connected UDP endpoint for the session request.
+func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+ ep := newEndpoint(r.stack, r.route.NetProto, queue)
+ if err := r.stack.RegisterTransportEndpoint(r.route.NICID(), []tcpip.NetworkProtocolNumber{r.route.NetProto}, ProtocolNumber, r.id, ep, ep.reusePort); err != nil {
+ ep.Close()
+ return nil, err
+ }
+
+ ep.id = r.id
+ ep.route = r.route.Clone()
+ ep.dstPort = r.id.RemotePort
+ ep.regNICID = r.route.NICID()
+
+ ep.state = stateConnected
+
+ ep.rcvMu.Lock()
+ ep.rcvReady = true
+ ep.rcvMu.Unlock()
+
+ ep.HandlePacket(r.route, r.id, r.vv)
+
+ return ep, nil
+}
diff --git a/tcpip/transport/udp/protocol.go b/tcpip/transport/udp/protocol.go
index 6678420..36dc5e2 100644
--- a/tcpip/transport/udp/protocol.go
+++ b/tcpip/transport/udp/protocol.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tcpip/transport/udp/udp_test.go b/tcpip/transport/udp/udp_test.go
index aa6d54c..18dcbff 100644
--- a/tcpip/transport/udp/udp_test.go
+++ b/tcpip/transport/udp/udp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -195,9 +195,7 @@
})
// Calculate the UDP pseudo-header checksum.
- xsum := header.Checksum([]byte(testV6Addr), 0)
- xsum = header.Checksum([]byte(stackV6Addr), xsum)
- xsum = header.Checksum([]byte{0, uint8(udp.ProtocolNumber)}, xsum)
+ xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, testV6Addr, stackV6Addr, uint16(len(u)))
// Calculate the UDP checksum and set it.
xsum = header.Checksum(payload, xsum)
@@ -233,9 +231,7 @@
})
// Calculate the UDP pseudo-header checksum.
- xsum := header.Checksum([]byte(testAddr), 0)
- xsum = header.Checksum([]byte(stackAddr), xsum)
- xsum = header.Checksum([]byte{0, uint8(udp.ProtocolNumber)}, xsum)
+ xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, testAddr, stackAddr, uint16(len(u)))
// Calculate the UDP checksum and set it.
xsum = header.Checksum(payload, xsum)
diff --git a/tmutex/tmutex.go b/tmutex/tmutex.go
index df61d89..c468502 100644
--- a/tmutex/tmutex.go
+++ b/tmutex/tmutex.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/tmutex/tmutex_test.go b/tmutex/tmutex_test.go
index a4537cb..ce34c79 100644
--- a/tmutex/tmutex_test.go
+++ b/tmutex/tmutex_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/waiter/waiter.go b/waiter/waiter.go
index 77db242..04aa3de 100644
--- a/waiter/waiter.go
+++ b/waiter/waiter.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -59,8 +59,6 @@
import (
"sync"
-
- "github.com/google/netstack/ilist"
)
// EventMask represents io events as used in the poll() syscall.
@@ -69,14 +67,28 @@
// Events that waiters can wait on. The meaning is the same as those in the
// poll() syscall.
const (
- EventIn EventMask = 0x01 // syscall.EPOLLIN
- EventPri EventMask = 0x02 // syscall.EPOLLPRI
- EventOut EventMask = 0x04 // syscall.EPOLLOUT
- EventErr EventMask = 0x08 // syscall.EPOLLERR
- EventHUp EventMask = 0x10 // syscall.EPOLLHUP
- EventNVal EventMask = 0x20 // Not defined in syscall.
+ EventIn EventMask = 0x01 // POLLIN
+ EventPri EventMask = 0x02 // POLLPRI
+ EventOut EventMask = 0x04 // POLLOUT
+ EventErr EventMask = 0x08 // POLLERR
+ EventHUp EventMask = 0x10 // POLLHUP
+
+ allEvents EventMask = 0x1f
)
+// EventMaskFromLinux returns an EventMask representing the supported events
+// from the Linux events e, which is in the format used by poll(2).
+func EventMaskFromLinux(e uint32) EventMask {
+ // Our flag definitions are currently identical to Linux.
+ return EventMask(e) & allEvents
+}
+
+// ToLinux returns e in the format used by Linux poll(2).
+func (e EventMask) ToLinux() uint32 {
+ // Our flag definitions are currently identical to Linux.
+ return uint32(e)
+}
+
// Waitable contains the methods that need to be implemented by waitable
// objects.
type Waitable interface {
@@ -127,7 +139,7 @@
// The following fields are protected by the queue lock.
mask EventMask
- ilist.Entry
+ waiterEntry
}
type channelCallback struct{}
@@ -162,7 +174,7 @@
//
// +stateify savable
type Queue struct {
- list ilist.List
+ list waiterList
mu sync.RWMutex
}
@@ -186,8 +198,7 @@
// in common with the notification mask.
func (q *Queue) Notify(mask EventMask) {
q.mu.RLock()
- for it := q.list.Front(); it != nil; it = it.Next() {
- e := it.(*Entry)
+ for e := q.list.Front(); e != nil; e = e.Next() {
if mask&e.mask != 0 {
e.Callback.Callback(e)
}
@@ -201,8 +212,7 @@
ret := EventMask(0)
q.mu.RLock()
- for it := q.list.Front(); it != nil; it = it.Next() {
- e := it.(*Entry)
+ for e := q.list.Front(); e != nil; e = e.Next() {
ret |= e.mask
}
q.mu.RUnlock()
diff --git a/waiter/waiter_list.go b/waiter/waiter_list.go
new file mode 100644
index 0000000..00b304a
--- /dev/null
+++ b/waiter/waiter_list.go
@@ -0,0 +1,173 @@
+package waiter
+
+// ElementMapper provides an identity mapping by default.
+//
+// This can be replaced to provide a struct that maps elements to linker
+// objects, if they are not the same. An ElementMapper is not typically
+// required if: Linker is left as is, Element is left as is, or Linker and
+// Element are the same type.
+type waiterElementMapper struct{}
+
+// linkerFor maps an Element to a Linker.
+//
+// This default implementation should be inlined.
+//
+//go:nosplit
+func (waiterElementMapper) linkerFor(elem *Entry) *Entry { return elem }
+
+// List is an intrusive list. Entries can be added to or removed from the list
+// in O(1) time and with no additional memory allocations.
+//
+// The zero value for List is an empty list ready to use.
+//
+// To iterate over a list (where l is a List):
+// for e := l.Front(); e != nil; e = e.Next() {
+// // do something with e.
+// }
+//
+// +stateify savable
+type waiterList struct {
+ head *Entry
+ tail *Entry
+}
+
+// Reset resets list l to the empty state.
+func (l *waiterList) Reset() {
+ l.head = nil
+ l.tail = nil
+}
+
+// Empty returns true iff the list is empty.
+func (l *waiterList) Empty() bool {
+ return l.head == nil
+}
+
+// Front returns the first element of list l or nil.
+func (l *waiterList) Front() *Entry {
+ return l.head
+}
+
+// Back returns the last element of list l or nil.
+func (l *waiterList) Back() *Entry {
+ return l.tail
+}
+
+// PushFront inserts the element e at the front of list l.
+func (l *waiterList) PushFront(e *Entry) {
+ waiterElementMapper{}.linkerFor(e).SetNext(l.head)
+ waiterElementMapper{}.linkerFor(e).SetPrev(nil)
+
+ if l.head != nil {
+ waiterElementMapper{}.linkerFor(l.head).SetPrev(e)
+ } else {
+ l.tail = e
+ }
+
+ l.head = e
+}
+
+// PushBack inserts the element e at the back of list l.
+func (l *waiterList) PushBack(e *Entry) {
+ waiterElementMapper{}.linkerFor(e).SetNext(nil)
+ waiterElementMapper{}.linkerFor(e).SetPrev(l.tail)
+
+ if l.tail != nil {
+ waiterElementMapper{}.linkerFor(l.tail).SetNext(e)
+ } else {
+ l.head = e
+ }
+
+ l.tail = e
+}
+
+// PushBackList inserts list m at the end of list l, emptying m.
+func (l *waiterList) PushBackList(m *waiterList) {
+ if l.head == nil {
+ l.head = m.head
+ l.tail = m.tail
+ } else if m.head != nil {
+ waiterElementMapper{}.linkerFor(l.tail).SetNext(m.head)
+ waiterElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
+
+ l.tail = m.tail
+ }
+
+ m.head = nil
+ m.tail = nil
+}
+
+// InsertAfter inserts e after b.
+func (l *waiterList) InsertAfter(b, e *Entry) {
+ a := waiterElementMapper{}.linkerFor(b).Next()
+ waiterElementMapper{}.linkerFor(e).SetNext(a)
+ waiterElementMapper{}.linkerFor(e).SetPrev(b)
+ waiterElementMapper{}.linkerFor(b).SetNext(e)
+
+ if a != nil {
+ waiterElementMapper{}.linkerFor(a).SetPrev(e)
+ } else {
+ l.tail = e
+ }
+}
+
+// InsertBefore inserts e before a.
+func (l *waiterList) InsertBefore(a, e *Entry) {
+ b := waiterElementMapper{}.linkerFor(a).Prev()
+ waiterElementMapper{}.linkerFor(e).SetNext(a)
+ waiterElementMapper{}.linkerFor(e).SetPrev(b)
+ waiterElementMapper{}.linkerFor(a).SetPrev(e)
+
+ if b != nil {
+ waiterElementMapper{}.linkerFor(b).SetNext(e)
+ } else {
+ l.head = e
+ }
+}
+
+// Remove removes e from l.
+func (l *waiterList) Remove(e *Entry) {
+ prev := waiterElementMapper{}.linkerFor(e).Prev()
+ next := waiterElementMapper{}.linkerFor(e).Next()
+
+ if prev != nil {
+ waiterElementMapper{}.linkerFor(prev).SetNext(next)
+ } else {
+ l.head = next
+ }
+
+ if next != nil {
+ waiterElementMapper{}.linkerFor(next).SetPrev(prev)
+ } else {
+ l.tail = prev
+ }
+}
+
+// Entry is a default implementation of Linker. Users can add anonymous fields
+// of this type to their structs to make them automatically implement the
+// methods needed by List.
+//
+// +stateify savable
+type waiterEntry struct {
+ next *Entry
+ prev *Entry
+}
+
+// Next returns the entry that follows e in the list.
+func (e *waiterEntry) Next() *Entry {
+ return e.next
+}
+
+// Prev returns the entry that precedes e in the list.
+func (e *waiterEntry) Prev() *Entry {
+ return e.prev
+}
+
+// SetNext assigns 'entry' as the entry that follows e in the list.
+func (e *waiterEntry) SetNext(elem *Entry) {
+ e.next = elem
+}
+
+// SetPrev assigns 'entry' as the entry that precedes e in the list.
+func (e *waiterEntry) SetPrev(elem *Entry) {
+ e.prev = elem
+}
diff --git a/waiter/waiter_test.go b/waiter/waiter_test.go
index 60853f9..c1b94a4 100644
--- a/waiter/waiter_test.go
+++ b/waiter/waiter_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 Google LLC
+// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.