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.